|
1 | 1 | use anyhow::{Context as _, Result, anyhow}; |
2 | | -use bstr::ByteSlice as _; |
| 2 | +use bstr::{BString, ByteSlice as _}; |
3 | 3 | use but_core::{ |
4 | 4 | RefMetadata, RepositoryExt, |
5 | 5 | ref_metadata::{StackId, WorkspaceCommitRelation, WorkspaceStack, WorkspaceStackBranch}, |
6 | 6 | }; |
7 | 7 | use but_ctx::Context; |
8 | 8 | use but_meta::{VirtualBranchesTomlMetadata, virtual_branches_legacy_types::Target}; |
9 | | -use but_testsupport::{gix_testtools, open_repo}; |
| 9 | +use but_testsupport::{gix_testtools, open_repo, visualize_commit_graph}; |
10 | 10 | use git2::build::CheckoutBuilder; |
11 | 11 | use gitbutler_edit_mode::commands::{ |
12 | 12 | abort_and_return_to_workspace, enter_edit_mode, save_and_return_to_workspace, |
@@ -98,6 +98,115 @@ fn basic_leaving_edit_mode() -> Result<()> { |
98 | 98 | Ok(()) |
99 | 99 | } |
100 | 100 |
|
| 101 | +#[test] |
| 102 | +fn multiple_commits_created_during_edit_mode() -> Result<()> { |
| 103 | + let (mut ctx, _tempdir) = command_ctx("conficted_entries_get_written_when_leaving_edit_mode")?; |
| 104 | + let repo = ctx.repo.get()?; |
| 105 | + |
| 106 | + let foobar = repo.head_commit()?.decode()?.parents().next().unwrap(); |
| 107 | + |
| 108 | + let worktree_dir = repo.workdir().unwrap().to_owned(); |
| 109 | + drop(repo); |
| 110 | + let stack_id = stack_id(&ctx)?; |
| 111 | + enter_edit_mode(&mut ctx, foobar, stack_id)?; |
| 112 | + |
| 113 | + let repo = ctx.repo.get()?; |
| 114 | + let commit = gix::objs::Commit::try_from(repo.find_commit(foobar)?.decode()?)?; |
| 115 | + let first_id = repo.write_object(gix::objs::Commit { |
| 116 | + message: BString::from(b"first commit added"), |
| 117 | + parents: [foobar].into(), |
| 118 | + ..commit.clone() |
| 119 | + })?; |
| 120 | + let second_id = repo.write_object(gix::objs::Commit { |
| 121 | + message: BString::from(b"second commit added"), |
| 122 | + parents: [first_id.detach()].into(), |
| 123 | + ..commit |
| 124 | + })?; |
| 125 | + repo.edit_reference(gix::refs::transaction::RefEdit { |
| 126 | + change: gix::refs::transaction::Change::Update { |
| 127 | + log: gix::refs::transaction::LogChange { |
| 128 | + mode: gix::refs::transaction::RefLog::AndReference, |
| 129 | + force_create_reflog: false, |
| 130 | + message: b"arbitrary message".into(), |
| 131 | + }, |
| 132 | + expected: gix::refs::transaction::PreviousValue::Any, |
| 133 | + new: gix::refs::Target::Object(second_id.detach()), |
| 134 | + }, |
| 135 | + name: "HEAD".try_into().unwrap(), |
| 136 | + deref: true, |
| 137 | + })?; |
| 138 | + drop(repo); |
| 139 | + |
| 140 | + std::fs::write(worktree_dir.join("file"), "edited during edit mode\n")?; |
| 141 | + |
| 142 | + save_and_return_to_workspace(&mut ctx)?; |
| 143 | + |
| 144 | + let repo = &*ctx.repo.get()?; |
| 145 | + insta::assert_snapshot!(visualize_commit_graph(repo, "refs/heads/gitbutler/workspace")?, @r" |
| 146 | + * 59ab552 (HEAD -> gitbutler/workspace) GitButler Workspace Commit |
| 147 | + * f6d3539 (branchy) second commit added |
| 148 | + * d39dd61 first commit added |
| 149 | + * 26804c3 foobar |
| 150 | + * 7950f06 (origin/main, origin/HEAD, main, gitbutler/target) init |
| 151 | + "); |
| 152 | + // As usual, any uncommitted changes (to "file" in this test) is applied |
| 153 | + // onto the HEAD commit at the time of exiting edit mode. |
| 154 | + let blob = repo.rev_parse_single(b"HEAD^{/second}:file")?.object()?; |
| 155 | + insta::assert_snapshot!(blob.data.as_bstr(), @"edited during edit mode"); |
| 156 | + // Rebase also happens correctly. |
| 157 | + let blob = repo.rev_parse_single(b"HEAD:file")?.object()?; |
| 158 | + insta::assert_snapshot!(blob.data.as_bstr(), @"edited during edit mode"); |
| 159 | + |
| 160 | + Ok(()) |
| 161 | +} |
| 162 | + |
| 163 | +#[test] |
| 164 | +fn apply_commit_on_itself() -> Result<()> { |
| 165 | + let (mut ctx, _tempdir) = command_ctx("conficted_entries_get_written_when_leaving_edit_mode")?; |
| 166 | + let repo = ctx.repo.get()?; |
| 167 | + |
| 168 | + let foobar = repo.head_commit()?.decode()?.parents().next().unwrap(); |
| 169 | + |
| 170 | + drop(repo); |
| 171 | + let stack_id = stack_id(&ctx)?; |
| 172 | + enter_edit_mode(&mut ctx, foobar, stack_id)?; |
| 173 | + |
| 174 | + let repo = ctx.repo.get()?; |
| 175 | + // Set HEAD to gitbutler/workspace, detached, to see what happens when we |
| 176 | + // try to apply itself on itself. |
| 177 | + let workspace_commit_id = repo |
| 178 | + .find_reference("refs/heads/gitbutler/workspace")? |
| 179 | + .peel_to_commit()? |
| 180 | + .id; |
| 181 | + repo.edit_reference(gix::refs::transaction::RefEdit { |
| 182 | + change: gix::refs::transaction::Change::Update { |
| 183 | + log: gix::refs::transaction::LogChange { |
| 184 | + mode: gix::refs::transaction::RefLog::AndReference, |
| 185 | + force_create_reflog: false, |
| 186 | + message: b"arbitrary message".into(), |
| 187 | + }, |
| 188 | + expected: gix::refs::transaction::PreviousValue::Any, |
| 189 | + new: gix::refs::Target::Object(workspace_commit_id), |
| 190 | + }, |
| 191 | + name: "HEAD".try_into().unwrap(), |
| 192 | + deref: true, |
| 193 | + })?; |
| 194 | + drop(repo); |
| 195 | + |
| 196 | + save_and_return_to_workspace(&mut ctx)?; |
| 197 | + |
| 198 | + let repo = &*ctx.repo.get()?; |
| 199 | + // It works. |
| 200 | + insta::assert_snapshot!(visualize_commit_graph(repo, "refs/heads/gitbutler/workspace")?, @r" |
| 201 | + * 85cd48c (HEAD -> gitbutler/workspace, gitbutler/edit) GitButler Workspace Commit |
| 202 | + * 6eb9642 (branchy) GitButler Workspace Commit |
| 203 | + * 26804c3 foobar |
| 204 | + * 7950f06 (origin/main, origin/HEAD, main, gitbutler/target) init |
| 205 | + "); |
| 206 | + |
| 207 | + Ok(()) |
| 208 | +} |
| 209 | + |
101 | 210 | // Fixture: |
102 | 211 | // * xxx (HEAD -> gitbutler/workspace) GitButler Workspace Commit |
103 | 212 | // * xxx foobar |
|
0 commit comments