Import Git repos without writing anything to disk
Dependencies
- [2]
5DUMO2ICBetter `pijul git` error message (when recording) - [3]
3SJ3DJNFAdding a --force option to `pijul add`, to include otherwise ignored files - [4]
UC5C5REV`pijul git`: two more fields in the benchmarks - [5]
XFYALEYNWhen importing initial Git commits, use walk rather than iter to walk the tree - [6]
2RXOCWUWMaking libpijul deterministic (and getting rid of `rand`) - [7]
A3DMBJJAUpgrading the `git` subcommand to the latest Sanakirja and Libpijul - [8]
RHHNPMZIFixing `pijul git` - [9]
5SLOJYHGFixing the Git feature - [10]
LGEJSLTYFixing output (including its uses in reset and pull) - [11]
V435QOJRUsing path-slash to fix path issues on Windows - [12]
CVAT6LN3Fixing git import, and adding more useful feedback (with `RUST_LOG="pijul=info"`) - [13]
S7MPXAPHRemoving warnings in pijul::commands::git - [14]
VO5OQW4WRemoving anyhow in libpijul - [15]
L4JXJHWXpijul/*: reorganize imports and remove extern crate - [16]
SXEYMYF7Fixing the bad changes in history (unfortunately, by rebooting). - [17]
PIQCNEEBUpgrading to Clap 3.0.0-alpha.5 - [18]
YVA72CP2Default feedback for the `pijul git` command - [19]
LOJL4HMYRecording a prefix we just deleted (mostly useful in `pijul git`, regular records returned NotFound) - [20]
SFY4U6XEremove redundant conditional compilation - [21]
ZDK3GNDBTag transactions (including a massive refactoring of errors) - [22]
VU4KVXHWGit import was importing parents and hidden files (including .git), in some cases - [23]
SNZ3OAMCuse native external subcommand support instead of hand-rolled one - [24]
R3H7D42UDebugging `pijul git`: proper error reporting - [25]
BJOZ25EUDeterministic Git import - [26]
OJZWJUF2MUCH faster `pijul add -r` - [27]
YGPEHOTElibpijul::fs::add_{file,dir} now return the created Inode - [28]
EUZFFJSOUpdating Pijul with the latest changes in Libpijul - [29]
GUL4M5FICleanup and formatting - [30]
ZHABNS3SCanonicalize all paths - [31]
I52XSRUHMassive cleanup, and simplification - [32]
46XERN6RFixing "addition error" in `pijul git` - [*]
LCERQSWMCleanup
Change contents
- replacement in pijul/src/main.rs at line 140
env_logger_init();if cfg!(debug_assertions) {env_logger::init();} else {env_logger_init();} - edit in pijul/src/commands/git.rs at line 2
use canonical_path::CanonicalPathBuf; - edit in pijul/src/commands/git.rs at line 6
use std::cell::RefCell; - edit in pijul/src/commands/git.rs at line 9
use std::rc::Rc; - replacement in pijul/src/commands/git.rs at line 444
&repo.repo.working_copy,&libpijul::working_copy::sink(), - replacement in pijul/src/commands/git.rs at line 482
) -> Result<(git2::Object<'a>, BTreeSet<PathBuf>), anyhow::Error> {) -> Result<(git2::Object<'a>, BTreeMap<PathBuf, bool>), anyhow::Error> { - edit in pijul/src/commands/git.rs at line 487[6.152395]→[6.152395:153092](∅→∅),[6.153092]→[6.0:84](∅→∅),[6.84]→[6.153092:153321](∅→∅),[6.153092]→[6.153092:153321](∅→∅)
let reset_was_useful = Rc::new(RefCell::new(false));let mut builder = git2::build::CheckoutBuilder::new();let repo_path = repo.repo.path.clone();let reset_was_useful_ = reset_was_useful.clone();builder.force().remove_untracked(true).remove_ignored(true).progress(move |file, a, b| {debug!("Git progress: {:?} {:?} {:?}", file, a, b);if let Some(file) = file {let file = repo_path.join(file);if let Ok(meta) = std::fs::metadata(&file) {if !meta.file_type().is_symlink() {*reset_was_useful_.borrow_mut() = true}} else {*reset_was_useful_.borrow_mut() = true}}});builder.notify(|notif, file, _, _, _| {info!("Git reset: {:?} {:?}", notif, file);true});git.reset(&object, git2::ResetType::Hard, Some(&mut builder))?; - replacement in pijul/src/commands/git.rs at line 491
let mut prefixes = BTreeSet::new();let mut prefixes = BTreeMap::new(); - edit in pijul/src/commands/git.rs at line 511
let is_dir = delta.new_file().mode() == git2::FileMode::Tree; - replacement in pijul/src/commands/git.rs at line 551
prefixes.insert(new_path);prefixes.insert(new_path, is_dir); - replacement in pijul/src/commands/git.rs at line 555
prefixes.insert(old_path);prefixes.insert(old_path, is_dir); - replacement in pijul/src/commands/git.rs at line 562
prefixes.insert(old_path);prefixes.insert(new_path);prefixes.insert(old_path, is_dir);prefixes.insert(new_path, is_dir); - replacement in pijul/src/commands/git.rs at line 583
prefixes.insert(m);prefixes.insert(m, t.kind() == Some(git2::ObjectType::Tree)); - edit in pijul/src/commands/git.rs at line 593
}#[derive(Clone)]struct Commit<'a> {r: &'a git2::Repository,c: git2::Commit<'a>, - edit in pijul/src/commands/git.rs at line 600
impl<'a> libpijul::working_copy::WorkingCopyRead for Commit<'a> {type Error = git2::Error;fn file_metadata(&self, file: &str) -> Result<InodeMetadata, Self::Error> {debug!("metadata {:?}", file);let entry = self.c.tree()?.get_path(Path::new(file))?;let is_dir = entry.kind() == Some(git2::ObjectType::Tree);if is_dir {Ok(InodeMetadata::new(0o100, true))} else {let permissions = entry.filemode();debug!("permissions = {:o} {:o} {:?}",permissions,permissions & 0o100,is_dir);Ok(InodeMetadata::new(permissions as usize & 0o100, false))}} - edit in pijul/src/commands/git.rs at line 622
fn read_file(&self, file: &str, buffer: &mut Vec<u8>) -> Result<(), Self::Error> {debug!("read file {:?}", file);let entry = self.c.tree()?.get_path(Path::new(file))?;if let Ok(b) = entry.to_object(self.r)?.peel_to_blob() {buffer.extend(b.content());}debug!("entry {:?}", entry.kind());Ok(())}fn modified_time(&self, _: &str) -> Result<std::time::SystemTime, Self::Error> {Ok(std::time::SystemTime::now())}} - replacement in pijul/src/commands/git.rs at line 650[6.3493]→[6.157898:157976](∅→∅),[6.14809]→[6.157898:157976](∅→∅),[6.157898]→[6.157898:157976](∅→∅),[6.157976]→[6.453:530](∅→∅),[6.530]→[6.157976:158004](∅→∅),[6.157976]→[6.157976:158004](∅→∅),[6.158004]→[6.0:63](∅→∅)
for p in prefixes.iter() {if let Ok(m) = std::fs::metadata(&p) {use path_slash::PathExt;let p = p.to_slash_lossy();if m.is_dir() {txn_.add_dir(&p, 0).map(|_| ()).unwrap_or(());let mut prefixes_ = BTreeMap::new();for (mut p, is_dir) in prefixes {use path_slash::PathExt;loop {debug!("p = {:?}", p);if prefixes_.contains_key(&p) {break;}let p_ = p.to_slash_lossy();debug!("adding prefix {:?}", p_);let (tracked, pos) = libpijul::fs::get_vertex(&*txn_, &p_)?;if !tracked {debug!("not tracked");if is_dir {txn_.add_dir(&p_, 0).map(|_| ()).unwrap_or(());} else {txn_.add_file(&p_, 0).map(|_| ()).unwrap_or(());}}debug!("pos = {:?}", pos);if pos.is_none() || !is_dir {if !p.pop() {prefixes_.insert(PathBuf::new(), true);break;} - replacement in pijul/src/commands/git.rs at line 676
txn_.add_file(&p, 0).map(|_| ()).unwrap_or(());prefixes_.insert(p, is_dir);break; - edit in pijul/src/commands/git.rs at line 686
let prefix_vec: Vec<_> = prefixes.into_iter().collect(); - replacement in pijul/src/commands/git.rs at line 711
&repo.repo.working_copy,// &repo.repo.working_copy&Commit {r: git,c: git.find_commit(*child)?,}, - replacement in pijul/src/commands/git.rs at line 717
&CanonicalPathBuf::canonicalize(&repo.repo.path)?,&prefix_vec,&prefixes_, - replacement in pijul/src/commands/git.rs at line 763
T: TxnT + TxnTExt + MutTxnTExt + Send + Sync + 'static,C: libpijul::changestore::ChangeStore + Clone + Send + 'static,T: TxnT + TxnTExt + MutTxnTExt,C: libpijul::changestore::ChangeStore + Clone,W: libpijul::working_copy::WorkingCopyRead + Clone, - replacement in pijul/src/commands/git.rs at line 769
working_copy: &libpijul::working_copy::FileSystem,working_copy: &W, - replacement in pijul/src/commands/git.rs at line 771
repo_path: &CanonicalPathBuf,prefixes: &[PathBuf],prefixes: &BTreeMap<PathBuf, bool>, - replacement in pijul/src/commands/git.rs at line 774
) -> Result<(usize, Option<libpijul::Hash>, libpijul::Merkle), libpijul::LocalApplyError<T>> {) -> Result<(usize, Option<libpijul::Hash>, libpijul::Merkle), libpijul::LocalApplyError<T>>whereW::Error: 'static,{ - replacement in pijul/src/commands/git.rs at line 781[6.160881]→[6.34:70](∅→∅),[6.70]→[6.0:269](∅→∅),[6.269]→[3.24:43](∅→∅),[3.43]→[6.269:606](∅→∅),[6.269]→[6.269:606](∅→∅),[6.606]→[2.0:57](∅→∅)
let num_cpus = num_cpus::get();for p in prefixes.iter() {use libpijul::working_copy::filesystem::*;match working_copy.record_prefix(txn.clone(),channel.clone(),changes,&mut state,repo_path.clone(),p,false,num_cpus,0,) {Ok(_) => {}Err(Error::Add(AddError::Fs(FsError::NotFound(_)))) => {}Err(Error::Add(AddError::Fs(FsError::AlreadyInRepo(_)))) => {}Err(Error::Add(AddError::Io(e))) if e.kind() == std::io::ErrorKind::NotFound => {}Err(e) => {error!("While adding {:?}: {:?}", p, e);let mut last = None;for (p, _) in prefixes.iter() {if let Some(last) = last {if p.starts_with(&last) {continue; - edit in pijul/src/commands/git.rs at line 788
state.record_single_thread(txn.clone(),libpijul::Algorithm::default(),false,&libpijul::DEFAULT_SEPARATOR,channel.clone(),working_copy,changes,p.to_str().unwrap(),).unwrap();last = Some(p); - replacement in pijul/src/commands/git.rs at line 803[6.727]→[6.727:977](∅→∅),[6.977]→[3.44:63](∅→∅),[3.63]→[6.977:1314](∅→∅),[6.977]→[6.977:1314](∅→∅),[6.1314]→[2.58:122](∅→∅),[2.122]→[6.1376:1390](∅→∅),[6.1376]→[6.1376:1390](∅→∅),[6.1390]→[6.549:559](∅→∅),[6.4996]→[6.549:559](∅→∅),[6.549]→[6.549:559](∅→∅)
use libpijul::working_copy::filesystem::*;match working_copy.record_prefix(txn.clone(),channel.clone(),changes,&mut state,repo_path.clone(),Path::new(""),false,num_cpus,0,) {Ok(_) => {}Err(Error::Add(AddError::Fs(FsError::NotFound(_)))) => {}Err(Error::Add(AddError::Fs(FsError::AlreadyInRepo(_)))) => {}Err(Error::Add(AddError::Io(e))) if e.kind() == std::io::ErrorKind::NotFound => {}Err(e) => {error!("While adding {:?}: {:?}", prefixes, e);}}state.record_single_thread(txn.clone(),libpijul::Algorithm::default(),false,&libpijul::DEFAULT_SEPARATOR,channel.clone(),working_copy,changes,"",).unwrap(); - edit in libpijul/src/fs.rs at line 166[34.9][6.741561]
/// Returns whether a path is registered in the working copy.pub fn get_vertex<T: TreeTxnT>(txn: &T,path: &str,) -> Result<(bool, Option<Position<ChangeId>>), TreeErr<T::TreeError>> {debug!("is_tracked {:?}", path);let (inode, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;debug!("/is_tracked {:?}", path);if remaining_path_components.next().is_none() {Ok((true, txn.get_inodes(&inode, None)?.cloned()))} else {Ok((false, None))}}