Skip to content

Commit fb439c9

Browse files
authored
Merge pull request libgit2#5306 from herrerog/patchid
diff: complete support for git patchid
2 parents 61176a9 + ece5bb5 commit fb439c9

File tree

9 files changed

+130
-74
lines changed

9 files changed

+130
-74
lines changed

include/git2/diff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,7 @@ typedef enum {
10931093
GIT_DIFF_FORMAT_RAW = 3u, /**< like git diff --raw */
10941094
GIT_DIFF_FORMAT_NAME_ONLY = 4u, /**< like git diff --name-only */
10951095
GIT_DIFF_FORMAT_NAME_STATUS = 5u, /**< like git diff --name-status */
1096+
GIT_DIFF_FORMAT_PATCH_ID = 6u, /**< git diff as used by git patch-id */
10961097
} git_diff_format_t;
10971098

10981099
/**

src/diff.c

Lines changed: 21 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -426,81 +426,38 @@ static void strip_spaces(git_buf *buf)
426426
git_buf_truncate(buf, len);
427427
}
428428

429-
static int file_cb(
429+
int git_diff_patchid_print_callback__to_buf(
430430
const git_diff_delta *delta,
431-
float progress,
431+
const git_diff_hunk *hunk,
432+
const git_diff_line *line,
432433
void *payload)
433434
{
434435
struct patch_id_args *args = (struct patch_id_args *) payload;
435436
git_buf buf = GIT_BUF_INIT;
436-
int error;
437-
438-
GIT_UNUSED(progress);
437+
int error = 0;
439438

440-
if (!args->first_file &&
441-
(error = flush_hunk(&args->result, &args->ctx)) < 0)
442-
goto out;
443-
args->first_file = 0;
444-
445-
if ((error = git_buf_printf(&buf,
446-
"diff--gita/%sb/%s---a/%s+++b/%s",
447-
delta->old_file.path,
448-
delta->new_file.path,
449-
delta->old_file.path,
450-
delta->new_file.path)) < 0)
439+
if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL ||
440+
line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
441+
line->origin == GIT_DIFF_LINE_DEL_EOFNL)
451442
goto out;
452443

453-
strip_spaces(&buf);
454-
455-
if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
444+
if ((error = git_diff_print_callback__to_buf(delta, hunk,
445+
line, &buf)) < 0)
456446
goto out;
457447

458-
out:
459-
git_buf_dispose(&buf);
460-
return error;
461-
}
462-
463-
static int patchid_line_cb(
464-
const git_diff_delta *delta,
465-
const git_diff_hunk *hunk,
466-
const git_diff_line *line,
467-
void *payload)
468-
{
469-
struct patch_id_args *args = (struct patch_id_args *) payload;
470-
git_buf buf = GIT_BUF_INIT;
471-
int error;
472-
473-
GIT_UNUSED(delta);
474-
GIT_UNUSED(hunk);
475-
476-
switch (line->origin) {
477-
case GIT_DIFF_LINE_ADDITION:
478-
git_buf_putc(&buf, '+');
479-
break;
480-
case GIT_DIFF_LINE_DELETION:
481-
git_buf_putc(&buf, '-');
482-
break;
483-
case GIT_DIFF_LINE_CONTEXT:
484-
break;
485-
case GIT_DIFF_LINE_CONTEXT_EOFNL:
486-
case GIT_DIFF_LINE_ADD_EOFNL:
487-
case GIT_DIFF_LINE_DEL_EOFNL:
488-
/*
489-
* Ignore EOF without newlines for patch IDs as whitespace is
490-
* not supposed to be significant.
491-
*/
492-
return 0;
493-
default:
494-
git_error_set(GIT_ERROR_PATCH, "invalid line origin for patch");
495-
return -1;
496-
}
497-
498-
git_buf_put(&buf, line->content, line->content_len);
499448
strip_spaces(&buf);
500449

450+
if (line->origin == GIT_DIFF_LINE_FILE_HDR &&
451+
!args->first_file &&
452+
(error = flush_hunk(&args->result, &args->ctx) < 0))
453+
goto out;
454+
501455
if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
502456
goto out;
503457

458+
if (line->origin == GIT_DIFF_LINE_FILE_HDR && args->first_file)
459+
args->first_file = 0;
460+
504461
out:
505462
git_buf_dispose(&buf);
506463
return error;
@@ -526,7 +483,10 @@ int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opt
526483
if ((error = git_hash_ctx_init(&args.ctx)) < 0)
527484
goto out;
528485

529-
if ((error = git_diff_foreach(diff, file_cb, NULL, NULL, patchid_line_cb, &args)) < 0)
486+
if ((error = git_diff_print(diff,
487+
GIT_DIFF_FORMAT_PATCH_ID,
488+
git_diff_patchid_print_callback__to_buf,
489+
&args)) < 0)
530490
goto out;
531491

532492
if ((error = (flush_hunk(&args.result, &args.ctx))) < 0)

src/diff.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ extern int git_diff_delta__format_file_header(
5757
const git_diff_delta *delta,
5858
const char *oldpfx,
5959
const char *newpfx,
60-
int oid_strlen);
60+
int oid_strlen,
61+
bool print_index);
6162

6263
extern int git_diff_delta__cmp(const void *a, const void *b);
6364
extern int git_diff_delta__casecmp(const void *a, const void *b);

src/diff_print.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ static int diff_print_modes(
269269
}
270270

271271
static int diff_print_oid_range(
272-
git_buf *out, const git_diff_delta *delta, int id_strlen)
272+
git_buf *out, const git_diff_delta *delta, int id_strlen,
273+
bool print_index)
273274
{
274275
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
275276

@@ -293,8 +294,9 @@ static int diff_print_oid_range(
293294
git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id);
294295

295296
if (delta->old_file.mode == delta->new_file.mode) {
296-
git_buf_printf(out, "index %s..%s %o\n",
297-
start_oid, end_oid, delta->old_file.mode);
297+
if (print_index)
298+
git_buf_printf(out, "index %s..%s %o\n",
299+
start_oid, end_oid, delta->old_file.mode);
298300
} else {
299301
if (delta->old_file.mode == 0)
300302
git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
@@ -303,7 +305,8 @@ static int diff_print_oid_range(
303305
else
304306
diff_print_modes(out, delta);
305307

306-
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
308+
if (print_index)
309+
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
307310
}
308311

309312
return git_buf_oom(out) ? -1 : 0;
@@ -400,7 +403,8 @@ int git_diff_delta__format_file_header(
400403
const git_diff_delta *delta,
401404
const char *oldpfx,
402405
const char *newpfx,
403-
int id_strlen)
406+
int id_strlen,
407+
bool print_index)
404408
{
405409
git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
406410
bool unchanged = delta_is_unchanged(delta);
@@ -431,7 +435,8 @@ int git_diff_delta__format_file_header(
431435
}
432436

433437
if (!unchanged) {
434-
if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0)
438+
if ((error = diff_print_oid_range(out, delta,
439+
id_strlen, print_index)) < 0)
435440
goto done;
436441

437442
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
@@ -566,6 +571,7 @@ static int diff_print_patch_file(
566571
(pi->flags & GIT_DIFF_FORCE_BINARY);
567572
bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
568573
int id_strlen = pi->id_strlen;
574+
bool print_index = (pi->format != GIT_DIFF_FORMAT_PATCH_ID);
569575

570576
if (binary && show_binary)
571577
id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev :
@@ -582,7 +588,8 @@ static int diff_print_patch_file(
582588
return 0;
583589

584590
if ((error = git_diff_delta__format_file_header(
585-
pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0)
591+
pi->buf, delta, oldpfx, newpfx,
592+
id_strlen, print_index)) < 0)
586593
return error;
587594

588595
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
@@ -670,6 +677,11 @@ int git_diff_print(
670677
print_hunk = diff_print_patch_hunk;
671678
print_line = diff_print_patch_line;
672679
break;
680+
case GIT_DIFF_FORMAT_PATCH_ID:
681+
print_file = diff_print_patch_file;
682+
print_binary = diff_print_patch_binary;
683+
print_line = diff_print_patch_line;
684+
break;
673685
case GIT_DIFF_FORMAT_PATCH_HEADER:
674686
print_file = diff_print_patch_file;
675687
break;

src/patch.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ size_t git_patch_size(
7979
git_buf file_header = GIT_BUF_INIT;
8080

8181
if (git_diff_delta__format_file_header(
82-
&file_header, patch->delta, NULL, NULL, 0) < 0)
82+
&file_header, patch->delta, NULL, NULL, 0, true) < 0)
8383
git_error_clear();
8484
else
8585
out += git_buf_len(&file_header);

src/patch_parse.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ static int parse_header_git_deletedfilemode(
231231
git_patch_parsed *patch,
232232
git_patch_parse_ctx *ctx)
233233
{
234-
git__free((char *)patch->base.delta->old_file.path);
234+
git__free((char *)patch->base.delta->new_file.path);
235235

236-
patch->base.delta->old_file.path = NULL;
236+
patch->base.delta->new_file.path = NULL;
237237
patch->base.delta->status = GIT_DELTA_DELETED;
238238
patch->base.delta->nfiles = 1;
239239

@@ -244,9 +244,9 @@ static int parse_header_git_newfilemode(
244244
git_patch_parsed *patch,
245245
git_patch_parse_ctx *ctx)
246246
{
247-
git__free((char *)patch->base.delta->new_file.path);
247+
git__free((char *)patch->base.delta->old_file.path);
248248

249-
patch->base.delta->new_file.path = NULL;
249+
patch->base.delta->old_file.path = NULL;
250250
patch->base.delta->status = GIT_DELTA_ADDED;
251251
patch->base.delta->nfiles = 1;
252252

@@ -884,6 +884,11 @@ static int parse_patch_binary_nodata(
884884
if (!old || !new)
885885
return git_parse_err("corrupt binary data without paths at line %"PRIuZ, ctx->parse_ctx.line_num);
886886

887+
if (patch->base.delta->status == GIT_DELTA_ADDED)
888+
old = "/dev/null";
889+
else if (patch->base.delta->status == GIT_DELTA_DELETED)
890+
new = "/dev/null";
891+
887892
if (git_parse_advance_expected_str(&ctx->parse_ctx, "Binary files ") < 0 ||
888893
git_parse_advance_expected_str(&ctx->parse_ctx, old) < 0 ||
889894
git_parse_advance_expected_str(&ctx->parse_ctx, " and ") < 0 ||

tests/diff/patchid.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,39 @@ void test_diff_patchid__simple_commit(void)
2020
verify_patch_id(PATCH_SIMPLE_COMMIT, "06094b1948b878b7d9ff7560b4eae672a014b0ec");
2121
}
2222

23+
void test_diff_patchid__deleted_file(void)
24+
{
25+
verify_patch_id(PATCH_DELETE_ORIGINAL, "d18507fe189f49c028b32c8c34e1ad98dd6a1aad");
26+
verify_patch_id(PATCH_DELETED_FILE_2_HUNKS, "f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7");
27+
}
28+
29+
void test_diff_patchid__created_file(void)
30+
{
31+
verify_patch_id(PATCH_ADD_ORIGINAL, "a7d39379308021465ae2ce65e338c048a3110db6");
32+
}
33+
34+
void test_diff_patchid__binary_file(void)
35+
{
36+
verify_patch_id(PATCH_ADD_BINARY_NOT_PRINTED, "2b31236b485faa30cf4dd33e4d6539829996739f");
37+
}
38+
39+
void test_diff_patchid__renamed_file(void)
40+
{
41+
verify_patch_id(PATCH_RENAME_EXACT, "4666d50cea4976f6f727448046d43461912058fd");
42+
verify_patch_id(PATCH_RENAME_SIMILAR, "a795087575fcb940227be524488bedd6b3d3f438");
43+
}
44+
45+
void test_diff_patchid__modechange(void)
46+
{
47+
verify_patch_id(PATCH_MODECHANGE_UNCHANGED, "dbf3423ee98375ef1c72a79fbd29a049a2bae771");
48+
verify_patch_id(PATCH_MODECHANGE_MODIFIED, "93aba696e1bbd2bbb73e3e3e62ed71f232137657");
49+
}
50+
51+
void test_diff_patchid__shuffle_hunks(void)
52+
{
53+
verify_patch_id(PATCH_DELETED_FILE_2_HUNKS_SHUFFLED, "f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7");
54+
}
55+
2356
void test_diff_patchid__filename_with_spaces(void)
2457
{
2558
verify_patch_id(PATCH_APPEND_NO_NL, "f0ba05413beaef743b630e796153839462ee477a");

tests/patch/patch_common.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,38 @@
385385
"@@ -9,0 +10 @@ below it!\n" \
386386
"+insert at end\n"
387387

388+
#define PATCH_DELETED_FILE_2_HUNKS \
389+
"diff --git a/a b/a\n" \
390+
"index 7f129fd..af431f2 100644\n" \
391+
"--- a/a\n" \
392+
"+++ b/a\n" \
393+
"@@ -1 +1 @@\n" \
394+
"-a contents 2\n" \
395+
"+a contents\n" \
396+
"diff --git a/c/d b/c/d\n" \
397+
"deleted file mode 100644\n" \
398+
"index 297efb8..0000000\n" \
399+
"--- a/c/d\n" \
400+
"+++ /dev/null\n" \
401+
"@@ -1 +0,0 @@\n" \
402+
"-c/d contents\n"
403+
404+
#define PATCH_DELETED_FILE_2_HUNKS_SHUFFLED \
405+
"diff --git a/c/d b/c/d\n" \
406+
"deleted file mode 100644\n" \
407+
"index 297efb8..0000000\n" \
408+
"--- a/c/d\n" \
409+
"+++ /dev/null\n" \
410+
"@@ -1 +0,0 @@\n" \
411+
"-c/d contents\n" \
412+
"diff --git a/a b/a\n" \
413+
"index 7f129fd..af431f2 100644\n" \
414+
"--- a/a\n" \
415+
"+++ b/a\n" \
416+
"@@ -1 +1 @@\n" \
417+
"-a contents 2\n" \
418+
"+a contents\n"
419+
388420
#define PATCH_SIMPLE_COMMIT \
389421
"commit 15e119375018fba121cf58e02a9f17fe22df0df8\n" \
390422
"Author: Edward Thomson <ethomson@edwardthomson.com>\n" \
@@ -878,6 +910,12 @@
878910
"index 27184d9..7c94f9e 100644\n" \
879911
"Binary files a/binary.bin and b/binary.bin differ\n"
880912

913+
#define PATCH_ADD_BINARY_NOT_PRINTED \
914+
"diff --git a/test.bin b/test.bin\n" \
915+
"new file mode 100644\n" \
916+
"index 0000000..9e0f96a\n" \
917+
"Binary files /dev/null and b/test.bin differ\n"
918+
881919
#define PATCH_ORIGINAL_NEW_FILE_WITH_SPACE \
882920
"diff --git a/sp ace.txt b/sp ace.txt\n" \
883921
"new file mode 100644\n" \

tests/patch/print.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,9 @@ void test_patch_print__binary_not_shown(void)
172172
patch_print_from_patchfile(PATCH_BINARY_NOT_PRINTED,
173173
strlen(PATCH_BINARY_NOT_PRINTED));
174174
}
175+
176+
void test_patch_print__binary_add_not_shown(void)
177+
{
178+
patch_print_from_patchfile(PATCH_ADD_BINARY_NOT_PRINTED,
179+
strlen(PATCH_ADD_BINARY_NOT_PRINTED));
180+
}

0 commit comments

Comments
 (0)