History

feat: `file::Transaction::rollback()` allows to explicitly roll back a pending change. (#595)

As opposed to dropping the Transaction, this method allows to obtain all edits that would have been applied.

Sebastian Thiel committed 3 years ago

Commit e86e1591 parent 5f7fe692 files · +53 −0Browse files

Unified · Split

2 files
  1. git-ref/src/store/file/transaction/prepare.rs +14 −0
  2. git-ref/tests/file/transaction/prepare_and_commit/create_or_update.rs +39 −0
git-ref/src/store/file/transaction/prepare.rs+14−0
@@ -357,6 +357,20 @@ impl<'s, 'p> Transaction<'s, 'p> { · 356 unmodified lines
357 self.updates = Some(updates);357 self.updates = Some(updates);
358 Ok(self)358 Ok(self)
359 }359 }
360
361 /// Rollback all intermediate state and return the `RefEdits` as we know them thus far.
362 ///
363 /// Note that they have been altered compared to what was initially provided as they have
364 /// been split and know about their current state on disk.
365 ///
366 /// # Note
367 ///
368 /// A rollback happens automatically as this instance is dropped as well.
369 pub fn rollback(self) -> Vec<RefEdit> {
370 self.updates
371 .map(|updates| updates.into_iter().map(|u| u.update).collect())
372 .unwrap_or_default()
373 }
360}374}
361 375
362fn possibly_adjust_name_for_prefixes(name: &FullNameRef) -> Option<FullName> {376fn possibly_adjust_name_for_prefixes(name: &FullNameRef) -> Option<FullName> {
git-ref/tests/file/transaction/prepare_and_commit/create_or_update.rs+39−0
@@ -68,6 +68,45 @@ fn reference_with_equally_named_empty_or_non_empty_directory_already_in_place_ca · 67 unmodified lines
68 Ok(())68 Ok(())
69}69}
70 70
71#[test]
72fn intermediate_directories_are_removed_on_rollback() -> crate::Result {
73 for explicit_rollback in [false, true] {
74 let (dir, store) = empty_store()?;
75
76 let create_at = |name: &str| RefEdit {
77 change: Change::Update {
78 log: LogChange::default(),
79 expected: PreviousValue::MustNotExist,
80 new: Target::Peeled(git_hash::Kind::Sha1.null()),
81 },
82 name: name.try_into().expect("valid"),
83 deref: false,
84 };
85
86 let transaction = store.transaction().prepare(
87 [create_at("refs/heads/a/b/ref"), create_at("refs/heads/a/c/ref")],
88 Fail::Immediately,
89 Fail::Immediately,
90 )?;
91
92 assert!(
93 dir.path().join("refs/heads/a/b").exists(),
94 "lock files have been created in their place to avoid concurrent modification"
95 );
96 assert!(dir.path().join("refs/heads/a/c").exists());
97
98 if explicit_rollback {
99 transaction.rollback();
100 } else {
101 drop(transaction);
102 }
103
104 assert!(!dir.path().join("refs/heads").exists());
105 assert!(!dir.path().join("refs").exists(), "we go all in right now and also remove the refs directory. 'git' might not do that, but it's not a problem either");
106 }
107 Ok(())
108}
109
71#[test]110#[test]
72fn reference_with_old_value_must_exist_when_creating_it() -> crate::Result {111fn reference_with_old_value_must_exist_when_creating_it() -> crate::Result {
73 let (_keep, store) = empty_store()?;112 let (_keep, store) = empty_store()?;