Unified · Split
| @@ -192,11 +192,11 @@ impl<'a> Transaction<'a> { · 191 unmodified lines | |||
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | for change in self.updates.iter_mut() { | 194 | for change in self.updates.iter_mut() { |
| 195 | assert!(!change.update.deref, "Deref mode is turned into splits and turned off"); | ||
| 195 | match &change.update.change { | 196 | match &change.update.change { |
| 196 | Change::Update { .. } => {} | 197 | Change::Update { .. } => {} |
| 197 | Change::Delete { deref, mode, .. } => { | 198 | Change::Delete { mode, .. } => { |
| 198 | let lock = change.lock.take().expect("each ref is locked, even deletions"); | 199 | let lock = change.lock.take().expect("each ref is locked, even deletions"); |
| 199 | assert!(!deref, "Deref mode is turned into splits and turned off"); | ||
| 200 | let (rm_reflog, rm_ref) = match mode { | 200 | let (rm_reflog, rm_ref) = match mode { |
| 201 | DeleteMode::RefAndRefLog => (true, true), | 201 | DeleteMode::RefAndRefLog => (true, true), |
| 202 | DeleteMode::RefLogOnly => (true, false), | 202 | DeleteMode::RefLogOnly => (true, false), |
| @@ -38,9 +38,6 @@ pub enum Change { · 37 unmodified lines | |||
| 38 | Update { | 38 | Update { |
| 39 | /// How to treat the reference log. | 39 | /// How to treat the reference log. |
| 40 | mode: UpdateMode, | 40 | mode: UpdateMode, |
| 41 | /// If set, symbolic references will be dereferenced so the change gets applied to their target. Has no effect if the reference | ||
| 42 | /// isn't symbolic. | ||
| 43 | deref: bool, | ||
| 44 | /// The previous value of the ref, which will be used to assure the ref is still in the known `previous` state before | 41 | /// The previous value of the ref, which will be used to assure the ref is still in the known `previous` state before |
| 45 | /// updating it. It will also be filled in automatically for use in the reflog, if applicable. | 42 | /// updating it. It will also be filled in automatically for use in the reflog, if applicable. |
| 46 | previous: Option<Target>, | 43 | previous: Option<Target>, |
| @@ -56,9 +53,6 @@ pub enum Change { · 9 unmodified lines | |||
| 56 | previous: Option<Target>, | 53 | previous: Option<Target>, |
| 57 | /// How to thread the reference log during deletion. | 54 | /// How to thread the reference log during deletion. |
| 58 | mode: DeleteMode, | 55 | mode: DeleteMode, |
| 59 | /// If set, symbolic references will be dereferenced so the change gets applied to their target. Has no effect if the reference | ||
| 60 | /// isn't symbolic. | ||
| 61 | deref: bool, | ||
| 62 | }, | 56 | }, |
| 63 | } | 57 | } |
| 64 | 58 | ||
| @@ -69,6 +63,38 @@ pub struct RefEdit { · 4 unmodified lines | |||
| 69 | pub change: Change, | 63 | pub change: Change, |
| 70 | /// The name of the reference to apply the change to | 64 | /// The name of the reference to apply the change to |
| 71 | pub name: FullName, | 65 | pub name: FullName, |
| 66 | /// If set, symbolic references identified by `name` will be dereferenced to have the `change` applied to their target. | ||
| 67 | /// This flag has no effect if the reference isn't symbolic. | ||
| 68 | pub deref: bool, | ||
| 69 | } | ||
| 70 | |||
| 71 | /// The way to deal with the Reflog in deletions. | ||
| 72 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] | ||
| 73 | pub enum DeleteMode { | ||
| 74 | /// Delete the reference and the log | ||
| 75 | RefAndRefLog, | ||
| 76 | /// Delete only the reflog | ||
| 77 | RefLogOnly, | ||
| 78 | } | ||
| 79 | |||
| 80 | /// The way to deal with the Reflog in a particular edit | ||
| 81 | /// | ||
| 82 | /// If the `create_unconditionally` field is set, create a reflog even if it otherwise wouldn't be created, | ||
| 83 | /// as is the case for tags. | ||
| 84 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] | ||
| 85 | pub enum UpdateMode { | ||
| 86 | /// As symbolic references only ever see this when you want to detach them, we won't try to dereference them | ||
| 87 | /// in this case and apply the change to it directly. | ||
| 88 | RefAndRefLog { | ||
| 89 | /// If set, update the reflog even if it otherwise wouldn't. | ||
| 90 | create_unconditionally: bool, | ||
| 91 | }, | ||
| 92 | /// Only update the reflog but require this to be a symbolic ref so the actual update can be performed on the | ||
| 93 | /// referent. | ||
| 94 | RefLogOnly { | ||
| 95 | /// If set, update the reflog even if it otherwise wouldn't. | ||
| 96 | create_unconditionally: bool, | ||
| 97 | }, | ||
| 72 | } | 98 | } |
| 73 | 99 | ||
| 74 | mod ext { | 100 | mod ext { |
| @@ -115,13 +141,13 @@ mod ext { · 40 unmodified lines | |||
| 115 | for edit in self.iter_mut() { | 141 | for edit in self.iter_mut() { |
| 116 | let edit = edit.borrow_mut(); | 142 | let edit = edit.borrow_mut(); |
| 117 | match edit.change { | 143 | match edit.change { |
| 118 | Change::Delete { ref mut deref, .. } | Change::Update { ref mut deref, .. } => { | 144 | Change::Delete { .. } | Change::Update { .. } => { |
| 119 | match store.find_one_existing(edit.name.to_partial()).ok() { | 145 | match store.find_one_existing(edit.name.to_partial()).ok() { |
| 120 | Some(Target::Symbolic(_name)) => { | 146 | Some(Target::Symbolic(_name)) => { |
| 121 | // todo!("split into new edit") | 147 | // todo!("split into new edit") |
| 122 | } | 148 | } |
| 123 | Some(Target::Peeled(_)) => { | 149 | Some(Target::Peeled(_)) => { |
| 124 | *deref = false; | 150 | edit.deref = false; |
| 125 | } | 151 | } |
| 126 | None => {} | 152 | None => {} |
| 127 | } | 153 | } |
| @@ -134,32 +160,3 @@ mod ext { · 6 unmodified lines | |||
| 134 | } | 160 | } |
| 135 | } | 161 | } |
| 136 | pub use ext::RefEditsExt; | 162 | pub use ext::RefEditsExt; |
| 137 | |||
| 138 | /// The way to deal with the Reflog in deletions. | ||
| 139 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] | ||
| 140 | pub enum DeleteMode { | ||
| 141 | /// Delete the reference and the log | ||
| 142 | RefAndRefLog, | ||
| 143 | /// Delete only the reflog | ||
| 144 | RefLogOnly, | ||
| 145 | } | ||
| 146 | |||
| 147 | /// The way to deal with the Reflog in a particular edit | ||
| 148 | /// | ||
| 149 | /// If the `create_unconditionally` field is set, create a reflog even if it otherwise wouldn't be created, | ||
| 150 | /// as is the case for tags. | ||
| 151 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] | ||
| 152 | pub enum UpdateMode { | ||
| 153 | /// As symbolic references only ever see this when you want to detach them, we won't try to dereference them | ||
| 154 | /// in this case and apply the change to it directly. | ||
| 155 | RefAndRefLog { | ||
| 156 | /// If set, update the reflog even if it otherwise wouldn't. | ||
| 157 | create_unconditionally: bool, | ||
| 158 | }, | ||
| 159 | /// Only update the reflog but require this to be a symbolic ref so the actual update can be performed on the | ||
| 160 | /// referent. | ||
| 161 | RefLogOnly { | ||
| 162 | /// If set, update the reflog even if it otherwise wouldn't. | ||
| 163 | create_unconditionally: bool, | ||
| 164 | }, | ||
| 165 | } | ||
| @@ -52,9 +52,9 @@ mod prepare_and_commit { · 51 unmodified lines | |||
| 52 | }, | 52 | }, |
| 53 | new: Target::Symbolic(referent.try_into()?), | 53 | new: Target::Symbolic(referent.try_into()?), |
| 54 | previous: None, // TODO: check failure if it doesn't exist | 54 | previous: None, // TODO: check failure if it doesn't exist |
| 55 | deref: false, | ||
| 56 | }, | 55 | }, |
| 57 | name: "HEAD".try_into()?, | 56 | name: "HEAD".try_into()?, |
| 57 | deref: false, | ||
| 58 | }), | 58 | }), |
| 59 | Fail::Immediately, | 59 | Fail::Immediately, |
| 60 | ); | 60 | ); |
| @@ -107,9 +107,9 @@ mod prepare_and_commit { · 46 unmodified lines | |||
| 107 | change: Change::Delete { | 107 | change: Change::Delete { |
| 108 | previous: None, | 108 | previous: None, |
| 109 | mode: DeleteMode::RefAndRefLog, | 109 | mode: DeleteMode::RefAndRefLog, |
| 110 | deref: false, | ||
| 111 | }, | 110 | }, |
| 112 | name: "DOES_NOT_EXIST".try_into().unwrap(), | 111 | name: "DOES_NOT_EXIST".try_into().unwrap(), |
| 112 | deref: false, | ||
| 113 | }), | 113 | }), |
| 114 | Fail::Immediately, | 114 | Fail::Immediately, |
| 115 | ) | 115 | ) |
| @@ -127,9 +127,9 @@ mod prepare_and_commit { · 11 unmodified lines | |||
| 127 | change: Change::Delete { | 127 | change: Change::Delete { |
| 128 | previous: Some(Target::Peeled(ObjectId::null_sha1())), | 128 | previous: Some(Target::Peeled(ObjectId::null_sha1())), |
| 129 | mode: DeleteMode::RefAndRefLog, | 129 | mode: DeleteMode::RefAndRefLog, |
| 130 | deref: false, | ||
| 131 | }, | 130 | }, |
| 132 | name: "DOES_NOT_EXIST".try_into().unwrap(), | 131 | name: "DOES_NOT_EXIST".try_into().unwrap(), |
| 132 | deref: false, | ||
| 133 | }), | 133 | }), |
| 134 | Fail::Immediately, | 134 | Fail::Immediately, |
| 135 | ) | 135 | ) |
| @@ -156,9 +156,9 @@ mod prepare_and_commit { · 20 unmodified lines | |||
| 156 | change: Change::Delete { | 156 | change: Change::Delete { |
| 157 | previous: Some(Target::Peeled(ObjectId::null_sha1())), | 157 | previous: Some(Target::Peeled(ObjectId::null_sha1())), |
| 158 | mode: DeleteMode::RefAndRefLog, | 158 | mode: DeleteMode::RefAndRefLog, |
| 159 | deref: false, | ||
| 160 | }, | 159 | }, |
| 161 | name: head.name().into(), | 160 | name: head.name().into(), |
| 161 | deref: false, | ||
| 162 | }), | 162 | }), |
| 163 | Fail::Immediately, | 163 | Fail::Immediately, |
| 164 | ) | 164 | ) |
| @@ -170,9 +170,9 @@ mod prepare_and_commit { · 5 unmodified lines | |||
| 170 | change: Change::Delete { | 170 | change: Change::Delete { |
| 171 | previous: Some(Target::Symbolic("refs/heads/main".try_into()?)), | 171 | previous: Some(Target::Symbolic("refs/heads/main".try_into()?)), |
| 172 | mode: DeleteMode::RefAndRefLog, | 172 | mode: DeleteMode::RefAndRefLog, |
| 173 | deref: false | ||
| 174 | }, | 173 | }, |
| 175 | name: head.name().into(), | 174 | name: head.name().into(), |
| 175 | deref: false | ||
| 176 | }], | 176 | }], |
| 177 | "the previous value was updated with the actual one" | 177 | "the previous value was updated with the actual one" |
| 178 | ); | 178 | ); |
| @@ -197,9 +197,9 @@ mod prepare_and_commit { · 18 unmodified lines | |||
| 197 | change: Change::Delete { | 197 | change: Change::Delete { |
| 198 | previous: Some(Target::Symbolic("refs/heads/main".try_into()?)), | 198 | previous: Some(Target::Symbolic("refs/heads/main".try_into()?)), |
| 199 | mode: DeleteMode::RefLogOnly, | 199 | mode: DeleteMode::RefLogOnly, |
| 200 | deref: false, | ||
| 201 | }, | 200 | }, |
| 202 | name: head.name().into(), | 201 | name: head.name().into(), |
| 202 | deref: false, | ||
| 203 | }), | 203 | }), |
| 204 | Fail::Immediately, | 204 | Fail::Immediately, |
| 205 | ) | 205 | ) |
| @@ -231,9 +231,9 @@ mod prepare_and_commit { · 25 unmodified lines | |||
| 231 | change: Change::Delete { | 231 | change: Change::Delete { |
| 232 | previous: Some(Target::Symbolic("refs/heads/main".try_into().unwrap())), | 232 | previous: Some(Target::Symbolic("refs/heads/main".try_into().unwrap())), |
| 233 | mode: DeleteMode::RefLogOnly, | 233 | mode: DeleteMode::RefLogOnly, |
| 234 | deref: true, | ||
| 235 | }, | 234 | }, |
| 236 | name: head.name().into(), | 235 | name: head.name().into(), |
| 236 | deref: true, | ||
| 237 | }), | 237 | }), |
| 238 | Fail::Immediately, | 238 | Fail::Immediately, |
| 239 | ) | 239 | ) |
| @@ -11,9 +11,9 @@ mod transaction { · 10 unmodified lines | |||
| 11 | change: Change::Delete { | 11 | change: Change::Delete { |
| 12 | previous: None, | 12 | previous: None, |
| 13 | mode: DeleteMode::RefAndRefLog, | 13 | mode: DeleteMode::RefAndRefLog, |
| 14 | deref: false, | ||
| 15 | }, | 14 | }, |
| 16 | name: name.try_into().expect("valid name"), | 15 | name: name.try_into().expect("valid name"), |
| 16 | deref: false, | ||
| 17 | } | 17 | } |
| 18 | } | 18 | } |
| 19 | 19 | ||
| @@ -78,12 +78,6 @@ mod transaction { · 58 unmodified lines | |||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | fn is_deref(edit: &RefEdit) -> bool { | ||
| 82 | match edit.change { | ||
| 83 | Change::Delete { deref, .. } | Change::Update { deref, .. } => deref, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | fn find<'a>(edits: &'a [RefEdit], name: &str) -> &'a RefEdit { | 81 | fn find<'a>(edits: &'a [RefEdit], name: &str) -> &'a RefEdit { |
| 88 | let name: FullName = name.try_into().unwrap(); | 82 | let name: FullName = name.try_into().unwrap(); |
| 89 | edits | 83 | edits |
| @@ -103,36 +97,36 @@ mod transaction { · 13 unmodified lines | |||
| 103 | change: Change::Delete { | 97 | change: Change::Delete { |
| 104 | previous: None, | 98 | previous: None, |
| 105 | mode: DeleteMode::RefAndRefLog, | 99 | mode: DeleteMode::RefAndRefLog, |
| 106 | deref: false, | ||
| 107 | }, | 100 | }, |
| 108 | name: "SYMBOLIC_PROBABLY_BUT_DEREF_IS_FALSE_SO_IGNORED".try_into()?, | 101 | name: "SYMBOLIC_PROBABLY_BUT_DEREF_IS_FALSE_SO_IGNORED".try_into()?, |
| 102 | deref: false, | ||
| 109 | }, | 103 | }, |
| 110 | RefEdit { | 104 | RefEdit { |
| 111 | change: Change::Delete { | 105 | change: Change::Delete { |
| 112 | previous: None, | 106 | previous: None, |
| 113 | mode: DeleteMode::RefAndRefLog, | 107 | mode: DeleteMode::RefAndRefLog, |
| 114 | deref: true, | ||
| 115 | }, | 108 | }, |
| 116 | name: "refs/heads/anything-but-not-symbolic".try_into()?, | 109 | name: "refs/heads/anything-but-not-symbolic".try_into()?, |
| 110 | deref: true, | ||
| 117 | }, | 111 | }, |
| 118 | RefEdit { | 112 | RefEdit { |
| 119 | change: Change::Delete { | 113 | change: Change::Delete { |
| 120 | previous: None, | 114 | previous: None, |
| 121 | mode: DeleteMode::RefAndRefLog, | 115 | mode: DeleteMode::RefAndRefLog, |
| 122 | deref: true, | ||
| 123 | }, | 116 | }, |
| 124 | name: "refs/heads/does-not-exist-and-deref-is-ignored".try_into()?, | 117 | name: "refs/heads/does-not-exist-and-deref-is-ignored".try_into()?, |
| 118 | deref: true, | ||
| 125 | }, | 119 | }, |
| 126 | ]; | 120 | ]; |
| 127 | 121 | ||
| 128 | edits.extend_with_splits_of_symbolic_refs(&store, |_| panic!("should not be called"))?; | 122 | edits.extend_with_splits_of_symbolic_refs(&store, |_| panic!("should not be called"))?; |
| 129 | assert_eq!(edits.len(), 3, "no edit was added"); | 123 | assert_eq!(edits.len(), 3, "no edit was added"); |
| 130 | assert!( | 124 | assert!( |
| 131 | !is_deref(find(&edits, "refs/heads/anything-but-not-symbolic")), | 125 | !find(&edits, "refs/heads/anything-but-not-symbolic").deref, |
| 132 | "the algorithm corrects these flags" | 126 | "the algorithm corrects these flags" |
| 133 | ); | 127 | ); |
| 134 | assert!( | 128 | assert!( |
| 135 | is_deref(find(&edits, "refs/heads/does-not-exist-and-deref-is-ignored")), | 129 | find(&edits, "refs/heads/does-not-exist-and-deref-is-ignored").deref, |
| 136 | "non-existing refs won't change the flag" | 130 | "non-existing refs won't change the flag" |
| 137 | ); | 131 | ); |
| 138 | store.assert_empty(); | 132 | store.assert_empty(); |