Merge branch 'js/patch'
* js/patch: diff.c: fix get_patch_id() t4014: fix test commit labels. format-patch: use clear_commit_marks() instead of some ad-hockery t4014: fix for whitespace from "wc -l" t4014: add format-patch --ignore-if-in-upstream test format-patch: introduce "--ignore-if-in-upstream" add diff_flush_patch_id() to calculate the patch id
This commit is contained in:
commit
f38c2a9c99
@ -160,6 +160,65 @@ static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
|
||||
freopen(filename, "w", stdout);
|
||||
}
|
||||
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
int cmd_format_patch(int argc, const char **argv, char **envp)
|
||||
{
|
||||
struct commit *commit;
|
||||
@ -170,6 +229,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 +296,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 +325,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;
|
||||
|
139
diff.c
139
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 = XDL_EMIT_FUNCNAMES;
|
||||
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;
|
||||
|
2
diff.h
2
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 */
|
||||
|
69
t/t4014-format-patch.sh
Executable file
69
t/t4014-format-patch.sh
Executable file
@ -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 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 C2 | git apply --index &&
|
||||
git commit -m "Master accepts moral equivalent of #2"
|
||||
|
||||
'
|
||||
|
||||
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
|
Loading…
Reference in New Issue
Block a user