If many components are changed simultaneously (in the same patch) along a file path, moving them is confusing, especially since swaps (a → b and b → a in the same patch) must be handled properly.
This fix takes care of this by introducing a map to remember which files were moved to a temporary location, and using that map instead of fs::inode_filename
to get the temporary path instead of the actual one.
C5MZXLVCVS6JVFYUASM4YH7KCC75D2BH3ARTJ5WCRAVEDQIQSSEQC
3QXUJMZDPH2EDJY6UXIRA5ZQK3GRS2R422S7CKN56I36O4POAQXQC
PEUUYRZ5MHYM2IQTN3ASXPIFZSK5KI32LIHWM6RIWVRRXIQ5GTNQC
SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC
RMDMAYRXYBU5OQXV5HSF6LFD4NBMKRNH5EPIVW3K5HAV6D56IG6QC
HBUMCAFVS4W5FHWNZR7ITJFEEUCVZ4Q2U4KGWYN7XZGDZ75JMFTQC
ZDK3GNDBWXJ2OXFDYB72ZCEBGLBF4MKE5K3PVHDZATHJ7HJIDPRQC
VNBLGT6GAN2AHKRFKTKED7WNDDRGNULY5H343ZYV3ETSDZZKGBTAC
2RXOCWUWOGHEKHT5W73LAHJSOZVRTOGS7BWLSIGEEEBJGMCZBXQAC
FXEDPLRI7PXLDXV634ZA6D5Q3ZWG3ESTKJTMRPJ4MAHI7PKU3M6AC
I24UEJQLCH2SOXA4UHIYWTRDCHSOPU7AFTRUOTX7HZIAV4AZKYEQC
CCLLB7OIFNFYJZTG3UCI7536TOCWSCSXR67VELSB466R24WLJSDAC
VO5OQW4W2656DIYYRNZ3PO7TQ4JOKQ3GVWE5ALUTYVMX3WMXJOYQC
YCEZL7VFBZNOZTSSI24D36ACJVZKXCCEOIFWIHQWK22QPB4PDTRAC
YN63NUZO4LVJ7XPMURDULTXBVJKW5MVCTZ24R7Z52QMHO3HPDUVQC
for (a, b) in state.actual_moves.iter() {
// Since we did a depth-first search of the output paths, we need
// to move in reverse order of the search.
for (a, b) in state.actual_moves.iter().rev() {
debug!("actual move: {:?} {:?}", a, b);
if let Some(ref current_name) = inode_filename(&*txn_, inode)? {
debug!("current_name = {:?}, path = {:?}", current_name, path);
if current_name != path {
if let Some(ref current_name) = inode_filename(&*txn_, inode, move_map)? {
let actual_path = if let Some(tmp) = tmp.take() {
Cow::Owned(tmp)
} else {
Cow::Borrowed(path)
};
debug!(
"current_name = {:?}, path = {:?}, actual_path = {:?}",
current_name, path, actual_path
);
if current_name.as_str() != &actual_path {
actual_moves.push((tmp_path, path.to_string()));
let mut tmp_ = actual_path.to_string();
crate::path::pop(&mut tmp_);
crate::path::push(&mut tmp_, &s);
debug!("rename {:?} {:?}", current_name, tmp_);
repo.rename(¤t_name, &tmp_)
.map_err(OutputError::WorkingCopy)?;
move_map.insert(inode, tmp_.to_string());
actual_moves.push((tmp_.to_string(), actual_path.to_string()));
*tmp = Some(tmp_);
}
fn inode_filename<T: TreeTxnT>(
txn: &T,
inode: Inode,
tmp: &HashMap<Inode, String>,
) -> Result<Option<String>, TreeErr<T::TreeError>> {
debug!("inode_filename {:?}", inode);
let mut components = Vec::new();
let mut current = inode;
loop {
if let Some(tmp) = tmp.get(¤t) {
components.push(SmallString::from_str(tmp));
break;
}
match txn.get_revtree(¤t, None)? {
Some(v) => {
components.push(v.basename.to_owned());
current = v.parent_inode;
if current == Inode::ROOT {
break;
}
}
None => {
debug!("filename_of_inode: not in tree");
return Ok(None);
}
}
}
let mut path = String::new();
for c in components.iter().rev() {
if !path.is_empty() {
path.push('/')
}
path.push_str(c.as_str());
}
debug!("inode_filename = {:?}", path);
Ok(Some(path))