From 08e3ce5a20d738746b2a7700be6d3954bf01a2b9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 24 Oct 2014 11:27:22 -0700 Subject: builtin/merge.c: drop a parameter that is never used Since the very beginning when we added the "renormalizing" parameter to this function with 7610fa57 (merge-recursive --renormalize, 2010-08-05), nobody seems to have ever referenced it. Signed-off-by: Junio C Hamano diff --git a/builtin/merge.c b/builtin/merge.c index 41fb66d..f6894c7 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -884,7 +884,7 @@ static int finish_automerge(struct commit *head, return 0; } -static int suggest_conflicts(int renormalizing) +static int suggest_conflicts(void) { const char *filename; FILE *fp; @@ -1557,7 +1557,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) fprintf(stderr, _("Automatic merge went well; " "stopped before committing as requested\n")); else - ret = suggest_conflicts(option_renormalize); + ret = suggest_conflicts(); done: free(branch_to_free); -- cgit v0.10.2-6-g49f6 From 75c961b767ec061696634c1079dbe5f1a9e10f93 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 24 Oct 2014 11:34:59 -0700 Subject: merge & sequencer: unify codepaths that write "Conflicts:" hint Two identical loops in suggest_conflicts() in merge, and do_recursive_merge() in sequencer, can use a single helper function extracted from the latter that prepares the "Conflicts:" hint that is meant to remind the user the paths for which merge conflicts had to be resolved to write a better commit log message. Signed-off-by: Junio C Hamano diff --git a/builtin/merge.c b/builtin/merge.c index f6894c7..d30cb60 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -28,6 +28,7 @@ #include "remote.h" #include "fmt-merge-msg.h" #include "gpg-interface.h" +#include "sequencer.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -888,24 +889,15 @@ static int suggest_conflicts(void) { const char *filename; FILE *fp; - int pos; + struct strbuf msgbuf = STRBUF_INIT; filename = git_path("MERGE_MSG"); fp = fopen(filename, "a"); if (!fp) die_errno(_("Could not open '%s' for writing"), filename); - fprintf(fp, "\nConflicts:\n"); - for (pos = 0; pos < active_nr; pos++) { - const struct cache_entry *ce = active_cache[pos]; - - if (ce_stage(ce)) { - fprintf(fp, "\t%s\n", ce->name); - while (pos + 1 < active_nr && - !strcmp(ce->name, - active_cache[pos + 1]->name)) - pos++; - } - } + + append_conflicts_hint(&msgbuf); + fputs(msgbuf.buf, fp); fclose(fp); rerere(allow_rerere_auto); printf(_("Automatic merge failed; " diff --git a/sequencer.c b/sequencer.c index 06e52b4..0f84bbe 100644 --- a/sequencer.c +++ b/sequencer.c @@ -287,6 +287,24 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from, return ret; } +void append_conflicts_hint(struct strbuf *msgbuf) +{ + int i; + + strbuf_addstr(msgbuf, "\nConflicts:\n"); + for (i = 0; i < active_nr;) { + const struct cache_entry *ce = active_cache[i++]; + if (ce_stage(ce)) { + strbuf_addch(msgbuf, '\t'); + strbuf_addstr(msgbuf, ce->name); + strbuf_addch(msgbuf, '\n'); + while (i < active_nr && !strcmp(ce->name, + active_cache[i]->name)) + i++; + } + } +} + static int do_recursive_merge(struct commit *base, struct commit *next, const char *base_label, const char *next_label, unsigned char *head, struct strbuf *msgbuf, @@ -328,21 +346,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next, if (opts->signoff) append_signoff(msgbuf, 0, 0); - if (!clean) { - int i; - strbuf_addstr(msgbuf, "\nConflicts:\n"); - for (i = 0; i < active_nr;) { - const struct cache_entry *ce = active_cache[i++]; - if (ce_stage(ce)) { - strbuf_addch(msgbuf, '\t'); - strbuf_addstr(msgbuf, ce->name); - strbuf_addch(msgbuf, '\n'); - while (i < active_nr && !strcmp(ce->name, - active_cache[i]->name)) - i++; - } - } - } + if (!clean) + append_conflicts_hint(msgbuf); return !clean; } diff --git a/sequencer.h b/sequencer.h index 1fc22dc..c53519d 100644 --- a/sequencer.h +++ b/sequencer.h @@ -51,5 +51,6 @@ int sequencer_pick_revisions(struct replay_opts *opts); extern const char sign_off_header[]; void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag); +void append_conflicts_hint(struct strbuf *msgbuf); #endif -- cgit v0.10.2-6-g49f6 From 073bd75e177aa9f1fc42b152a38e251c7156dfe0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 28 Oct 2014 12:44:09 -0700 Subject: builtin/commit.c: extract ignore_non_trailer() helper function Extract a helper function from prepare_to_commit() to determine where to place a new Signed-off-by: line, which is essentially the true "end" of the log message, ignoring the trailing "Conflicts:" line and everything below it. The detection _should_ make sure the "Conflicts:" line it finds is truly the conflict hint block by checking everything that follows is a HT indented pathname to avoid false positive, but this logic will be revamped in a later patch to ignore comments and blanks anyway, so it is left as-is in this step. Signed-off-by: Junio C Hamano diff --git a/builtin/commit.c b/builtin/commit.c index fedb45a..cd455aa 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -593,6 +593,37 @@ static char *cut_ident_timestamp_part(char *string) return ket; } +/* + * Inspect sb and determine the true "end" of the log message, in + * order to find where to put a new Signed-off-by: line. Ignored are + * trailing "Conflict:" block. + * + * Returns the number of bytes from the tail to ignore, to be fed as + * the second parameter to append_signoff(). + */ +static int ignore_non_trailer(struct strbuf *sb) +{ + int ignore_footer = 0; + int i, eol, previous = 0; + const char *nl; + + for (i = 0; i < sb->len; i++) { + nl = memchr(sb->buf + i, '\n', sb->len - i); + if (nl) + eol = nl - sb->buf; + else + eol = sb->len; + if (!prefixcmp(sb->buf + previous, "\nConflicts:\n")) { + ignore_footer = sb->len - previous; + break; + } + while (i < eol) + i++; + previous = eol; + } + return ignore_footer; +} + static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *current_head, struct wt_status *s, @@ -718,32 +749,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (clean_message_contents) stripspace(&sb, 0); - if (signoff) { - /* - * See if we have a Conflicts: block at the end. If yes, count - * its size, so we can ignore it. - */ - int ignore_footer = 0; - int i, eol, previous = 0; - const char *nl; - - for (i = 0; i < sb.len; i++) { - nl = memchr(sb.buf + i, '\n', sb.len - i); - if (nl) - eol = nl - sb.buf; - else - eol = sb.len; - if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) { - ignore_footer = sb.len - previous; - break; - } - while (i < eol) - i++; - previous = eol; - } - - append_signoff(&sb, ignore_footer, 0); - } + if (signoff) + append_signoff(&sb, ignore_non_trailer(&sb), 0); if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); -- cgit v0.10.2-6-g49f6 From 261f315bebfa9af2d09f20211960100ff06f87cb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 28 Oct 2014 13:04:38 -0700 Subject: merge & sequencer: turn "Conflicts:" hint into a comment Just like other hints such as "Changes to be committed" we show in the editor to remind the committer what paths were involved in the resulting commit to help improving their log message, this section is merely a reminder. Traditionally, it was not made into comments primarily because it has to be generated outside the wt-status infrastructure, and also because it was meant as a bit stronger reminder than the others (i.e. explaining how you resolved conflicts is much more important than mentioning what you did to every paths involved in the commit). But that still does not make this hint a part of the log message proper, and not showing it as a comment is inviting mistakes. Note that we still notice "Conflicts:" followed by list of indented pathnames as an old-style cruft and insert a new Signed-off-by: before it. This is so that "commit --amend -s" adds the new S-o-b at the right place when used on an older commit. Signed-off-by: Junio C Hamano diff --git a/builtin/commit.c b/builtin/commit.c index cd455aa..0a78e76 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -596,32 +596,47 @@ static char *cut_ident_timestamp_part(char *string) /* * Inspect sb and determine the true "end" of the log message, in * order to find where to put a new Signed-off-by: line. Ignored are - * trailing "Conflict:" block. + * trailing comment lines and blank lines, and also the traditional + * "Conflicts:" block that is not commented out, so that we can use + * "git commit -s --amend" on an existing commit that forgot to remove + * it. * * Returns the number of bytes from the tail to ignore, to be fed as * the second parameter to append_signoff(). */ static int ignore_non_trailer(struct strbuf *sb) { - int ignore_footer = 0; - int i, eol, previous = 0; - const char *nl; + int boc = 0; + int bol = 0; + int in_old_conflicts_block = 0; - for (i = 0; i < sb->len; i++) { - nl = memchr(sb->buf + i, '\n', sb->len - i); - if (nl) - eol = nl - sb->buf; + while (bol < sb->len) { + char *next_line; + + if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol))) + next_line = sb->buf + sb->len; else - eol = sb->len; - if (!prefixcmp(sb->buf + previous, "\nConflicts:\n")) { - ignore_footer = sb->len - previous; - break; + next_line++; + + if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') { + /* is this the first of the run of comments? */ + if (!boc) + boc = bol; + /* otherwise, it is just continuing */ + } else if (!prefixcmp(sb->buf + bol, "Conflicts:\n")) { + in_old_conflicts_block = 1; + if (!boc) + boc = bol; + } else if (in_old_conflicts_block && sb->buf[bol] == '\t') { + ; /* a pathname in the conflicts block */ + } else if (boc) { + /* the previous was not trailing comment */ + boc = 0; + in_old_conflicts_block = 0; } - while (i < eol) - i++; - previous = eol; + bol = next_line - sb->buf; } - return ignore_footer; + return boc ? sb->len - boc : 0; } static int prepare_to_commit(const char *index_file, const char *prefix, diff --git a/sequencer.c b/sequencer.c index 0f84bbe..1d97da3 100644 --- a/sequencer.c +++ b/sequencer.c @@ -291,13 +291,12 @@ void append_conflicts_hint(struct strbuf *msgbuf) { int i; - strbuf_addstr(msgbuf, "\nConflicts:\n"); + strbuf_addch(msgbuf, '\n'); + strbuf_commented_addf(msgbuf, "Conflicts:\n"); for (i = 0; i < active_nr;) { const struct cache_entry *ce = active_cache[i++]; if (ce_stage(ce)) { - strbuf_addch(msgbuf, '\t'); - strbuf_addstr(msgbuf, ce->name); - strbuf_addch(msgbuf, '\n'); + strbuf_commented_addf(msgbuf, "\t%s\n", ce->name); while (i < active_nr && !strcmp(ce->name, active_cache[i]->name)) i++; diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index 223b984..7c5ad08 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -351,19 +351,45 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s' test_expect_success 'commit after failed cherry-pick adds -s at the right place' ' pristine_detach initial && test_must_fail git cherry-pick picked && + git commit -a -s && - pwd && - cat < expected && -picked -Signed-off-by: C O Mitter + # Do S-o-b and Conflicts appear in the right order? + cat <<-\EOF >expect && + Signed-off-by: C O Mitter + # Conflicts: + EOF + grep -e "^# Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual && + test_cmp expect actual && + + cat <<-\EOF >expected && + picked -Conflicts: - foo -EOF + Signed-off-by: C O Mitter + EOF - git show -s --pretty=format:%B > actual && + git show -s --pretty=format:%B >actual && test_cmp expected actual ' +test_expect_success 'commit --amend -s places the sign-off at the right place' ' + pristine_detach initial && + test_must_fail git cherry-pick picked && + + # emulate old-style conflicts block + mv .git/MERGE_MSG .git/MERGE_MSG+ && + sed -e "/^# Conflicts:/,\$s/^# *//" <.git/MERGE_MSG+ >.git/MERGE_MSG && + + git commit -a && + git commit --amend -s && + + # Do S-o-b and Conflicts appear in the right order? + cat <<-\EOF >expect && + Signed-off-by: C O Mitter + Conflicts: + EOF + grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual && + test_cmp expect actual +' + test_done -- cgit v0.10.2-6-g49f6 From 8c3845892363abf3fb5cf2fe61bc455554f50c68 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 9 Nov 2014 10:23:41 +0100 Subject: commit: make ignore_non_trailer() non static Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/commit.c b/builtin/commit.c index 80a618f..537e228 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -669,52 +669,6 @@ static void adjust_comment_line_char(const struct strbuf *sb) comment_line_char = *p; } -/* - * Inspect sb and determine the true "end" of the log message, in - * order to find where to put a new Signed-off-by: line. Ignored are - * trailing comment lines and blank lines, and also the traditional - * "Conflicts:" block that is not commented out, so that we can use - * "git commit -s --amend" on an existing commit that forgot to remove - * it. - * - * Returns the number of bytes from the tail to ignore, to be fed as - * the second parameter to append_signoff(). - */ -static int ignore_non_trailer(struct strbuf *sb) -{ - int boc = 0; - int bol = 0; - int in_old_conflicts_block = 0; - - while (bol < sb->len) { - char *next_line; - - if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol))) - next_line = sb->buf + sb->len; - else - next_line++; - - if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') { - /* is this the first of the run of comments? */ - if (!boc) - boc = bol; - /* otherwise, it is just continuing */ - } else if (starts_with(sb->buf + bol, "Conflicts:\n")) { - in_old_conflicts_block = 1; - if (!boc) - boc = bol; - } else if (in_old_conflicts_block && sb->buf[bol] == '\t') { - ; /* a pathname in the conflicts block */ - } else if (boc) { - /* the previous was not trailing comment */ - boc = 0; - in_old_conflicts_block = 0; - } - bol = next_line - sb->buf; - } - return boc ? sb->len - boc : 0; -} - static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *current_head, struct wt_status *s, diff --git a/commit.c b/commit.c index ae7f2b1..07d2290 100644 --- a/commit.c +++ b/commit.c @@ -1660,3 +1660,49 @@ void print_commit_list(struct commit_list *list, printf(format, sha1_to_hex(list->item->object.sha1)); } } + +/* + * Inspect sb and determine the true "end" of the log message, in + * order to find where to put a new Signed-off-by: line. Ignored are + * trailing comment lines and blank lines, and also the traditional + * "Conflicts:" block that is not commented out, so that we can use + * "git commit -s --amend" on an existing commit that forgot to remove + * it. + * + * Returns the number of bytes from the tail to ignore, to be fed as + * the second parameter to append_signoff(). + */ +int ignore_non_trailer(struct strbuf *sb) +{ + int boc = 0; + int bol = 0; + int in_old_conflicts_block = 0; + + while (bol < sb->len) { + char *next_line; + + if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol))) + next_line = sb->buf + sb->len; + else + next_line++; + + if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') { + /* is this the first of the run of comments? */ + if (!boc) + boc = bol; + /* otherwise, it is just continuing */ + } else if (starts_with(sb->buf + bol, "Conflicts:\n")) { + in_old_conflicts_block = 1; + if (!boc) + boc = bol; + } else if (in_old_conflicts_block && sb->buf[bol] == '\t') { + ; /* a pathname in the conflicts block */ + } else if (boc) { + /* the previous was not trailing comment */ + boc = 0; + in_old_conflicts_block = 0; + } + bol = next_line - sb->buf; + } + return boc ? sb->len - boc : 0; +} diff --git a/commit.h b/commit.h index a401ddf..f87b392 100644 --- a/commit.h +++ b/commit.h @@ -326,6 +326,9 @@ extern struct commit_extra_header *read_commit_extra_headers(struct commit *, co extern void free_commit_extra_headers(struct commit_extra_header *extra); +/* Find the end of the log message, the right place for a new trailer. */ +extern int ignore_non_trailer(struct strbuf *sb); + typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra, void *cb_data); -- cgit v0.10.2-6-g49f6 From 61cfef4ca4bdacbb90866fa8fd0e0f0b16b2cafc Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 9 Nov 2014 10:23:42 +0100 Subject: trailer: reuse ignore_non_trailer() to ignore conflict lines Make sure we look for trailers before any conflict line by reusing the ignore_non_trailer() function. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh index 1efb880..fed053a 100755 --- a/t/t7513-interpret-trailers.sh +++ b/t/t7513-interpret-trailers.sh @@ -232,6 +232,8 @@ test_expect_success 'with message that has comments' ' Reviewed-by: Johan Cc: Peff + # last comment + EOF cat basic_patch >>expected && git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual && diff --git a/trailer.c b/trailer.c index 219a5a2..30636d2 100644 --- a/trailer.c +++ b/trailer.c @@ -2,6 +2,7 @@ #include "string-list.h" #include "run-command.h" #include "string-list.h" +#include "commit.h" #include "trailer.h" /* * Copyright (c) 2013, 2014 Christian Couder @@ -769,6 +770,22 @@ static int find_trailer_start(struct strbuf **lines, int count) return only_spaces ? count : 0; } +/* Get the index of the end of the trailers */ +static int find_trailer_end(struct strbuf **lines, int patch_start) +{ + struct strbuf sb = STRBUF_INIT; + int i, ignore_bytes; + + for (i = 0; i < patch_start; i++) + strbuf_addbuf(&sb, lines[i]); + ignore_bytes = ignore_non_trailer(&sb); + strbuf_release(&sb); + for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--) + ignore_bytes -= lines[i]->len; + + return i + 1; +} + static int has_blank_line_before(struct strbuf **lines, int start) { for (;start >= 0; start--) { @@ -791,14 +808,15 @@ static int process_input_file(struct strbuf **lines, struct trailer_item **in_tok_last) { int count = 0; - int patch_start, trailer_start, i; + int patch_start, trailer_start, trailer_end, i; /* Get the line count */ while (lines[count]) count++; patch_start = find_patch_start(lines, count); - trailer_start = find_trailer_start(lines, patch_start); + trailer_end = find_trailer_end(lines, patch_start); + trailer_start = find_trailer_start(lines, trailer_end); /* Print lines before the trailers as is */ print_lines(lines, 0, trailer_start); @@ -807,14 +825,14 @@ static int process_input_file(struct strbuf **lines, printf("\n"); /* Parse trailer lines */ - for (i = trailer_start; i < patch_start; i++) { + for (i = trailer_start; i < trailer_end; i++) { if (lines[i]->buf[0] != comment_line_char) { struct trailer_item *new = create_trailer_item(lines[i]->buf); add_trailer_item(in_tok_first, in_tok_last, new); } } - return patch_start; + return trailer_end; } static void free_all(struct trailer_item **first) @@ -831,7 +849,7 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai struct trailer_item *in_tok_last = NULL; struct trailer_item *arg_tok_first; struct strbuf **lines; - int patch_start; + int trailer_end; /* Default config must be setup first */ git_config(git_trailer_default_config, NULL); @@ -840,7 +858,7 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai lines = read_input_file(file); /* Print the lines before the trailers */ - patch_start = process_input_file(lines, &in_tok_first, &in_tok_last); + trailer_end = process_input_file(lines, &in_tok_first, &in_tok_last); arg_tok_first = process_command_line_args(trailers); @@ -851,7 +869,7 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai free_all(&in_tok_first); /* Print the lines after the trailers as is */ - print_lines(lines, patch_start, INT_MAX); + print_lines(lines, trailer_end, INT_MAX); strbuf_list_free(lines); } -- cgit v0.10.2-6-g49f6 From 3d24a7267dd9b57b864d119a533bdfdfaccd9161 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 9 Nov 2014 10:23:43 +0100 Subject: trailer: add test with an old style conflict block Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh index fed053a..bd0ab46 100755 --- a/t/t7513-interpret-trailers.sh +++ b/t/t7513-interpret-trailers.sh @@ -213,7 +213,7 @@ test_expect_success 'with 2 files arguments' ' ' test_expect_success 'with message that has comments' ' - cat basic_message >>message_with_comments && + cat basic_message >message_with_comments && sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF && # comment @@ -240,6 +240,36 @@ test_expect_success 'with message that has comments' ' test_cmp expected actual ' +test_expect_success 'with message that has an old style conflict block' ' + cat basic_message >message_with_comments && + sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF && + # comment + + # other comment + Cc: Z + # yet another comment + Reviewed-by: Johan + Reviewed-by: Z + # last comment + + Conflicts: + + EOF + cat basic_message >expected && + cat >>expected <<-\EOF && + # comment + + Reviewed-by: Johan + Cc: Peff + # last comment + + Conflicts: + + EOF + git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual && + test_cmp expected actual +' + test_expect_success 'with commit complex message and trailer args' ' cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && -- cgit v0.10.2-6-g49f6