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)); This patch also adds a switch "--with-patch-id" to the diff family, to print out the patch id before each patch. Signed-off-by: Johannes Schindelin <Johannes.Schindelin@xxxxxx> --- > > > - add a DIFF_FORMAT_PATCH_ID > > > > Please don't add any DIFF_FORMAT_*. I'm cleaning the diff output code > > and replacing diff_options.output_format with one-bit flags. > > Okay. For the purposes of git-format-patch, this is not needed > anyway, but rather a function which takes two tree objects and > returns the patch id. When you are finished it should be easy to > add this as a display format. Timo, I am prepared to redo this patch when you finished the cleanup. diff.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff.h | 3 + 2 files changed, 132 insertions(+), 0 deletions(-) diff --git a/diff.c b/diff.c index 5b34f73..1140d54 100644 --- a/diff.c +++ b/diff.c @@ -1460,6 +1460,8 @@ int diff_opt_parse(struct diff_options * options->output_format = DIFF_FORMAT_PATCH; options->with_stat = 1; } + else if (!strcmp(arg, "--with-patch-id")) + options->with_patch_id = 1; else if (!strcmp(arg, "-z")) options->line_termination = 0; else if (!strncmp(arg, "-l", 2)) @@ -2027,12 +2029,139 @@ static void diff_summary(struct diff_fil } } +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 */ +int diff_flush_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; +} + void diff_flush(struct diff_options *options) { struct diff_queue_struct *q = &diff_queued_diff; int i; int diff_output_format = options->output_format; struct diffstat_t *diffstat = NULL; + unsigned char sha1[20]; + + if (options->with_patch_id && !diff_flush_patch_id(options, sha1)) + printf("patch-id %s\n", sha1_to_hex(sha1)); if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) { diffstat = xcalloc(sizeof (struct diffstat_t), 1); diff --git a/diff.h b/diff.h index 7d7b6cd..29aac52 100644 --- a/diff.h +++ b/diff.h @@ -27,6 +27,7 @@ struct diff_options { unsigned recursive:1, with_raw:1, with_stat:1, + with_patch_id:1, tree_in_recursive:1, binary:1, full_index:1, @@ -185,4 +186,6 @@ extern int run_diff_files(struct rev_inf 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 */ -- 1.4.0.g319e-dirty - : send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html