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!(stdout, @"");
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
New conflicts appeared in these commits: 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: To resolve the conflicts, start by updating to it:
jj new kmkuslswpqwq jj new kmkuslswpqwq
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. 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 : zsuskuln c51c9c55 file | file
Parent commit : royxmykx 6b18b3c1 deletion | deletion Parent commit : royxmykx 6b18b3c1 deletion | deletion
Added 0 files, modified 1 files, removed 0 files 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", "@-"]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "-r", "@-"]);
insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Created royxmykx 2b5202ae (conflict) merge Created royxmykx b90654a0 (conflict) merge
Rebased 1 descendant commits Rebased 1 descendant commits
Working copy now at: yqosqzyt 23b1fe1b (conflict) (empty) (no description set) Working copy now at: yqosqzyt 1de824f2 (conflict) (empty) (no description set)
Parent commit : royxmykx 2b5202ae (conflict) merge Parent commit : royxmykx b90654a0 (conflict) merge
Added 0 files, modified 0 files, removed 1 files Added 0 files, modified 0 files, removed 1 files
"###); "###);
let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s", "-r", "@-"]); 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"]); test_env.jj_cmd_ok(&workspace_root, &["describe", "-m", "third"]);
let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push", "--all"]); let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push", "--all"]);
insta::assert_snapshot!(stderr, @r###" 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###" insta::assert_snapshot!(stdout, @r###"
@ yqosqzyt test.user@example.com 2001-02-03 04:05:13.000 +07:00 3f89addf @ yqosqzyt test.user@example.com 2001-02-03 04:05:13.000 +07:00 3f89addf
(empty) (no description set) (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 (empty) merge
~ ~
@ -113,61 +113,61 @@ fn test_rewrite_immutable_commands() {
// abandon // abandon
let stderr = test_env.jj_cmd_failure(&repo_path, &["abandon", "main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["abandon", "main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// chmod // chmod
let stderr = test_env.jj_cmd_failure(&repo_path, &["chmod", "-r=main", "x", "file"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["chmod", "-r=main", "x", "file"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// describe // describe
let stderr = test_env.jj_cmd_failure(&repo_path, &["describe", "main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["describe", "main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// diffedit // diffedit
let stderr = test_env.jj_cmd_failure(&repo_path, &["diffedit", "-r=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["diffedit", "-r=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// edit // edit
let stderr = test_env.jj_cmd_failure(&repo_path, &["edit", "main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["edit", "main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// move --from // move --from
let stderr = test_env.jj_cmd_failure(&repo_path, &["move", "--from=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["move", "--from=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// move --to // move --to
let stderr = test_env.jj_cmd_failure(&repo_path, &["move", "--to=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["move", "--to=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// new --insert-before // new --insert-before
let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// new --insert-after parent_of_main // new --insert-after parent_of_main
let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "--insert-after", "description(b)"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "--insert-after", "description(b)"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// rebase -s // rebase -s
let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s=main", "-d=@"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s=main", "-d=@"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// rebase -b // rebase -b
@ -179,43 +179,43 @@ fn test_rewrite_immutable_commands() {
// rebase -r // rebase -r
let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r=main", "-d=@"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r=main", "-d=@"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// resolve // resolve
let stderr = test_env.jj_cmd_failure(&repo_path, &["resolve", "-r=description(merge)", "file"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["resolve", "-r=description(merge)", "file"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// restore -c // restore -c
let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "-c=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "-c=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// restore --to // restore --to
let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "--to=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "--to=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// split // split
let stderr = test_env.jj_cmd_failure(&repo_path, &["split", "-r=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["split", "-r=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// squash // squash
let stderr = test_env.jj_cmd_failure(&repo_path, &["squash", "-r=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["squash", "-r=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. Hint: Configure the set of immutable commits via `revset-aliases.immutable_heads()`.
"###); "###);
// unsquash // unsquash
let stderr = test_env.jj_cmd_failure(&repo_path, &["unsquash", "-r=main"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["unsquash", "-r=main"]);
insta::assert_snapshot!(stderr, @r###" 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()`. 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###" insta::assert_snapshot!(stdout, @r###"
@ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3 @ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description 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 my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description my description
@ -44,11 +44,11 @@ fn test_obslog_with_or_without_diff() {
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r###"
@ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3 @ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description 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 my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description 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 (empty) my description
"###); "###);
@ -66,7 +66,7 @@ fn test_obslog_with_or_without_diff() {
5 : foo 5 : foo
6 : bar 6 : bar
7 : >>>>>>> 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 my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description my description
@ -84,7 +84,7 @@ fn test_obslog_with_or_without_diff() {
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r###"
@ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3 @ rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description 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 my description
"###); "###);
@ -93,7 +93,7 @@ fn test_obslog_with_or_without_diff() {
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r###"
rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3 rlvkpnrz test.user@example.com 2001-02-03 04:05:10.000 +07:00 66b42ad3
my description 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 my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description my description
@ -119,7 +119,7 @@ fn test_obslog_with_or_without_diff() {
-bar -bar
->>>>>>> ->>>>>>>
+resolved +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 my description
rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc rlvkpnrz hidden test.user@example.com 2001-02-03 04:05:09.000 +07:00 6fbba7bc
my description my description

View file

@ -33,15 +33,15 @@ fn test_report_conflicts() {
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Rebased 3 commits Rebased 3 commits
New conflicts appeared in these commits: New conflicts appeared in these commits:
kkmpptxz a2593769 (conflict) C kkmpptxz 9baab11e (conflict) C
rlvkpnrz 727244df (conflict) B rlvkpnrz de73196a (conflict) B
To resolve the conflicts, start by updating to the first one: To resolve the conflicts, start by updating to the first one:
jj new rlvkpnrzqnoo jj new rlvkpnrzqnoo
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: zsuskuln 30928080 (conflict) (empty) (no description set) Working copy now at: zsuskuln 7dc9bf15 (conflict) (empty) (no description set)
Parent commit : kkmpptxz a2593769 (conflict) C Parent commit : kkmpptxz 9baab11e (conflict) C
Added 0 files, modified 1 files, removed 0 files Added 0 files, modified 1 files, removed 0 files
"###); "###);
@ -50,8 +50,8 @@ fn test_report_conflicts() {
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Rebased 3 commits Rebased 3 commits
Existing conflicts were resolved or abandoned from these commits: Existing conflicts were resolved or abandoned from these commits:
kkmpptxz hidden a2593769 (conflict) C kkmpptxz hidden 9baab11e (conflict) C
rlvkpnrz hidden 727244df (conflict) B rlvkpnrz hidden de73196a (conflict) B
Working copy now at: zsuskuln 355a2e34 (empty) (no description set) Working copy now at: zsuskuln 355a2e34 (empty) (no description set)
Parent commit : kkmpptxz ed071401 C Parent commit : kkmpptxz ed071401 C
Added 0 files, modified 1 files, removed 0 files Added 0 files, modified 1 files, removed 0 files
@ -64,16 +64,16 @@ fn test_report_conflicts() {
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Also rebased 2 descendant commits onto parent of rebased commit Also rebased 2 descendant commits onto parent of rebased commit
New conflicts appeared in these commits: New conflicts appeared in these commits:
rlvkpnrz 9df65f08 (conflict) B rlvkpnrz e93270ab (conflict) B
kkmpptxz 7530822d (conflict) C kkmpptxz 4f0eeaa6 (conflict) C
To resolve the conflicts, start by updating to one of the first ones: To resolve the conflicts, start by updating to one of the first ones:
jj new rlvkpnrzqnoo jj new rlvkpnrzqnoo
jj new kkmpptxzrspx jj new kkmpptxzrspx
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: zsuskuln 203be58b (conflict) (empty) (no description set) Working copy now at: zsuskuln 83074dac (conflict) (empty) (no description set)
Parent commit : kkmpptxz 7530822d (conflict) C Parent commit : kkmpptxz 4f0eeaa6 (conflict) C
Added 0 files, modified 1 files, removed 0 files 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"]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["new", "rlvkpnrzqnoo"]);
insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Working copy now at: vruxwmqv 406f84d0 (conflict) (empty) (no description set) Working copy now at: vruxwmqv 2ec0b4c3 (conflict) (empty) (no description set)
Parent commit : rlvkpnrz 9df65f08 (conflict) B Parent commit : rlvkpnrz e93270ab (conflict) B
Added 0 files, modified 1 files, removed 0 files Added 0 files, modified 1 files, removed 0 files
"###); "###);
std::fs::write(repo_path.join("file"), "resolved\n").unwrap(); 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!(stdout, @"");
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Existing conflicts were resolved or abandoned from these commits: 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) Working copy now at: yostqsxw 8e160bc4 (empty) (no description set)
Parent commit : rlvkpnrz c5319490 B Parent commit : rlvkpnrz c5319490 B
"###); "###);
@ -118,16 +118,16 @@ fn test_report_conflicts_with_divergent_commits() {
Concurrent modification detected, resolving automatically. Concurrent modification detected, resolving automatically.
Rebased 3 commits Rebased 3 commits
New conflicts appeared in these commits: New conflicts appeared in these commits:
zsuskuln?? 76c40a95 (conflict) C3 zsuskuln?? 94be9a4c (conflict) C3
zsuskuln?? e92329f2 (conflict) C2 zsuskuln?? cdae4322 (conflict) C2
kkmpptxz aed319ec (conflict) B kkmpptxz b76d6a88 (conflict) B
To resolve the conflicts, start by updating to the first one: To resolve the conflicts, start by updating to the first one:
jj new kkmpptxzrspx jj new kkmpptxzrspx
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. Then run `jj squash` to move the resolution into the conflicted commit.
Working copy now at: zsuskuln?? e92329f2 (conflict) C2 Working copy now at: zsuskuln?? cdae4322 (conflict) C2
Parent commit : kkmpptxz aed319ec (conflict) B Parent commit : kkmpptxz b76d6a88 (conflict) B
Added 0 files, modified 1 files, removed 0 files 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###" insta::assert_snapshot!(stderr, @r###"
Rebased 3 commits Rebased 3 commits
Existing conflicts were resolved or abandoned from these commits: Existing conflicts were resolved or abandoned from these commits:
zsuskuln hidden 76c40a95 (conflict) C3 zsuskuln hidden 94be9a4c (conflict) C3
zsuskuln hidden e92329f2 (conflict) C2 zsuskuln hidden cdae4322 (conflict) C2
kkmpptxz hidden aed319ec (conflict) B kkmpptxz hidden b76d6a88 (conflict) B
Working copy now at: zsuskuln?? 9c33e9a9 C2 Working copy now at: zsuskuln?? 9c33e9a9 C2
Parent commit : kkmpptxz 9ce42c2a B Parent commit : kkmpptxz 9ce42c2a B
Added 0 files, modified 1 files, removed 0 files 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###" insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits Rebased 1 commits
New conflicts appeared in these commits: New conflicts appeared in these commits:
zsuskuln?? 0d6cb6b7 (conflict) C2 zsuskuln?? 33752e7e (conflict) C2
To resolve the conflicts, start by updating to it: To resolve the conflicts, start by updating to it:
jj new zsuskulnrvyr jj new zsuskulnrvyr
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. 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) Parent commit : zzzzzzzz 00000000 (empty) (no description set)
Added 0 files, modified 1 files, removed 0 files 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###" insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits Rebased 1 commits
New conflicts appeared in these commits: New conflicts appeared in these commits:
zsuskuln?? 9652a362 (conflict) C3 zsuskuln?? 37bb9c2f (conflict) C3
To resolve the conflicts, start by updating to it: To resolve the conflicts, start by updating to it:
jj new zsuskulnrvyr jj new zsuskulnrvyr
Then use `jj resolve`, or edit the conflict markers in the file directly. 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###" insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits Rebased 1 commits
Existing conflicts were resolved or abandoned from these 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 Working copy now at: zsuskuln?? 24f79296 C2
Parent commit : kkmpptxz 9ce42c2a B Parent commit : kkmpptxz 9ce42c2a B
Added 0 files, modified 1 files, removed 0 files 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###" insta::assert_snapshot!(stderr, @r###"
Rebased 1 commits Rebased 1 commits
Existing conflicts were resolved or abandoned from these 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###" insta::assert_snapshot!(stderr, @r###"
Resolving conflicts in: file Resolving conflicts in: file
New conflicts appeared in these commits: 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: To resolve the conflicts, start by updating to it:
jj new vruxwmqvtpmx jj new vruxwmqvtpmx
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. 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 : zsuskuln aa493daf a | a
Parent commit : royxmykx db6a4daf b | b Parent commit : royxmykx db6a4daf b | b
Added 0 files, modified 1 files, removed 0 files Added 0 files, modified 1 files, removed 0 files
@ -690,13 +690,13 @@ fn test_multiple_conflicts() {
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Resolving conflicts in: another_file Resolving conflicts in: another_file
New conflicts appeared in these commits: 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: To resolve the conflicts, start by updating to it:
jj new vruxwmqvtpmx jj new vruxwmqvtpmx
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. 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 : zsuskuln de7553ef a | a
Parent commit : royxmykx f68bc2f0 b | b Parent commit : royxmykx f68bc2f0 b | b
Added 0 files, modified 1 files, removed 0 files Added 0 files, modified 1 files, removed 0 files
@ -727,13 +727,13 @@ fn test_multiple_conflicts() {
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Resolving conflicts in: another_file Resolving conflicts in: another_file
New conflicts appeared in these commits: 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: To resolve the conflicts, start by updating to it:
jj new vruxwmqvtpmx jj new vruxwmqvtpmx
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. 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 : zsuskuln de7553ef a | a
Parent commit : royxmykx f68bc2f0 b | b Parent commit : royxmykx f68bc2f0 b | b
Added 0 files, modified 1 files, removed 0 files 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) Created rlvkpnrz e25100af (empty) (no description set)
Rebased 1 descendant commits Rebased 1 descendant commits
New conflicts appeared in these 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: To resolve the conflicts, start by updating to it:
jj new kkmpptxzrspx jj new kkmpptxzrspx
Then use `jj resolve`, or edit the conflict markers in the file directly. 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`. 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. 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) Parent commit : rlvkpnrz e25100af (empty) (no description set)
Added 0 files, modified 1 files, removed 0 files 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"]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["restore", "file"]);
insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Created vruxwmqv b2c9c888 conflict | (conflict) (empty) conflict Created vruxwmqv 0817af7e conflict | (conflict) (empty) conflict
Working copy now at: vruxwmqv b2c9c888 conflict | (conflict) (empty) conflict Working copy now at: vruxwmqv 0817af7e conflict | (conflict) (empty) conflict
Parent commit : zsuskuln aa493daf a | a Parent commit : zsuskuln aa493daf a | a
Parent commit : royxmykx db6a4daf b | b Parent commit : royxmykx db6a4daf b | b
Added 0 files, modified 1 files, removed 0 files 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"]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["restore"]);
insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Created vruxwmqv 4fc10820 conflict | (conflict) (empty) conflict Created vruxwmqv da925083 conflict | (conflict) (empty) conflict
Working copy now at: vruxwmqv 4fc10820 conflict | (conflict) (empty) conflict Working copy now at: vruxwmqv da925083 conflict | (conflict) (empty) conflict
Parent commit : zsuskuln aa493daf a | a Parent commit : zsuskuln aa493daf a | a
Parent commit : royxmykx db6a4daf b | b Parent commit : royxmykx db6a4daf b | b
Added 0 files, modified 1 files, removed 0 files Added 0 files, modified 1 files, removed 0 files

View file

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

View file

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

View file

@ -25,7 +25,8 @@ use std::time::SystemTime;
use std::{fs, io, str}; use std::{fs, io, str};
use async_trait::async_trait; use async_trait::async_trait;
use gix::objs::{CommitRefIter, WriteTo}; use gix::bstr::BString;
use gix::objs::{CommitRef, CommitRefIter, WriteTo};
use itertools::Itertools; use itertools::Itertools;
use prost::Message; use prost::Message;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -54,6 +55,8 @@ const CHANGE_ID_LENGTH: usize = 16;
const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/"; const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/";
const CONFLICT_SUFFIX: &str = ".jjconflict"; const CONFLICT_SUFFIX: &str = ".jjconflict";
const JJ_TREES_COMMIT_HEADER: &[u8] = b"jj:trees";
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum GitBackendInitError { pub enum GitBackendInitError {
#[error("Failed to initialize git repository")] #[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) .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( fn commit_from_git_without_root_parent(
id: &CommitId, id: &CommitId,
git_object: &gix::Object, 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()); 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 // If this commit is a conflict, we'll update the root tree later, when we read
// the extra metadata. // the extra metadata.
let root_tree = if uses_tree_conflict_format { let root_tree = root_tree_from_header(&commit)
MergedTreeId::resolved(tree_id) .map_err(|()| to_read_object_err("Invalid jj:trees header", id))?;
} else { let root_tree = root_tree.unwrap_or_else(|| {
MergedTreeId::Legacy(tree_id) 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 // Use lossy conversion as commit message with "mojibake" is still better than
// nothing. // nothing.
// TODO: what should we do with commit.encoding? // TODO: what should we do with commit.encoding?
@ -571,7 +599,13 @@ fn deserialize_extras(commit: &mut Commit, bytes: &[u8]) {
.iter() .iter()
.map(|id_bytes| TreeId::from_bytes(id_bytes)) .map(|id_bytes| TreeId::from_bytes(id_bytes))
.collect(); .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 { } else {
// uses_tree_conflict_format was set but there was no root_tree override in the // 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 // 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)?); 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); let extras = serialize_extras(&contents);
// If two writers write commits of the same id with different metadata, they // If two writers write commits of the same id with different metadata, they
@ -1114,7 +1158,7 @@ impl Backend for GitBackend {
committer: committer.into(), committer: committer.into(),
encoding: None, encoding: None,
parents: parents.clone(), parents: parents.clone(),
extra_headers: Default::default(), extra_headers: extra_headers.clone(),
}; };
if let Some(sign) = &mut sign_with { if let Some(sign) = &mut sign_with {