git_ref/transaction/
ext.rs1use git_object::bstr::BString;
2
3use crate::{
4 transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog, Target},
5 PartialNameRef,
6};
7
8pub trait RefEditsExt<T>
10where
11 T: std::borrow::Borrow<RefEdit> + std::borrow::BorrowMut<RefEdit>,
12{
13 fn assure_one_name_has_one_edit(&self) -> Result<(), BString>;
15
16 fn extend_with_splits_of_symbolic_refs(
20 &mut self,
21 find: impl FnMut(&PartialNameRef) -> Option<Target>,
22 make_entry: impl FnMut(usize, RefEdit) -> T,
23 ) -> Result<(), std::io::Error>;
24
25 fn pre_process(
29 &mut self,
30 find: impl FnMut(&PartialNameRef) -> Option<Target>,
31 make_entry: impl FnMut(usize, RefEdit) -> T,
32 ) -> Result<(), std::io::Error> {
33 self.extend_with_splits_of_symbolic_refs(find, make_entry)?;
34 self.assure_one_name_has_one_edit().map_err(|name| {
35 std::io::Error::new(
36 std::io::ErrorKind::AlreadyExists,
37 format!("A reference named '{name}' has multiple edits"),
38 )
39 })
40 }
41}
42
43impl<E> RefEditsExt<E> for Vec<E>
44where
45 E: std::borrow::Borrow<RefEdit> + std::borrow::BorrowMut<RefEdit>,
46{
47 fn assure_one_name_has_one_edit(&self) -> Result<(), BString> {
48 let mut names: Vec<_> = self.iter().map(|e| &e.borrow().name).collect();
49 names.sort();
50 match names.windows(2).find(|v| v[0] == v[1]) {
51 Some(name) => Err(name[0].as_bstr().to_owned()),
52 None => Ok(()),
53 }
54 }
55
56 fn extend_with_splits_of_symbolic_refs(
57 &mut self,
58 mut find: impl FnMut(&PartialNameRef) -> Option<Target>,
59 mut make_entry: impl FnMut(usize, RefEdit) -> E,
60 ) -> Result<(), std::io::Error> {
61 let mut new_edits = Vec::new();
62 let mut first = 0;
63 let mut round = 1;
64 loop {
65 for (eid, edit) in self[first..].iter_mut().enumerate().map(|(eid, v)| (eid + first, v)) {
66 let edit = edit.borrow_mut();
67 if !edit.deref {
68 continue;
69 };
70
71 edit.deref = false;
75 if let Some(Target::Symbolic(referent)) = find(edit.name.as_ref().as_partial_name()) {
76 new_edits.push(make_entry(
77 eid,
78 match &mut edit.change {
79 Change::Delete {
80 expected: previous,
81 log: mode,
82 } => {
83 let current_mode = *mode;
84 *mode = RefLog::Only;
85 RefEdit {
86 change: Change::Delete {
87 expected: previous.clone(),
88 log: current_mode,
89 },
90 name: referent,
91 deref: true,
92 }
93 }
94 Change::Update { log, expected, new } => {
95 let current = std::mem::replace(
96 log,
97 LogChange {
98 message: log.message.clone(),
99 mode: RefLog::Only,
100 force_create_reflog: log.force_create_reflog,
101 },
102 );
103 let next = std::mem::replace(expected, PreviousValue::Any);
104 RefEdit {
105 change: Change::Update {
106 expected: next,
107 new: new.clone(),
108 log: current,
109 },
110 name: referent,
111 deref: true,
112 }
113 }
114 },
115 ));
116 }
117 }
118 if new_edits.is_empty() {
119 break Ok(());
120 }
121 if round == 5 {
122 break Err(std::io::Error::new(
123 std::io::ErrorKind::WouldBlock,
124 format!("Could not follow all splits after {round} rounds, assuming reference cycle"),
125 ));
126 }
127 round += 1;
128 first = self.len();
129
130 self.append(&mut new_edits);
131 }
132 }
133}