RMDMAYRXYBU5OQXV5HSF6LFD4NBMKRNH5EPIVW3K5HAV6D56IG6QC
GBX4AFASHNICJ25B6PAGXP3C3NJ5PPXZ76STLDVPBUVEGDJP5ZAAC
RUBSM5DRA4O7SGD7NO7QYFGDQ2CQL6K7SZTIQVGRWAQHGSFUBGCQC
SAADQM3H4JAE2JE65Y6IHONZ57V3ZZ3IBBV72O3LRBTGWBPJWR3AC
OQQ4TGEMWDB6JXZ4H4D2XQJE3PDLINTZ4YCL2O2EVG67CDSAM35AC
V6J6DTJCGVYSL5W7NVSRP5ROIUWTMZW2MBYO7UBZFCUEJQO3ADWQC
7KNPYIDUXCBZ65NA74NL2DIFS6STJWN4XXHXLTIV5QXZSVLARJMAC
KJOOI346EYXQIRDEGWHYILGX44AZNPHBFUDW5HYEEG5IC2ZXV6GAC
2VXTRPO4OML5A2RPI7SAEV5SJNOFIVS4TMYYG6TUUS5EXFMAPRJAC
HOTQHK5URWTJ4IBMSWYPZQOPHA2ZQSPQAN5MAKGUDUAC6LZRDKOQC
SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC
CCLLB7OIFNFYJZTG3UCI7536TOCWSCSXR67VELSB466R24WLJSDAC
I24UEJQLCH2SOXA4UHIYWTRDCHSOPU7AFTRUOTX7HZIAV4AZKYEQC
FXEDPLRI7PXLDXV634ZA6D5Q3ZWG3ESTKJTMRPJ4MAHI7PKU3M6AC
ZAEUSICJC3YOWGF6NZEQCQ34PHPRSBCJEP7FIWE6VIWJGVU734HQC
VO5OQW4W2656DIYYRNZ3PO7TQ4JOKQ3GVWE5ALUTYVMX3WMXJOYQC
ZSF3YFZTDOXMCOC3HOT5C6MQLYLWOR7QJAOUDS2M2Z4SC2YW3GRAC
YN63NUZO4LVJ7XPMURDULTXBVJKW5MVCTZ24R7Z52QMHO3HPDUVQC
3AMEP2Y5J6GA4AWQONF4JVA3XSR3ASLHHKMYG44R72SOUY3UQCDAC
GHO6DWPILBBTL6CVZKERJBTFL3EY6ZT4YM4E5R4S6YPGVFKFHCVAC
IIV3EL2XYI2X7HZWKXEXQFAE3R3KC2Q7SGOT3Q332HSENMYVF32QC
TVVW53HZGYPODAXEQ4BFZNSPBOFG6JEDVOKIYIDZMWFAMOBKOR2QC
3S6LU2U5TIU43WRE5RQS3IOLVJLVNCDL4W44FVK2HR3NAXZ7IDUAC
LGEJSLTYI7Y2CYC3AN6ECMT3D3MTWCAKZPVQEG5MPM2OBW5FQ46AC
VYHHOEYHO67JNJEODX5L3CQFIV3DAXZBBIQUOMCWJDYF3VWICDNQC
LLT3GY6ULCVHMO3VUSVI5H4O244Z3ULOWLTW2IGJXIA2TWIHJDSQC
LERRJNFC6324RC6ADDTEPCNR3MYH6GKIQUDD433ZAIEECFF5CADAC
G6S6PWZEFJK7ARWBIFKDU6VYC5DCJ2YFJMWZOLLWWKU52R2QPXZAC
DJYHARZ7CSRMX6ZFM6P52SM2EC57VTSHWAIMFSD7Q3EL7UYZGLXQC
XR7MNOMU5PMOOEY2EPPUABZ7NOP432RDCWUET23ONPXTT3JQIFIAC
246V5TYIUL7CFN7G5Y7A35EEM6IJPN532ROEYVSM7Q4HCQSWPDBQC
NUAOEIXMKZO5KQWQ3TLQWO3RIXBPJU4QOPW7MGCV3ITDAUHFEK5QC
PKIHBUGT3N4BUZ2QP2UWJI4ICOIF6EZVXBFKG753SOTYBAKSVTFAC
ZHABNS3S6FSINO74FOI5KHYXYDTBPO4FQTTYTUS7NNKEVVNLYC4AC
ADPAFSMYUBTKSK63EPCY5WQGROJQWFCWO4UFPWY3ZXD5ZNH26P2QC
O4DNWMPDUWI6SKYOZTQKCSX6MSR73CTGCUSM65TSQYVOUSAAS6KAC
3I4PAA2AW3VUTA3HLS2G4TQMWB7BO25DMCC7VWJHG6WMHCBHR6JAC
GJZWSXHQ6SYUDTVDOUBTJYU3SG567M2AXWURWKRNVP2MISZ444DQC
YCEZL7VFBZNOZTSSI24D36ACJVZKXCCEOIFWIHQWK22QPB4PDTRAC
C4MJ7D7QCOFGIHQSDV3UC76DTSE5M7JSLGN5MROMSVKFVQRFSP5QC
2RXOCWUWOGHEKHT5W73LAHJSOZVRTOGS7BWLSIGEEEBJGMCZBXQAC
VNBLGT6GAN2AHKRFKTKED7WNDDRGNULY5H343ZYV3ETSDZZKGBTAC
XA23FMQM2AI7RMR36AYN7UNP2D5JWVJMJPHURWZO7URM7H46PU6AC
I52XSRUH5RVHQBFWVMAQPTUSPAJ4KNVID2RMI3UGCVKFLYUO6WZAC
BD5PC25AB5MKVIYDFSDGRZ4YGX4PKW4SMZ3YAYAPNA5HLDVJUR3QC
BXD3IQYNMKMI5BTANCF6NZHZP4CKPWADGJMBT2R3WTMKVKONT5QAC
WZVCLZKY34KQBQU6YBGJLQCDADBQ67LQVDNRVCMQVY3O3C3EIWSQC
CCFJ7VO3I73FE3MZRS5RSDRYNZVW7AXC345P4BXS7JIL2TU3LQJQC
RRCSHAYZ6RLWVPNYHF2F5FSRL2KKJNPQUQRIJYLJGB23BZQQ7JLQC
stack.push((source, dest));
stack.push((grandparent, source_parent));
if grandparent.is_root() || grandparent.start == grandparent.end {
return_value = restore_inode(txn, changes, source, dest, Inode::ROOT, salt)?;
} else {
stack.push((source, dest));
stack.push((grandparent, source_parent));
}
debug!("TAKEN 2");
self.delete_obsolete_children(
&*txn,
txn.graph(&channel),
working_copy,
changes,
&item.full_path,
Position::ROOT,
)?;
Position::OPTION_ROOT
// Test for a "root" vertex below the 0 one.
let f0 = EdgeFlags::FOLDER | EdgeFlags::BLOCK;
let f1 = f0 | EdgeFlags::PSEUDO;
self.recorded_inodes
.lock()
.insert(Inode::ROOT, Position::ROOT.to_option());
let mut has_nonempty_root = false;
for e in iter_adjacent(&*txn, txn.graph(&*channel), Vertex::ROOT, f0, f1)? {
let e = e?;
let child = txn.find_block(txn.graph(&*channel), e.dest()).unwrap();
if child.start == child.end {
let grandchild =
iter_adjacent(&*txn, txn.graph(&*channel), *child, f0, f1)?
.next()
.unwrap()?
.dest();
root_vertices.push(grandchild);
self.delete_obsolete_children(
&*txn,
txn.graph(&channel),
working_copy,
changes,
&item.full_path,
grandchild,
)?;
} else {
has_nonempty_root = true
}
}
if has_nonempty_root && !root_vertices.is_empty() {
// This repository is mixed between "zero" roots,
// and new-style-roots.
root_vertices.push(Position::ROOT)
}
Position::ROOT.to_option()
// Move on to the next step.
debug!("TAKING LOCK {}", line!());
let txn = txn.read();
let channel = channel.r.read();
self.push_children::<_, _, C>(
&*txn,
&*channel,
working_copy,
&mut item,
&mut components,
vertex,
&mut stack,
prefix,
changes,
)?;
if root_vertices.is_empty() {
// Move on to the next step.
debug!("TAKING LOCK {}", line!());
let txn = txn.read();
let channel = channel.r.read();
self.push_children::<_, _, C>(
&*txn,
&*channel,
working_copy,
&mut item,
&mut components,
vertex,
&mut stack,
prefix,
changes,
)?;
} else {
for vertex in root_vertices {
let txn = txn.read();
let channel = channel.r.read();
self.push_children::<_, _, C>(
&*txn,
&*channel,
working_copy,
&mut item,
&mut components,
vertex.to_option(),
&mut stack,
prefix,
changes,
)?;
}
}
fn add_root_if_needed(
&mut self,
v_papa: Position<Option<ChangeId>>,
) -> Position<Option<ChangeId>> {
let mut contents = self.contents.lock();
if v_papa.change == Some(ChangeId::ROOT) {
let mut new_root = self.new_root.lock();
if let Some(pos) = *new_root {
Position { change: None, pos }
} else {
contents.push(0);
let pos = ChangePosition(contents.len().into());
contents.push(0);
let pos2 = ChangePosition(contents.len().into());
contents.push(0);
self.actions.push(Hunk::FileAdd {
add_name: Atom::NewVertex(NewVertex {
up_context: vec![v_papa],
down_context: vec![],
start: pos2,
end: pos2,
flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
inode: v_papa,
}),
add_inode: Atom::NewVertex(NewVertex {
up_context: vec![Position {
change: None,
pos: pos2,
}],
down_context: vec![],
start: pos,
end: pos,
flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
inode: v_papa,
}),
contents: None,
path: "/".to_string(),
encoding: None,
});
*new_root = Some(pos);
Position { change: None, pos }
}
} else {
v_papa
}
}
for name_ in iter_adjacent(
&*txn_,
txn_.graph(&*channel_),
vertex.inode_vertex(),
f0,
f1,
)? {
debug!("name_ = {:?}", name_);
let name_ = name_?;
if !name_.flag().contains(EdgeFlags::PARENT) {
debug!("continue");
continue;
}
if name_.flag().contains(EdgeFlags::DELETED) {
debug!("is_deleted {:?}: {:?}", item.full_path, name_);
is_deleted = true;
break;
}
let name_dest = txn_
.find_block_end(txn_.graph(&*channel_), name_.dest())
.unwrap();
let mut meta = Vec::new();
let FileMetadata {
basename,
metadata,
encoding,
} = changes
.get_file_meta(
|p| txn_.get_external(&p).unwrap().map(From::from),
*name_dest,
&mut meta,
)
.map_err(RecordError::Changestore)?;
debug!(
"former basename of {:?}: {:?} {:?}",
vertex, basename, metadata
);
if let Some(v_papa) =
iter_adjacent(&*txn_, txn_.graph(&*channel_), *name_dest, f0, f1)?.next()
{
let v_papa = v_papa?;
if !v_papa.flag().contains(EdgeFlags::DELETED) {
former_parents.push(Parent {
basename: basename.to_string(),
metadata,
encoding,
parent: v_papa.dest().to_option(),
})
}
}
}
let (former_parents, is_deleted) =
collect_former_parents::<C, W, T>(changes, &*txn_, &*channel_, vertex)?;
debug!("new_meta = {:?}", new_meta);
if former_parents.len() > 1
|| former_parents[0].basename != item.basename
|| former_parents[0].metadata != item.metadata
|| former_parents[0].parent != item.v_papa
|| is_deleted
{
debug!("new_papa = {:?}", new_papa);
self.record_moved_file::<_, _, W>(
changes,
&*txn_,
&*channel_,
&item,
vertex,
new_papa.unwrap(),
former_parents[0].encoding.clone(),
)?
}
if new_meta.is_file()
&& (self.force_rediff
|| modified_since_last_commit(
&*txn_,
&*channel_,
&working_copy,
&item.full_path,
)?)
{
let mut ret = retrieve(&*txn_, txn_.graph(&*channel_), vertex)?;
let mut b = Vec::new();
let encoding = working_copy
.decode_file(&item.full_path, &mut b)
.map_err(RecordError::WorkingCopy)?;
debug!("diffing…");
let len = self.actions.len();
self.diff(
changes,
&*txn_,
&*channel_,
diff_algorithm,
item.full_path.clone(),
item.inode,
vertex.to_option(),
&mut ret,
&b,
&encoding,
diff_sep,
)?;
if self.actions.len() > len {
if let Ok(last_modified) = working_copy.modified_time(&item.full_path) {
if self.oldest_change == std::time::SystemTime::UNIX_EPOCH {
self.oldest_change = last_modified;
} else {
self.oldest_change = self.oldest_change.min(last_modified);
}
}
}
debug!(
"new actions: {:?}, total {:?}",
&self.actions.len() - len,
self.actions.len()
);
}
self.record_nondeleted(
&*txn_,
diff_algorithm,
diff_sep,
&*channel_,
working_copy,
changes,
item,
new_papa,
vertex,
new_meta,
&former_parents,
is_deleted,
)?
changes,
)?
}
Ok(())
}
fn record_nondeleted<
T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,
W: WorkingCopyRead + Clone,
C: ChangeStore,
>(
&mut self,
txn: &T,
diff_algorithm: diff::Algorithm,
diff_sep: ®ex::bytes::Regex,
channel: &T::Channel,
working_copy: W,
changes: &C,
item: &RecordItem,
new_papa: Option<Position<Option<ChangeId>>>,
vertex: Position<ChangeId>,
new_meta: InodeMetadata,
former_parents: &[Parent],
is_deleted: bool,
) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
where
<W as crate::working_copy::WorkingCopyRead>::Error: 'static,
{
if former_parents.len() > 1
|| former_parents[0].basename != item.basename
|| former_parents[0].metadata != item.metadata
|| former_parents[0].parent != item.v_papa
|| is_deleted
{
debug!("new_papa = {:?}", new_papa);
self.record_moved_file::<_, _, W>(
}
if new_meta.is_file()
&& (self.force_rediff
|| modified_since_last_commit(txn, channel, &working_copy, &item.full_path)?)
{
let mut ret = retrieve(txn, txn.graph(channel), vertex)?;
let mut b = Vec::new();
let encoding = working_copy
.decode_file(&item.full_path, &mut b)
.map_err(RecordError::WorkingCopy)?;
debug!("diffing…");
let len = self.actions.len();
self.diff(
changes,
txn,
channel,
diff_algorithm,
item.full_path.clone(),
item.inode,
vertex.to_option(),
&mut ret,
&b,
&encoding,
diff_sep,
)?;
if self.actions.len() > len {
if let Ok(last_modified) = working_copy.modified_time(&item.full_path) {
if self.oldest_change == std::time::SystemTime::UNIX_EPOCH {
self.oldest_change = last_modified;
} else {
self.oldest_change = self.oldest_change.min(last_modified);
}
}
}
debug!(
"new actions: {:?}, total {:?}",
&self.actions.len() - len,
self.actions.len()
);
fn collect_former_parents<C: ChangeStore, W: WorkingCopyRead, T: ChannelTxnT>(
changes: &C,
txn: &T,
channel: &T::Channel,
vertex: Position<ChangeId>,
) -> Result<(Vec<Parent>, bool), RecordError<C::Error, W::Error, T::GraphError>>
where
W::Error: 'static,
{
let mut former_parents = Vec::new();
let f0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
let f1 = EdgeFlags::all();
let mut is_deleted = true;
for name_ in iter_adjacent(txn, txn.graph(channel), vertex.inode_vertex(), f0, f1)? {
debug!("name_ = {:?}", name_);
let name_ = name_?;
if !name_.flag().contains(EdgeFlags::PARENT) {
debug!("continue");
continue;
}
if name_.flag().contains(EdgeFlags::DELETED) {
debug!("is_deleted {:?}", name_);
is_deleted = true;
break;
}
let name_dest = txn
.find_block_end(txn.graph(channel), name_.dest())
.unwrap();
let mut meta = Vec::new();
let FileMetadata {
basename,
metadata,
encoding,
} = changes
.get_file_meta(
|p| txn.get_external(&p).unwrap().map(From::from),
*name_dest,
&mut meta,
)
.map_err(RecordError::Changestore)?;
debug!(
"former basename of {:?}: {:?} {:?}",
vertex, basename, metadata
);
if let Some(v_papa) = iter_adjacent(txn, txn.graph(channel), *name_dest, f0, f1)?.next() {
let v_papa = v_papa?;
if !v_papa.flag().contains(EdgeFlags::DELETED) {
former_parents.push(Parent {
basename: basename.to_string(),
metadata,
encoding,
parent: v_papa.dest().to_option(),
})
}
}
}
Ok((former_parents, is_deleted))
}
let grandparent_changed = parent_pos != grandparent.dest().to_option();
let grandparent_changed = if parent_pos.change == Some(ChangeId::ROOT) {
!is_root_vertex(txn, channel, grandparent.dest())?
} else {
parent_pos != grandparent.dest().to_option()
};
}
fn is_root_vertex<T: GraphTxnT>(
txn: &T,
channel: &T::Graph,
v: Position<ChangeId>,
) -> Result<bool, TxnErr<T::GraphError>> {
for parent in iter_adjacent(
txn,
channel,
v.inode_vertex(),
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)? {
let p = parent?.dest();
let p = txn.find_block_end(channel, p).unwrap();
if p.start == p.end {
return Ok(true);
} else {
return Ok(false);
}
}
Ok(false)
let mut conflicts = Vec::new();
let mut state = OutputState {
done_vertices: HashMap::default(),
actual_moves: Vec::new(),
conflicts: Vec::new(),
output_name_conflicts,
work: work.clone(),
done_inodes: HashSet::new(),
salt,
if_modified_after,
next_prefix_basename: prefix.next(),
is_following_prefix: true,
pending_change_id,
};
let mut next_prefix_basename = prefix.next();
let mut is_first_none = true;
if next_prefix_basename.is_none() {
let dead = {
let txn_ = txn.read();
let channel = channel.read();
let graph = txn_.graph(&*channel);
collect_dead_files(&*txn_, graph, pending_change_id, Inode::ROOT)?
};
debug!("dead (line {}) = {:?}", line!(), dead);
if !dead.is_empty() {
let mut txn = txn.write();
kill_dead_files::<T, R, P>(&mut *txn, &channel, &repo, &dead)?;
}
is_first_none = false;
}
state.kill_dead_files::<_, _, P>(repo, &txn, &channel)?;
debug!("files: {:?} {:?}", a, b);
{
let txn = txn.read();
sort_conflicting_names(&txn, &channel, &mut b);
state.output_name(repo, changes, &txn, &channel, &mut next_files, a, b)?;
}
std::mem::swap(&mut files, &mut next_files);
}
stop.store(true, std::sync::atomic::Ordering::Relaxed);
let o = output_loop(repo, changes, txn, channel, work, stop, 0);
for t in threads {
state.conflicts.extend(t.join().unwrap()?.into_iter());
}
state.conflicts.extend(o?.into_iter());
for (a, b) in state.actual_moves.iter() {
repo.rename(a, b).map_err(OutputError::WorkingCopy)?
}
Ok(state.conflicts)
}
fn sort_conflicting_names<T: ChannelTxnT + Send + Sync + 'static>(
txn: &ArcTxn<T>,
channel: &ChannelRef<T>,
b: &mut [(Vertex<ChangeId>, OutputItem)],
) {
debug!("files: {:?}", b);
let txn = txn.read();
let channel = channel.read();
b.sort_unstable_by(|u, v| {
txn.get_changeset(txn.changes(&channel), &u.0.change)
.unwrap()
.cmp(
&txn.get_changeset(txn.changes(&channel), &v.0.change)
.unwrap(),
)
});
}
struct OutputState<'a> {
actual_moves: Vec<(String, String)>,
output_name_conflicts: bool,
done_vertices: HashMap<Position<ChangeId>, (Vertex<ChangeId>, String)>,
conflicts: Vec<Conflict>,
work: Arc<crossbeam_deque::Injector<(OutputItem, String, Option<String>)>>,
done_inodes: HashSet<Inode>,
salt: u64,
if_modified_after: Option<std::time::SystemTime>,
next_prefix_basename: Option<&'a str>,
is_following_prefix: bool,
pending_change_id: ChangeId,
}
impl<'a> OutputState<'a> {
fn kill_dead_files<
T: TreeMutTxnT
+ ChannelMutTxnT
+ GraphMutTxnT<GraphError = <T as TreeTxnT>::TreeError>
+ Send
+ Sync
+ 'static,
R: WorkingCopy + Clone + Send + Sync + 'static,
P: ChangeStore + Clone + 'static,
>(
&mut self,
repo: &R,
txn: &ArcTxn<T>,
channel: &ChannelRef<T>,
) -> Result<(), OutputError<P::Error, T::TreeError, R::Error>> {
if self.next_prefix_basename.is_none() && self.is_following_prefix {
let dead = {
let txn_ = txn.read();
b.sort_unstable_by(|u, v| {
txn.get_changeset(txn.changes(&channel), &u.0.change)
.unwrap()
.cmp(
&txn.get_changeset(txn.changes(&channel), &v.0.change)
.unwrap(),
)
});
let graph = txn_.graph(&*channel);
collect_dead_files(&*txn_, graph, self.pending_change_id, Inode::ROOT)?
};
debug!("dead (line {}) = {:?}", line!(), dead);
if !dead.is_empty() {
let mut txn = txn.write();
kill_dead_files::<T, R, P>(&mut *txn, &channel, &repo, &dead)?;
let mut is_first_name = true;
for (name_key, mut output_item) in b {
let name_entry = match done_vertices.entry(output_item.pos) {
Entry::Occupied(e) => {
debug!(
"pos already visited: {:?} {:?} {:?} {:?}",
a,
output_item.pos,
e.get(),
name_key
);
if e.get().0 != name_key {
conflicts.push(Conflict::MultipleNames {
pos: output_item.pos,
path: e.get().1.clone(),
});
}
continue;
}
Entry::Vacant(e) => {
debug!("first visit {:?} {:?}", a, output_item.pos);
e
}
};
self.is_following_prefix = false;
}
Ok(())
}
let output_item_inode = {
let txn = txn.read();
if let Some(inode) = txn.get_revinodes(&output_item.pos, None)? {
Some((*inode, *txn.get_inodes(inode, None)?.unwrap()))
} else {
None
}
};
fn make_inode(
&mut self,
a: &str,
name_key: Vertex<ChangeId>,
output_item: &mut OutputItem,
is_first_name: &mut bool,
) -> MakeInode {
let name_entry = match self.done_vertices.entry(output_item.pos) {
Entry::Occupied(e) => {
debug!(
"pos already visited: {:?} {:?} {:?} {:?}",
a,
output_item.pos,
e.get(),
name_key
);
if e.get().0 != name_key {
// The same inode has more than one name.
self.conflicts.push(Conflict::MultipleNames {
pos: output_item.pos,
path: e.get().1.clone(),
});
}
return MakeInode::AlreadyOutput;
}
Entry::Vacant(e) => {
debug!("first visit {:?} {:?}", a, output_item.pos);
e
}
};
let name = if !*is_first_name {
// Multiple inodes share the same name.
if self.output_name_conflicts {
let name = make_conflicting_name(&a, name_key);
self.conflicts.push(Conflict::Name { path: name.clone() });
name
} else {
debug!("not outputting {:?} {:?}", a, name_key);
self.conflicts.push(Conflict::Name {
path: a.to_string(),
});
return MakeInode::NameConflict;
}
} else {
*is_first_name = false;
a.to_string()
};
debug!("name = {:?} {:?}", name, name_key);
let file_name = path::file_name(&name).unwrap();
path::push(&mut output_item.path, file_name);
name_entry.insert((name_key, output_item.path.clone()));
MakeInode::Ok(name)
}
if let Some((inode, _)) = output_item_inode {
if !done_inodes.insert(inode) {
fn output_name<
T: TreeMutTxnT
+ ChannelMutTxnT
+ GraphMutTxnT<GraphError = <T as TreeTxnT>::TreeError>
+ Send
+ Sync
+ 'static,
R: WorkingCopy + Clone + Send + Sync + 'static,
P: ChangeStore + Send + Clone + 'static,
>(
&mut self,
repo: &R,
changes: &P,
txn: &ArcTxn<T>,
channel: &ChannelRef<T>,
next_files: &mut HashMap<String, Vec<(Vertex<ChangeId>, OutputItem)>>,
a: String,
b: Vec<(Vertex<ChangeId>, OutputItem)>,
) -> Result<(), OutputError<P::Error, T::TreeError, R::Error>> {
let mut is_first_name = true;
for (name_key, mut output_item) in b {
debug!("name_key = {:?} {:?}", name_key, output_item);
let name = match self.make_inode(&a, name_key, &mut output_item, &mut is_first_name) {
MakeInode::Ok(file_name) => file_name,
MakeInode::AlreadyOutput => continue,
MakeInode::NameConflict => break,
};
let output_item_inode = {
let txn = txn.read();
if let Some(inode) = txn.get_revinodes(&output_item.pos, None)? {
if !self.done_inodes.insert(*inode) {
}
let name = if !is_first_name {
if output_name_conflicts {
let name = make_conflicting_name(&a, name_key);
conflicts.push(Conflict::Name { path: name.clone() });
name
} else {
debug!("not outputting {:?} {:?}", a, name_key);
conflicts.push(Conflict::Name {
path: a.to_string(),
});
break;
}
Some((*inode, *txn.get_inodes(inode, None)?.unwrap()))
is_first_name = false;
a.clone()
};
let file_name = path::file_name(&name).unwrap();
path::push(&mut output_item.path, file_name);
name_entry.insert((name_key, output_item.path.clone()));
if let Some(ref mut tmp) = output_item.tmp {
path::push(tmp, file_name);
}
let path = std::mem::replace(&mut output_item.path, String::new());
let mut tmp = output_item.tmp.take();
let inode = move_or_create::<T, R, P>(
txn.clone(),
&repo,
&output_item,
output_item_inode,
&path,
&mut tmp,
&file_name,
&mut actual_moves,
salt,
)?;
debug!("inode = {:?}", inode);
if next_prefix_basename.is_none() && is_first_none {
let dead = {
let txn_ = txn.read();
let channel = channel.read();
collect_dead_files(&*txn_, txn_.graph(&*channel), pending_change_id, inode)?
};
debug!("dead (line {}) = {:?}", line!(), dead);
if !dead.is_empty() {
let mut txn = txn.write();
kill_dead_files::<T, R, P>(&mut *txn, &channel, &repo, &dead)?;
}
is_first_none = false;
None
if output_item.meta.is_dir() {
};
let file_name = path::file_name(&name).unwrap();
let mut tmp = output_item.tmp.take().map(|mut tmp| {
path::push(&mut tmp, file_name);
tmp
});
let path = std::mem::replace(&mut output_item.path, String::new());
let inode = move_or_create::<T, R, P>(
txn.clone(),
&repo,
&output_item,
output_item_inode,
&path,
&mut tmp,
&file_name,
&mut self.actual_moves,
self.salt,
)?;
debug!("inode = {:?}", inode);
self.kill_dead_files::<_, _, P>(repo, txn, channel)?;
if output_item.meta.is_dir() {
if !path.is_empty() {
{
let txn = txn.read();
let channel = channel.read();
collect_children(
&*txn,
&*changes,
txn.graph(&*channel),
output_item.pos,
inode,
&path,
tmp.as_deref(),
next_prefix_basename,
&mut next_files,
)?;
}
debug!("setting permissions for {:?}", path);
} else {
if needs_output(repo, if_modified_after, &path) {
work.push((output_item.clone(), path.clone(), tmp.clone()));
} else {
debug!("Not outputting {:?}", path)
}
if output_item.is_zombie {
conflicts.push(Conflict::ZombieFile {
path: name.to_string(),
})
let txn = txn.read();
let channel = channel.read();
collect_children(
&*txn,
&*changes,
txn.graph(&*channel),
output_item.pos,
inode,
&path,
tmp.as_deref(),
self.next_prefix_basename,
next_files,
)?;
} else {
if needs_output(repo, self.if_modified_after, &path) {
self.work
.push((output_item.clone(), path.clone(), tmp.clone()));
} else {
debug!("Not outputting {:?}", path)
std::mem::swap(&mut files, &mut next_files);
}
stop.store(true, std::sync::atomic::Ordering::Relaxed);
let o = output_loop(repo, changes, txn, channel, work, stop, 0);
for t in threads {
conflicts.extend(t.join().unwrap()?.into_iter());
}
conflicts.extend(o?.into_iter());
for (a, b) in actual_moves.iter() {
repo.rename(a, b).map_err(OutputError::WorkingCopy)?
Ok(())
let mut name_buf = Vec::new();
let FileMetadata {
basename,
metadata: perms,
..
} = changes
.get_file_meta(
|h| txn.get_external(&h).unwrap().map(|x| x.into()),
if name_vertex.start != name_vertex.end {
debug!("name_vertex: {:?} {:?}", e, name_vertex);
collect(
txn,
changes,
channel,
inode,
path,
tmp,
prefix_basename,
files,
name_vertex,
)?
} else {
let inode_pos = iter_adjacent(
txn,
channel,
&mut name_buf,
)
.map_err(PristineOutputError::Changestore)?;
debug!("filename: {:?} {:?}", perms, basename);
let mut name = path.to_string();
if let Some(next) = prefix_basename {
if next != basename {
continue;
}
}
path::push(&mut name, basename);
debug!("name_vertex: {:?} {:?}", e, name_vertex);
let child = if let Some(child) = iter_adjacent(
txn,
channel,
*name_vertex,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
)?
.next()
{
child?
} else {
let mut edge = None;
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)?
.next()
.unwrap()?
.dest();
if !e.flag().contains(EdgeFlags::PARENT) {
edge = Some(e);
break;
}
debug!("e = {:?}", e);
let name_vertex = txn.find_block(channel, e.dest()).unwrap();
collect(
txn,
changes,
channel,
inode,
path,
tmp,
prefix_basename,
files,
name_vertex,
)?
let e = edge.unwrap();
let mut f = std::fs::File::create("debug_output").unwrap();
debug_root(txn, channel, e.dest().inode_vertex(), &mut f, false).unwrap();
panic!("no child");
};
}
}
Ok(())
}
debug!("child: {:?}", child);
let v = files.entry(name).or_insert_with(Vec::new);
v.push((
fn collect<T: GraphTxnT, P: ChangeStore>(
txn: &T,
changes: &P,
channel: &T::Graph,
inode: Inode,
path: &str,
tmp: Option<&str>,
prefix_basename: Option<&str>,
files: &mut HashMap<String, Vec<(Vertex<ChangeId>, OutputItem)>>,
name_vertex: &Vertex<ChangeId>,
) -> Result<(), PristineOutputError<P::Error, T::GraphError>> {
let mut name_buf = Vec::new();
let FileMetadata {
basename,
metadata: perms,
..
} = changes
.get_file_meta(
|h| txn.get_external(&h).unwrap().map(|x| x.into()),
OutputItem {
parent: inode,
path: path.to_string(),
tmp: tmp.map(String::from),
meta: perms,
pos: child.dest(),
is_zombie: is_zombie(txn, channel, child.dest())?,
},
));
&mut name_buf,
)
.map_err(PristineOutputError::Changestore)?;
debug!("filename: {:?} {:?}", perms, basename);
let mut name = path.to_string();
if let Some(next) = prefix_basename {
if next != basename {
return Ok(());
}
path::push(&mut name, basename);
let child = if let Some(child) = iter_adjacent(
txn,
channel,
*name_vertex,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
)?
.next()
{
child?
} else {
let mut edge = None;
for e in iter_adjacent(
txn,
channel,
*name_vertex,
EdgeFlags::FOLDER,
EdgeFlags::all(),
)? {
let e = e?;
if !e.flag().contains(EdgeFlags::PARENT) {
edge = Some(e);
break;
}
}
let e = edge.unwrap();
let mut f = std::fs::File::create("debug_output").unwrap();
debug_root(txn, channel, e.dest().inode_vertex(), &mut f, false).unwrap();
panic!("no child");
};
debug!("child: {:?}", child);
let v = files.entry(name).or_insert_with(Vec::new);
v.push((
*name_vertex,
OutputItem {
parent: inode,
path: path.to_string(),
tmp: tmp.map(String::from),
meta: perms,
pos: child.dest(),
is_zombie: is_zombie(txn, channel, child.dest())?,
},
));
let child = match self.adj.next()? {
Ok(child) => child,
Err(e) => return Some(Err(e.0)),
let dest = loop {
debug!("adj2 = {:?}", self.adj2.is_some());
if let Some(mut adj2) = self.adj2.take() {
match adj2.next() {
None => {}
Some(Ok(ch)) => {
self.adj2 = Some(adj2);
break self.txn.find_block(self.channel, ch.dest()).unwrap();
}
Some(Err(e)) => return Some(Err(e.0)),
}
}
match self.adj.next() {
Some(Ok(child)) => {
let d = self.txn.find_block(self.channel, child.dest()).unwrap();
if d.start == d.end {
match iter_adjacent(
self.txn,
self.channel,
*d,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
.and_then(|mut it| it.next().unwrap())
.and_then(|x| {
iter_adjacent(
self.txn,
self.channel,
x.dest().inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
}) {
Ok(adj) => self.adj2 = Some(adj),
Err(e) => return Some(Err(e.0)),
}
} else {
break d;
}
}
Some(Err(e)) => return Some(Err(e.0)),
None => return None,
}
let FileMetadata {
basename: name,
metadata: perms,
..
} = FileMetadata::read(&change_contents[n.start.0.into()..n.end.0.into()]);
let (name, perms) = if n.start == n.end {
("", InodeMetadata::DIR)
} else {
let FileMetadata {
basename: name,
metadata: perms,
..
} = FileMetadata::read(&change_contents[n.start.0.into()..n.end.0.into()]);
(name, perms)
};