patch-id: fix patch-id for mode changes

Currently patch-id as used in rebase and cherry-pick does not account
for file modes if the file is modified. One consequence of this is
that if you have a local patch that changes modes, but upstream
has applied an outdated version of the patch that doesn't include
that mode change, "git rebase" will drop your local version of the
patch along with your mode changes. It also means that internal
patch-id doesn't produce the same output as the builtin, which does
account for mode changes due to them being part of diff output.

Fix by adding mode to the patch-id if it has changed, in the same
format that would be produced by diff, so that it is compatible
with builtin patch-id.

Signed-off-by: Jerry Zhang <Jerry@skydio.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jerry Zhang 2022-10-24 20:07:42 +00:00 committed by Junio C Hamano
parent 0df19eb9d9
commit 93105aba6c
2 changed files with 35 additions and 1 deletions

5
diff.c
View File

@ -6224,6 +6224,11 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
} else if (p->two->mode == 0) {
patch_id_add_string(&ctx, "deletedfilemode");
patch_id_add_mode(&ctx, p->one->mode);
} else if (p->one->mode != p->two->mode) {
patch_id_add_string(&ctx, "oldmode");
patch_id_add_mode(&ctx, p->one->mode);
patch_id_add_string(&ctx, "newmode");
patch_id_add_mode(&ctx, p->two->mode);
}
if (diff_header_only) {

View File

@ -48,7 +48,17 @@ test_expect_success 'setup: 500 lines' '
git branch -f squashed main &&
git checkout -q -f squashed &&
git reset -q --soft HEAD~2 &&
git commit -q -m squashed
git commit -q -m squashed &&
git branch -f mode main &&
git checkout -q -f mode &&
test_chmod +x file &&
git commit -q -a --amend &&
git branch -f modeother other &&
git checkout -q -f modeother &&
test_chmod +x file &&
git commit -q -a --amend
'
test_expect_success 'detect upstream patch' '
@ -71,6 +81,13 @@ test_expect_success 'detect upstream patch binary' '
test_when_finished "rm .gitattributes"
'
test_expect_success 'detect upstream patch modechange' '
git checkout -q modeother^{} &&
git rebase mode &&
git rev-list mode...HEAD~ >revs &&
test_must_be_empty revs
'
test_expect_success 'do not drop patch' '
git checkout -q other^{} &&
test_must_fail git rebase squashed &&
@ -85,4 +102,16 @@ test_expect_success 'do not drop patch binary' '
test_when_finished "rm .gitattributes"
'
test_expect_success 'do not drop patch modechange' '
git checkout -q modeother^{} &&
git rebase other &&
cat >expected <<-\EOF &&
diff --git a/file b/file
old mode 100644
new mode 100755
EOF
git diff HEAD~ >modediff &&
test_cmp expected modediff
'
test_done