From fcb3d0adc13b398c930814a5e0c886717acc1b70 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 25 Jun 2006 03:51:08 +0200 Subject: add diff_flush_patch_id() to calculate the patch id Call it like this: unsigned char id[20]; if (diff_flush_patch_id(diff_options, id)) printf("And the patch id is: %s\n", sha1_to_hex(id)); Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff.h | 2 + 2 files changed, 141 insertions(+) diff --git a/diff.c b/diff.c index fbb6c26cd9..afb629e613 100644 --- a/diff.c +++ b/diff.c @@ -2104,6 +2104,145 @@ static void diff_summary(struct diff_filepair *p) } } +struct patch_id_t { + struct xdiff_emit_state xm; + SHA_CTX *ctx; + int patchlen; +}; + +static int remove_space(char *line, int len) +{ + int i; + char *dst = line; + unsigned char c; + + for (i = 0; i < len; i++) + if (!isspace((c = line[i]))) + *dst++ = c; + + return dst - line; +} + +static void patch_id_consume(void *priv, char *line, unsigned long len) +{ + struct patch_id_t *data = priv; + int new_len; + + /* Ignore line numbers when computing the SHA1 of the patch */ + if (!strncmp(line, "@@ -", 4)) + return; + + new_len = remove_space(line, len); + + SHA1_Update(data->ctx, line, new_len); + data->patchlen += new_len; +} + +/* returns 0 upon success, and writes result into sha1 */ +static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) +{ + struct diff_queue_struct *q = &diff_queued_diff; + int i; + SHA_CTX ctx; + struct patch_id_t data; + char buffer[PATH_MAX * 4 + 20]; + + SHA1_Init(&ctx); + memset(&data, 0, sizeof(struct patch_id_t)); + data.ctx = &ctx; + data.xm.consume = patch_id_consume; + + for (i = 0; i < q->nr; i++) { + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + mmfile_t mf1, mf2; + struct diff_filepair *p = q->queue[i]; + int len1, len2; + + if (p->status == 0) + return error("internal diff status error"); + if (p->status == DIFF_STATUS_UNKNOWN) + continue; + if (diff_unmodified_pair(p)) + continue; + if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || + (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) + continue; + if (DIFF_PAIR_UNMERGED(p)) + continue; + + diff_fill_sha1_info(p->one); + diff_fill_sha1_info(p->two); + if (fill_mmfile(&mf1, p->one) < 0 || + fill_mmfile(&mf2, p->two) < 0) + return error("unable to read files to diff"); + + /* Maybe hash p->two? into the patch id? */ + if (mmfile_is_binary(&mf2)) + continue; + + len1 = remove_space(p->one->path, strlen(p->one->path)); + len2 = remove_space(p->two->path, strlen(p->two->path)); + if (p->one->mode == 0) + len1 = snprintf(buffer, sizeof(buffer), + "diff--gita/%.*sb/%.*s" + "newfilemode%06o" + "---/dev/null" + "+++b/%.*s", + len1, p->one->path, + len2, p->two->path, + p->two->mode, + len2, p->two->path); + else if (p->two->mode == 0) + len1 = snprintf(buffer, sizeof(buffer), + "diff--gita/%.*sb/%.*s" + "deletedfilemode%06o" + "---a/%.*s" + "+++/dev/null", + len1, p->one->path, + len2, p->two->path, + p->one->mode, + len1, p->one->path); + else + len1 = snprintf(buffer, sizeof(buffer), + "diff--gita/%.*sb/%.*s" + "---a/%.*s" + "+++b/%.*s", + len1, p->one->path, + len2, p->two->path, + len1, p->one->path, + len2, p->two->path); + SHA1_Update(&ctx, buffer, len1); + + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 3; + xecfg.flags = 3; + ecb.outf = xdiff_outf; + ecb.priv = &data; + xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + } + + SHA1_Final(sha1, &ctx); + return 0; +} + +int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1) +{ + struct diff_queue_struct *q = &diff_queued_diff; + int i; + int result = diff_get_patch_id(options, sha1); + + for (i = 0; i < q->nr; i++) + diff_free_filepair(q->queue[i]); + + free(q->queue); + q->queue = NULL; + q->nr = q->alloc = 0; + + return result; +} + void diff_flush(struct diff_options *options) { struct diff_queue_struct *q = &diff_queued_diff; diff --git a/diff.h b/diff.h index b61fdc8f1e..d5068af7d1 100644 --- a/diff.h +++ b/diff.h @@ -184,4 +184,6 @@ extern int run_diff_files(struct rev_info *revs, int silent_on_removed); extern int run_diff_index(struct rev_info *revs, int cached); +extern int diff_flush_patch_id(struct diff_options *, unsigned char *); + #endif /* DIFF_H */ -- cgit v1.2.3 From 9c6efa366eed0526e82a052b77227451273af0c3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 25 Jun 2006 03:52:01 +0200 Subject: format-patch: introduce "--ignore-if-in-upstream" With this flag, format-patch will try very hard not to output patches which are already in the upstream branch. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/builtin-log.c b/builtin-log.c index 44d2d136f5..4ee5891dfd 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -160,6 +160,71 @@ static void reopen_stdout(struct commit *commit, int nr, int keep_subject) freopen(filename, "w", stdout); } +static void reset_all_objects_flags() +{ + int i; + + for (i = 0; i < obj_allocs; i++) + if (objs[i]) + objs[i]->flags = 0; +} + +static int get_patch_id(struct commit *commit, struct diff_options *options, + unsigned char *sha1) +{ + diff_tree_sha1(commit->parents->item->object.sha1, commit->object.sha1, + "", options); + diffcore_std(options); + return diff_flush_patch_id(options, sha1); +} + +static void get_patch_ids(struct rev_info *rev, struct diff_options *options) +{ + struct rev_info check_rev; + struct commit *commit; + struct object *o1, *o2; + unsigned flags1, flags2; + unsigned char sha1[20]; + + if (rev->pending.nr != 2) + die("Need exactly one range."); + + o1 = rev->pending.objects[0].item; + flags1 = o1->flags; + o2 = rev->pending.objects[1].item; + flags2 = o2->flags; + + if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) + die("Not a range."); + + diff_setup(options); + options->recursive = 1; + if (diff_setup_done(options) < 0) + die("diff_setup_done failed"); + + /* given a range a..b get all patch ids for b..a */ + init_revisions(&check_rev); + o1->flags ^= UNINTERESTING; + o2->flags ^= UNINTERESTING; + add_pending_object(&check_rev, o1, "o1"); + add_pending_object(&check_rev, o2, "o2"); + prepare_revision_walk(&check_rev); + + while ((commit = get_revision(&check_rev)) != NULL) { + /* ignore merges */ + if (commit->parents && commit->parents->next) + continue; + + if (!get_patch_id(commit, options, sha1)) + created_object(sha1, xcalloc(1, sizeof(struct object))); + } + + /* reset for next revision walk */ + reset_all_objects_flags(); + o1->flags = flags1; + o2->flags = flags2; +} + int cmd_format_patch(int argc, const char **argv, char **envp) { struct commit *commit; @@ -170,6 +235,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp) int numbered = 0; int start_number = -1; int keep_subject = 0; + int ignore_if_in_upstream = 0; + struct diff_options patch_id_opts; char *add_signoff = NULL; init_revisions(&rev); @@ -235,6 +302,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp) rev.mime_boundary = git_version_string; else if (!strncmp(argv[i], "--attach=", 9)) rev.mime_boundary = argv[i] + 9; + else if (!strcmp(argv[i], "--ignore-if-in-upstream")) + ignore_if_in_upstream = 1; else argv[j++] = argv[i]; } @@ -262,14 +331,25 @@ int cmd_format_patch(int argc, const char **argv, char **envp) add_head(&rev); } + if (ignore_if_in_upstream) + get_patch_ids(&rev, &patch_id_opts); + if (!use_stdout) realstdout = fdopen(dup(1), "w"); prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { + unsigned char sha1[20]; + /* ignore merges */ if (commit->parents && commit->parents->next) continue; + + if (ignore_if_in_upstream && + !get_patch_id(commit, &patch_id_opts, sha1) && + lookup_object(sha1)) + continue; + nr++; list = realloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; -- cgit v1.2.3 From ece3c67f9c8f0074cae76204a648cbfc6075bb44 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 Jun 2006 15:40:09 -0700 Subject: t4014: add format-patch --ignore-if-in-upstream test Signed-off-by: Junio C Hamano --- t/t4014-format-patch.sh | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 t/t4014-format-patch.sh diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh new file mode 100755 index 0000000000..c044044f80 --- /dev/null +++ b/t/t4014-format-patch.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Copyright (c) 2006 Junio C Hamano +# + +test_description='Format-patch skipping already incorporated patches' + +. ./test-lib.sh + +test_expect_success setup ' + + for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file && + git add file && + git commit -m Initial && + git checkout -b side && + + for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file && + git update-index file && + git commit -m "Side change #1" && + + for i in D E F; do echo "$i"; done >>file && + git update-index file && + git commit -m "Side change #2" && + git tag C1 && + + for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file && + git update-index file && + git commit -m "Side change #3" && + + git checkout master && + git diff-tree -p C1 | git apply --index && + git commit -m "Master accepts moral equivalent of #1" + +' + +test_expect_success "format-patch --ignore-if-in-upstream" ' + + git format-patch --stdout master..side >patch0 && + cnt=`grep "^From " patch0 | wc -l` && + test "$cnt" = 3 + +' + +test_expect_success "format-patch --ignore-if-in-upstream" ' + + git format-patch --stdout \ + --ignore-if-in-upstream master..side >patch1 && + cnt=`grep "^From " patch1 | wc -l` && + test "$cnt" = 2 + +' + +test_expect_success "format-patch result applies" ' + + git checkout -b rebuild-0 master && + git am -3 patch0 && + cnt=`git rev-list master.. | wc -l` && + test "$cnt" = 2 +' + +test_expect_success "format-patch --ignore-if-in-upstream result applies" ' + + git checkout -b rebuild-1 master && + git am -3 patch1 && + cnt=`git rev-list master.. | wc -l` && + test "$cnt" = 2 +' + +test_done -- cgit v1.2.3 From 8780bd8fd2fb9a85c39de8d7d3105b94c6a9cf40 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 27 Jun 2006 10:12:12 +0200 Subject: t4014: fix for whitespace from "wc -l" Some "wc" insist on putting a TAB in front of the number. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t4014-format-patch.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index c044044f80..ac2fde773d 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -37,7 +37,7 @@ test_expect_success "format-patch --ignore-if-in-upstream" ' git format-patch --stdout master..side >patch0 && cnt=`grep "^From " patch0 | wc -l` && - test "$cnt" = 3 + test $cnt = 3 ' @@ -46,7 +46,7 @@ test_expect_success "format-patch --ignore-if-in-upstream" ' git format-patch --stdout \ --ignore-if-in-upstream master..side >patch1 && cnt=`grep "^From " patch1 | wc -l` && - test "$cnt" = 2 + test $cnt = 2 ' @@ -55,7 +55,7 @@ test_expect_success "format-patch result applies" ' git checkout -b rebuild-0 master && git am -3 patch0 && cnt=`git rev-list master.. | wc -l` && - test "$cnt" = 2 + test $cnt = 2 ' test_expect_success "format-patch --ignore-if-in-upstream result applies" ' @@ -63,7 +63,7 @@ test_expect_success "format-patch --ignore-if-in-upstream result applies" ' git checkout -b rebuild-1 master && git am -3 patch1 && cnt=`git rev-list master.. | wc -l` && - test "$cnt" = 2 + test $cnt = 2 ' test_done -- cgit v1.2.3 From 81db094107df9a73a0be4538a74a26115a84dd13 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 27 Jun 2006 22:38:04 +0200 Subject: format-patch: use clear_commit_marks() instead of some ad-hockery It is cleaner, and it describes better what the idea behind the code is. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 4ee5891dfd..f9515a8a4a 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -160,15 +160,6 @@ static void reopen_stdout(struct commit *commit, int nr, int keep_subject) freopen(filename, "w", stdout); } -static void reset_all_objects_flags() -{ - int i; - - for (i = 0; i < obj_allocs; i++) - if (objs[i]) - objs[i]->flags = 0; -} - static int get_patch_id(struct commit *commit, struct diff_options *options, unsigned char *sha1) { @@ -220,7 +211,10 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options) } /* reset for next revision walk */ - reset_all_objects_flags(); + clear_commit_marks((struct commit *)o1, + SEEN | UNINTERESTING | SHOWN | ADDED); + clear_commit_marks((struct commit *)o2, + SEEN | UNINTERESTING | SHOWN | ADDED); o1->flags = flags1; o2->flags = flags2; } -- cgit v1.2.3 From 982b64e4ccc993e51a7e7a1e1713953ecf95e4f2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 Jun 2006 22:48:34 -0700 Subject: t4014: fix test commit labels. The commit tag and commit comments used in the test claimed that the #1 commit was merged upstream where the test actually let the upstream merge #2 commit. Fix them. Signed-off-by: Junio C Hamano --- t/t4014-format-patch.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index ac2fde773d..4795872a77 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -21,15 +21,15 @@ test_expect_success setup ' for i in D E F; do echo "$i"; done >>file && git update-index file && git commit -m "Side change #2" && - git tag C1 && + git tag C2 && for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file && git update-index file && git commit -m "Side change #3" && git checkout master && - git diff-tree -p C1 | git apply --index && - git commit -m "Master accepts moral equivalent of #1" + git diff-tree -p C2 | git apply --index && + git commit -m "Master accepts moral equivalent of #2" ' -- cgit v1.2.3 From 9fdc3bb5c20e37e410e03ed5b6c71a7e647ccee8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 Jun 2006 22:49:42 -0700 Subject: diff.c: fix get_patch_id() The function internally generated diff to get the patch id but passed a wrong emit flags to the xdiff layer when it did so. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.c b/diff.c index afb629e613..5a71489a47 100644 --- a/diff.c +++ b/diff.c @@ -2217,7 +2217,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = 3; - xecfg.flags = 3; + xecfg.flags = XDL_EMIT_FUNCNAMES; ecb.outf = xdiff_outf; ecb.priv = &data; xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); -- cgit v1.2.3