patch-id: fix stable patch id for binary / header-only

Patch-ids for binary patches are found by hashing the object
ids of the before and after objects in succession. However in
the --stable case, there is a bug where hunks are not flushed
for binary and header-only patch ids, which would always result
in a patch-id of 0000. The --unstable case is currently correct.

Reorder the logic to branch into 3 cases for populating the
patch body: header-only which populates nothing, binary which
populates the object ids, and normal which populates the text
diff. All branches will end up flushing the hunk.

Don't populate the ---a/ and +++b/ lines for binary diffs, to correspond
to those lines not being present in the "git diff" text output.
This is necessary because we advertise that the patch-id calculated
internally and used in format-patch is the same that what the
builtin "git patch-id" would produce when piped from a diff.

Update the test to run on both binary and normal files.

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:39 +00:00
committed by Junio C Hamano
parent a0feb8611d
commit 0570be79ea
2 changed files with 53 additions and 39 deletions

58
diff.c
View File

@@ -6221,46 +6221,46 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
if (p->one->mode == 0) { if (p->one->mode == 0) {
patch_id_add_string(&ctx, "newfilemode"); patch_id_add_string(&ctx, "newfilemode");
patch_id_add_mode(&ctx, p->two->mode); patch_id_add_mode(&ctx, p->two->mode);
patch_id_add_string(&ctx, "---/dev/null");
patch_id_add_string(&ctx, "+++b/");
the_hash_algo->update_fn(&ctx, p->two->path, len2);
} else if (p->two->mode == 0) { } else if (p->two->mode == 0) {
patch_id_add_string(&ctx, "deletedfilemode"); patch_id_add_string(&ctx, "deletedfilemode");
patch_id_add_mode(&ctx, p->one->mode); patch_id_add_mode(&ctx, p->one->mode);
patch_id_add_string(&ctx, "---a/");
the_hash_algo->update_fn(&ctx, p->one->path, len1);
patch_id_add_string(&ctx, "+++/dev/null");
} else {
patch_id_add_string(&ctx, "---a/");
the_hash_algo->update_fn(&ctx, p->one->path, len1);
patch_id_add_string(&ctx, "+++b/");
the_hash_algo->update_fn(&ctx, p->two->path, len2);
} }
if (diff_header_only) if (diff_header_only) {
continue; /* don't do anything since we're only populating header info */
} else if (diff_filespec_is_binary(options->repo, p->one) ||
if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
fill_mmfile(options->repo, &mf2, p->two) < 0)
return error("unable to read files to diff");
if (diff_filespec_is_binary(options->repo, p->one) ||
diff_filespec_is_binary(options->repo, p->two)) { diff_filespec_is_binary(options->repo, p->two)) {
the_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid), the_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid),
the_hash_algo->hexsz); the_hash_algo->hexsz);
the_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid), the_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid),
the_hash_algo->hexsz); the_hash_algo->hexsz);
continue; } else {
if (p->one->mode == 0) {
patch_id_add_string(&ctx, "---/dev/null");
patch_id_add_string(&ctx, "+++b/");
the_hash_algo->update_fn(&ctx, p->two->path, len2);
} else if (p->two->mode == 0) {
patch_id_add_string(&ctx, "---a/");
the_hash_algo->update_fn(&ctx, p->one->path, len1);
patch_id_add_string(&ctx, "+++/dev/null");
} else {
patch_id_add_string(&ctx, "---a/");
the_hash_algo->update_fn(&ctx, p->one->path, len1);
patch_id_add_string(&ctx, "+++b/");
the_hash_algo->update_fn(&ctx, p->two->path, len2);
}
if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
fill_mmfile(options->repo, &mf2, p->two) < 0)
return error("unable to read files to diff");
xpp.flags = 0;
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
if (xdi_diff_outf(&mf1, &mf2, NULL,
patch_id_consume, &data, &xpp, &xecfg))
return error("unable to generate patch-id diff for %s",
p->one->path);
} }
xpp.flags = 0;
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
if (xdi_diff_outf(&mf1, &mf2, NULL,
patch_id_consume, &data, &xpp, &xecfg))
return error("unable to generate patch-id diff for %s",
p->one->path);
if (stable) if (stable)
flush_one_hunk(oid, &ctx); flush_one_hunk(oid, &ctx);
} }

View File

@@ -43,15 +43,16 @@ test_expect_success 'setup: 500 lines' '
git add newfile && git add newfile &&
git commit -q -m "add small file" && git commit -q -m "add small file" &&
git cherry-pick main >/dev/null 2>&1 git cherry-pick main >/dev/null 2>&1 &&
'
test_expect_success 'setup attributes' ' git branch -f squashed main &&
echo "file binary" >.gitattributes git checkout -q -f squashed &&
git reset -q --soft HEAD~2 &&
git commit -q -m squashed
' '
test_expect_success 'detect upstream patch' ' test_expect_success 'detect upstream patch' '
git checkout -q main && git checkout -q main^{} &&
scramble file && scramble file &&
git add file && git add file &&
git commit -q -m "change big file again" && git commit -q -m "change big file again" &&
@@ -61,14 +62,27 @@ test_expect_success 'detect upstream patch' '
test_must_be_empty revs test_must_be_empty revs
' '
test_expect_success 'detect upstream patch binary' '
echo "file binary" >.gitattributes &&
git checkout -q other^{} &&
git rebase main &&
git rev-list main...HEAD~ >revs &&
test_must_be_empty revs &&
test_when_finished "rm .gitattributes"
'
test_expect_success 'do not drop patch' ' test_expect_success 'do not drop patch' '
git branch -f squashed main &&
git checkout -q -f squashed &&
git reset -q --soft HEAD~2 &&
git commit -q -m squashed &&
git checkout -q other^{} && git checkout -q other^{} &&
test_must_fail git rebase squashed && test_must_fail git rebase squashed &&
git rebase --quit test_when_finished "git rebase --abort"
'
test_expect_success 'do not drop patch binary' '
echo "file binary" >.gitattributes &&
git checkout -q other^{} &&
test_must_fail git rebase squashed &&
test_when_finished "git rebase --abort" &&
test_when_finished "rm .gitattributes"
' '
test_done test_done