git_backend: write trees involved in conflict in git commit header

We haven't used custom Git commit headers for two main reasons:

1. I don't want commits created by jj to be different from any other
   commits. I don't want Git projects to get annoyed by such commit
   and reject them.

2. I've been concerned that tools don't know how to handle such
   headers, perhaps even resulting in crashes.

The first argument doesn't apply to commits with conflicts because
such commits would never be accepted by a project whether or not they
use custom commit headers. The second argument is less relevant for
conflicted commits because most tools will be confused by such commits
anyway.

Storing conflict information in commit headers means that we can
transfer them via the regular Git wire protocol. We already include
the tree objects nested inside the root-level tree, so they will also
be transferred.

So, let's start by writing the information redundantly to the commit
header and to the existing storage. That way we can roll it back if we
realize there's a problem with using commit headers.
This commit is contained in:
Martin von Zweigbergk 2024-03-09 08:55:52 -08:00 committed by Martin von Zweigbergk
parent ea5a208ca5
commit 4d42604913
11 changed files with 124 additions and 80 deletions

View file

@ -224,13 +224,13 @@ fn test_chmod_file_dir_deletion_conflicts() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
New conflicts appeared in these commits:
kmkuslsw 4cc432b5 file_deletion | (conflict) file_deletion
kmkuslsw b4c38719 file_deletion | (conflict) file_deletion
To resolve the conflicts, start by updating to it:
jj new kmkuslswpqwq
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: kmkuslsw 4cc432b5 file_deletion | (conflict) file_deletion
Working copy now at: kmkuslsw b4c38719 file_deletion | (conflict) file_deletion
Parent commit : zsuskuln c51c9c55 file | file
Parent commit : royxmykx 6b18b3c1 deletion | deletion
Added 0 files, modified 1 files, removed 0 files

View file

@ -379,10 +379,10 @@ fn test_diffedit_merge() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "-r", "@-"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Created royxmykx 2b5202ae (conflict) merge
Created royxmykx b90654a0 (conflict) merge
Rebased 1 descendant commits
Working copy now at: yqosqzyt 23b1fe1b (conflict) (empty) (no description set)
Parent commit : royxmykx 2b5202ae (conflict) merge
Working copy now at: yqosqzyt 1de824f2 (conflict) (empty) (no description set)
Parent commit : royxmykx b90654a0 (conflict) merge
Added 0 files, modified 0 files, removed 1 files
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s", "-r", "@-"]);

View file

@ -577,7 +577,7 @@ fn test_git_push_conflict() {
test_env.jj_cmd_ok(&workspace_root, &["describe", "-m", "third"]);
let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push", "--all"]);
insta::assert_snapshot!(stderr, @r###"
Error: Won't push commit 1973d389875c since it has conflicts
Error: Won't push commit d9ca3146ade7 since it has conflicts
"###);
}

View file

@ -100,7 +100,7 @@ fn test_rewrite_immutable_commands() {
insta::assert_snapshot!(stdout, @r###"
@ yqosqzyt test.user@example.com 2001-02-03 04:05:13.000 +07:00 3f89addf
(empty) (no description set)
mzvwutvl test.user@example.com 2001-02-03 04:05:11.000 +07:00 main 16ca9d80 conflict
mzvwutvl test.user@example.com 2001-02-03 04:05:11.000 +07:00 main 3d14df18 conflict
(empty) merge
~
@ -113,61 +113,61 @@ fn test_rewrite_immutable_commands() {
// abandon
let stderr = test_env.jj_cmd_failure(&repo_path, &["abandon", "main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// chmod
let stderr = test_env.jj_cmd_failure(&repo_path, &["chmod", "-r=main", "x", "file"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// describe
let stderr = test_env.jj_cmd_failure(&repo_path, &["describe", "main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// diffedit
let stderr = test_env.jj_cmd_failure(&repo_path, &["diffedit", "-r=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// edit
let stderr = test_env.jj_cmd_failure(&repo_path, &["edit", "main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// move --from
let stderr = test_env.jj_cmd_failure(&repo_path, &["move", "--from=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// move --to
let stderr = test_env.jj_cmd_failure(&repo_path, &["move", "--to=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// new --insert-before
let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// new --insert-after parent_of_main
let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "--insert-after", "description(b)"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// rebase -s
let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s=main", "-d=@"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// rebase -b
@ -179,43 +179,43 @@ fn test_rewrite_immutable_commands() {
// rebase -r
let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r=main", "-d=@"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// resolve
let stderr = test_env.jj_cmd_failure(&repo_path, &["resolve", "-r=description(merge)", "file"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// restore -c
let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "-c=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// restore --to
let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "--to=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// split
let stderr = test_env.jj_cmd_failure(&repo_path, &["split", "-r=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// squash
let stderr = test_env.jj_cmd_failure(&repo_path, &["squash", "-r=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
// unsquash
let stderr = test_env.jj_cmd_failure(&repo_path, &["unsquash", "-r=main"]);
insta::assert_snapshot!(stderr, @r###"
Error: Commit 16ca9d800b08 is immutable
Error: Commit 3d14df18607e is immutable
Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###);
}

View file

@ -31,7 +31,7 @@ fn test_obslog_with_or_without_diff() {
insta::assert_snapshot!(stdout, @r###"
@ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 5f4634a5 conflict
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 ebc23d4b conflict
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description
@ -44,11 +44,11 @@ fn test_obslog_with_or_without_diff() {
insta::assert_snapshot!(stdout, @r###"
@ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 5f4634a5 conflict
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 ebc23d4b conflict
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:08.000 +07:00 eac0d0da
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:08.000 +07:00 eac0d0da
(empty) my description
"###);
@ -66,7 +66,7 @@ fn test_obslog_with_or_without_diff() {
5 : foo
6 : bar
7 : >>>>>>>
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 5f4634a5 conflict
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 ebc23d4b conflict
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description
@ -84,7 +84,7 @@ fn test_obslog_with_or_without_diff() {
insta::assert_snapshot!(stdout, @r###"
@ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 5f4634a5 conflict
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 ebc23d4b conflict
my description
"###);
@ -93,7 +93,7 @@ fn test_obslog_with_or_without_diff() {
insta::assert_snapshot!(stdout, @r###"
rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 5f4634a5 conflict
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 ebc23d4b conflict
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description
@ -119,7 +119,7 @@ fn test_obslog_with_or_without_diff() {
-bar
->>>>>>>
+resolved
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 5f4634a5 conflict
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 ebc23d4b conflict
my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description

View file

@ -33,15 +33,15 @@ fn test_report_conflicts() {
insta::assert_snapshot!(stderr, @r###"
Rebased 3 commits
New conflicts appeared in these commits:
kkmpptxz a2593769 (conflict) C
rlvkpnrz 727244df (conflict) B
kkmpptxz 9baab11e (conflict) C
rlvkpnrz de73196a (conflict) B
To resolve the conflicts, start by updating to the first one:
jj new rlvkpnrzqnoo
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: zsuskuln 30928080 (conflict) (empty) (no description set)
Parent commit : kkmpptxz a2593769 (conflict) C
Working copy now at: zsuskuln 7dc9bf15 (conflict) (empty) (no description set)
Parent commit : kkmpptxz 9baab11e (conflict) C
Added 0 files, modified 1 files, removed 0 files
"###);
@ -50,8 +50,8 @@ fn test_report_conflicts() {
insta::assert_snapshot!(stderr, @r###"
Rebased 3 commits
Existing conflicts were resolved or abandoned from these commits:
kkmpptxz hidden a2593769 (conflict) C
rlvkpnrz hidden 727244df (conflict) B
kkmpptxz hidden 9baab11e (conflict) C
rlvkpnrz hidden de73196a (conflict) B
Working copy now at: zsuskuln 355a2e34 (empty) (no description set)
Parent commit : kkmpptxz ed071401 C
Added 0 files, modified 1 files, removed 0 files
@ -64,16 +64,16 @@ fn test_report_conflicts() {
insta::assert_snapshot!(stderr, @r###"
Also rebased 2 descendant commits onto parent of rebased commit
New conflicts appeared in these commits:
rlvkpnrz 9df65f08 (conflict) B
kkmpptxz 7530822d (conflict) C
rlvkpnrz e93270ab (conflict) B
kkmpptxz 4f0eeaa6 (conflict) C
To resolve the conflicts, start by updating to one of the first ones:
jj new rlvkpnrzqnoo
jj new kkmpptxzrspx
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: zsuskuln 203be58b (conflict) (empty) (no description set)
Parent commit : kkmpptxz 7530822d (conflict) C
Working copy now at: zsuskuln 83074dac (conflict) (empty) (no description set)
Parent commit : kkmpptxz 4f0eeaa6 (conflict) C
Added 0 files, modified 1 files, removed 0 files
"###);
@ -81,8 +81,8 @@ fn test_report_conflicts() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["new", "rlvkpnrzqnoo"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Working copy now at: vruxwmqv 406f84d0 (conflict) (empty) (no description set)
Parent commit : rlvkpnrz 9df65f08 (conflict) B
Working copy now at: vruxwmqv 2ec0b4c3 (conflict) (empty) (no description set)
Parent commit : rlvkpnrz e93270ab (conflict) B
Added 0 files, modified 1 files, removed 0 files
"###);
std::fs::write(repo_path.join("file"), "resolved\n").unwrap();
@ -90,7 +90,7 @@ fn test_report_conflicts() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Existing conflicts were resolved or abandoned from these commits:
rlvkpnrz hidden 9df65f08 (conflict) B
rlvkpnrz hidden e93270ab (conflict) B
Working copy now at: yostqsxw 8e160bc4 (empty) (no description set)
Parent commit : rlvkpnrz c5319490 B
"###);
@ -118,16 +118,16 @@ fn test_report_conflicts_with_divergent_commits() {
Concurrent modification detected, resolving automatically.
Rebased 3 commits
New conflicts appeared in these commits:
zsuskuln?? 76c40a95 (conflict) C3
zsuskuln?? e92329f2 (conflict) C2
kkmpptxz aed319ec (conflict) B
zsuskuln?? 94be9a4c (conflict) C3
zsuskuln?? cdae4322 (conflict) C2
kkmpptxz b76d6a88 (conflict) B
To resolve the conflicts, start by updating to the first one:
jj new kkmpptxzrspx
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: zsuskuln?? e92329f2 (conflict) C2
Parent commit : kkmpptxz aed319ec (conflict) B
Working copy now at: zsuskuln?? cdae4322 (conflict) C2
Parent commit : kkmpptxz b76d6a88 (conflict) B
Added 0 files, modified 1 files, removed 0 files
"###);
@ -136,9 +136,9 @@ fn test_report_conflicts_with_divergent_commits() {
insta::assert_snapshot!(stderr, @r###"
Rebased 3 commits
Existing conflicts were resolved or abandoned from these commits:
zsuskuln hidden 76c40a95 (conflict) C3
zsuskuln hidden e92329f2 (conflict) C2
kkmpptxz hidden aed319ec (conflict) B
zsuskuln hidden 94be9a4c (conflict) C3
zsuskuln hidden cdae4322 (conflict) C2
kkmpptxz hidden b76d6a88 (conflict) B
Working copy now at: zsuskuln?? 9c33e9a9 C2
Parent commit : kkmpptxz 9ce42c2a B
Added 0 files, modified 1 files, removed 0 files
@ -151,13 +151,13 @@ fn test_report_conflicts_with_divergent_commits() {
insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits
New conflicts appeared in these commits:
zsuskuln?? 0d6cb6b7 (conflict) C2
zsuskuln?? 33752e7e (conflict) C2
To resolve the conflicts, start by updating to it:
jj new zsuskulnrvyr
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: zsuskuln?? 0d6cb6b7 (conflict) C2
Working copy now at: zsuskuln?? 33752e7e (conflict) C2
Parent commit : zzzzzzzz 00000000 (empty) (no description set)
Added 0 files, modified 1 files, removed 0 files
"###);
@ -168,7 +168,7 @@ fn test_report_conflicts_with_divergent_commits() {
insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits
New conflicts appeared in these commits:
zsuskuln?? 9652a362 (conflict) C3
zsuskuln?? 37bb9c2f (conflict) C3
To resolve the conflicts, start by updating to it:
jj new zsuskulnrvyr
Then use `jj resolve`, or edit the conflict markers in the file directly.
@ -184,7 +184,7 @@ fn test_report_conflicts_with_divergent_commits() {
insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits
Existing conflicts were resolved or abandoned from these commits:
zsuskuln hidden 0d6cb6b7 (conflict) C2
zsuskuln hidden 33752e7e (conflict) C2
Working copy now at: zsuskuln?? 24f79296 C2
Parent commit : kkmpptxz 9ce42c2a B
Added 0 files, modified 1 files, removed 0 files
@ -198,6 +198,6 @@ fn test_report_conflicts_with_divergent_commits() {
insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits
Existing conflicts were resolved or abandoned from these commits:
zsuskuln hidden 9652a362 (conflict) C3
zsuskuln hidden 37bb9c2f (conflict) C3
"###);
}

View file

@ -220,13 +220,13 @@ conflict
insta::assert_snapshot!(stderr, @r###"
Resolving conflicts in: file
New conflicts appeared in these commits:
vruxwmqv 23991847 conflict | (conflict) conflict
vruxwmqv 8144e92d conflict | (conflict) conflict
To resolve the conflicts, start by updating to it:
jj new vruxwmqvtpmx
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: vruxwmqv 23991847 conflict | (conflict) conflict
Working copy now at: vruxwmqv 8144e92d conflict | (conflict) conflict
Parent commit : zsuskuln aa493daf a | a
Parent commit : royxmykx db6a4daf b | b
Added 0 files, modified 1 files, removed 0 files
@ -690,13 +690,13 @@ fn test_multiple_conflicts() {
insta::assert_snapshot!(stderr, @r###"
Resolving conflicts in: another_file
New conflicts appeared in these commits:
vruxwmqv c3c25bce conflict | (conflict) conflict
vruxwmqv 1e22a8e4 conflict | (conflict) conflict
To resolve the conflicts, start by updating to it:
jj new vruxwmqvtpmx
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: vruxwmqv c3c25bce conflict | (conflict) conflict
Working copy now at: vruxwmqv 1e22a8e4 conflict | (conflict) conflict
Parent commit : zsuskuln de7553ef a | a
Parent commit : royxmykx f68bc2f0 b | b
Added 0 files, modified 1 files, removed 0 files
@ -727,13 +727,13 @@ fn test_multiple_conflicts() {
insta::assert_snapshot!(stderr, @r###"
Resolving conflicts in: another_file
New conflicts appeared in these commits:
vruxwmqv fd3874cd conflict | (conflict) conflict
vruxwmqv 3c438f88 conflict | (conflict) conflict
To resolve the conflicts, start by updating to it:
jj new vruxwmqvtpmx
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: vruxwmqv fd3874cd conflict | (conflict) conflict
Working copy now at: vruxwmqv 3c438f88 conflict | (conflict) conflict
Parent commit : zsuskuln de7553ef a | a
Parent commit : royxmykx f68bc2f0 b | b
Added 0 files, modified 1 files, removed 0 files

View file

@ -62,13 +62,13 @@ fn test_restore() {
Created rlvkpnrz e25100af (empty) (no description set)
Rebased 1 descendant commits
New conflicts appeared in these commits:
kkmpptxz e301deb3 (conflict) (no description set)
kkmpptxz 761deaef (conflict) (no description set)
To resolve the conflicts, start by updating to it:
jj new kkmpptxzrspx
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: kkmpptxz e301deb3 (conflict) (no description set)
Working copy now at: kkmpptxz 761deaef (conflict) (no description set)
Parent commit : rlvkpnrz e25100af (empty) (no description set)
Added 0 files, modified 1 files, removed 0 files
"###);
@ -197,8 +197,8 @@ fn test_restore_conflicted_merge() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["restore", "file"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Created vruxwmqv b2c9c888 conflict | (conflict) (empty) conflict
Working copy now at: vruxwmqv b2c9c888 conflict | (conflict) (empty) conflict
Created vruxwmqv 0817af7e conflict | (conflict) (empty) conflict
Working copy now at: vruxwmqv 0817af7e conflict | (conflict) (empty) conflict
Parent commit : zsuskuln aa493daf a | a
Parent commit : royxmykx db6a4daf b | b
Added 0 files, modified 1 files, removed 0 files
@ -236,8 +236,8 @@ fn test_restore_conflicted_merge() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["restore"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Created vruxwmqv 4fc10820 conflict | (conflict) (empty) conflict
Working copy now at: vruxwmqv 4fc10820 conflict | (conflict) (empty) conflict
Created vruxwmqv da925083 conflict | (conflict) (empty) conflict
Working copy now at: vruxwmqv da925083 conflict | (conflict) (empty) conflict
Parent commit : zsuskuln aa493daf a | a
Parent commit : royxmykx db6a4daf b | b
Added 0 files, modified 1 files, removed 0 files

View file

@ -56,7 +56,7 @@ fn test_enable_tree_level_conflicts() {
// non-empty
let stdout = test_env.jj_cmd_success(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r###"
@ mzvwutvl test.user@example.com 2001-02-03 04:05:13.000 +07:00 54c562fa conflict
@ mzvwutvl test.user@example.com 2001-02-03 04:05:13.000 +07:00 51f1748d conflict
(no description set)
zsuskuln test.user@example.com 2001-02-03 04:05:10.000 +07:00 5100e4e1 conflict
(empty) merge

View file

@ -321,7 +321,7 @@ fn test_workspaces_conflicting_edits() {
"###);
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
@r###"
a3c96849ef9f124cbfc2416dc13bf17309d5020a (divergent)
b0b43f24d501eab73ea245c115cdc571541e4a51 (divergent)
fe8f41ed01d693b2d4365cd89e42ad9c531a939b default@
@ a1896a17282f19089a5cec44358d6609910e0513 secondary@ (divergent)
@ -333,7 +333,7 @@ fn test_workspaces_conflicting_edits() {
let stdout = get_log_output(&test_env, &secondary_path);
assert!(!stdout.starts_with("The working copy is stale"));
insta::assert_snapshot!(stdout, @r###"
a3c96849ef9f124cbfc2416dc13bf17309d5020a (divergent)
b0b43f24d501eab73ea245c115cdc571541e4a51 (divergent)
fe8f41ed01d693b2d4365cd89e42ad9c531a939b default@
@ a1896a17282f19089a5cec44358d6609910e0513 secondary@ (divergent)

View file

@ -25,7 +25,8 @@ use std::time::SystemTime;
use std::{fs, io, str};
use async_trait::async_trait;
use gix::objs::{CommitRefIter, WriteTo};
use gix::bstr::BString;
use gix::objs::{CommitRef, CommitRefIter, WriteTo};
use itertools::Itertools;
use prost::Message;
use smallvec::SmallVec;
@ -54,6 +55,8 @@ const CHANGE_ID_LENGTH: usize = 16;
const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/";
const CONFLICT_SUFFIX: &str = ".jjconflict";
const JJ_TREES_COMMIT_HEADER: &[u8] = b"jj:trees";
#[derive(Debug, Error)]
pub enum GitBackendInitError {
#[error("Failed to initialize git repository")]
@ -420,6 +423,27 @@ fn gix_open_opts_from_settings(settings: &UserSettings) -> gix::open::Options {
.open_path_as_is(true)
}
/// Reads the `jj:trees` header from the commit.
fn root_tree_from_header(git_commit: &CommitRef) -> Result<Option<MergedTreeId>, ()> {
for (key, value) in &git_commit.extra_headers {
if *key == JJ_TREES_COMMIT_HEADER {
let mut tree_ids = SmallVec::new();
for hex in str::from_utf8(value.as_ref()).or(Err(()))?.split(' ') {
let tree_id = TreeId::try_from_hex(hex).or(Err(()))?;
if tree_id.as_bytes().len() != HASH_LENGTH {
return Err(());
}
tree_ids.push(tree_id);
}
if tree_ids.len() % 2 == 0 {
return Err(());
}
return Ok(Some(MergedTreeId::Merge(Merge::from_vec(tree_ids))));
}
}
Ok(None)
}
fn commit_from_git_without_root_parent(
id: &CommitId,
git_object: &gix::Object,
@ -449,11 +473,15 @@ fn commit_from_git_without_root_parent(
let tree_id = TreeId::from_bytes(commit.tree().as_bytes());
// If this commit is a conflict, we'll update the root tree later, when we read
// the extra metadata.
let root_tree = if uses_tree_conflict_format {
MergedTreeId::resolved(tree_id)
} else {
MergedTreeId::Legacy(tree_id)
};
let root_tree = root_tree_from_header(&commit)
.map_err(|()| to_read_object_err("Invalid jj:trees header", id))?;
let root_tree = root_tree.unwrap_or_else(|| {
if uses_tree_conflict_format {
MergedTreeId::resolved(tree_id)
} else {
MergedTreeId::Legacy(tree_id)
}
});
// Use lossy conversion as commit message with "mojibake" is still better than
// nothing.
// TODO: what should we do with commit.encoding?
@ -571,7 +599,13 @@ fn deserialize_extras(commit: &mut Commit, bytes: &[u8]) {
.iter()
.map(|id_bytes| TreeId::from_bytes(id_bytes))
.collect();
commit.root_tree = MergedTreeId::Merge(merge_builder.build());
let merge = merge_builder.build();
// Check that the trees from the extras match the one we found in the jj:trees
// header
if let MergedTreeId::Merge(existing_merge) = &commit.root_tree {
assert!(existing_merge.is_resolved() || *existing_merge == merge);
}
commit.root_tree = MergedTreeId::Merge(merge);
} else {
// uses_tree_conflict_format was set but there was no root_tree override in the
// proto, which means we should just promote the tree id from the
@ -1097,6 +1131,16 @@ impl Backend for GitBackend {
parents.push(validate_git_object_id(parent_id)?);
}
}
let mut extra_headers = vec![];
if let MergedTreeId::Merge(tree_ids) = &contents.root_tree {
if !tree_ids.is_resolved() {
let value = tree_ids.iter().map(|id| id.hex()).join(" ").into_bytes();
extra_headers.push((
BString::new(JJ_TREES_COMMIT_HEADER.to_vec()),
BString::new(value),
));
}
}
let extras = serialize_extras(&contents);
// If two writers write commits of the same id with different metadata, they
@ -1114,7 +1158,7 @@ impl Backend for GitBackend {
committer: committer.into(),
encoding: None,
parents: parents.clone(),
extra_headers: Default::default(),
extra_headers: extra_headers.clone(),
};
if let Some(sign) = &mut sign_with {