Merge branch 'jc/conflict-hint' into cc/interpret-trailers-more
authorJunio C Hamano <gitster@pobox.com>
Mon, 10 Nov 2014 17:56:39 +0000 (09:56 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 10 Nov 2014 17:56:39 +0000 (09:56 -0800)
* jc/conflict-hint:
merge & sequencer: turn "Conflicts:" hint into a comment
builtin/commit.c: extract ignore_non_trailer() helper function
merge & sequencer: unify codepaths that write "Conflicts:" hint
builtin/merge.c: drop a parameter that is never used
git-tag.txt: Add a missing hyphen to `-s`

Documentation/git-tag.txt
builtin/commit.c
builtin/merge.c
sequencer.c
sequencer.h
t/t3507-cherry-pick-conflict.sh
index 320908369f067f5d494c6bc0c3c9f28fa26859a6..e953ba4439f5995c797b4d7ad869847a5479d982 100644 (file)
@@ -42,7 +42,7 @@ committer identity for the current user is used to find the
 GnuPG key for signing.         The configuration variable `gpg.program`
 is used to specify custom GnuPG binary.
 
-Tag objects (created with `-a`, `s`, or `-u`) are called "annotated"
+Tag objects (created with `-a`, `-s`, or `-u`) are called "annotated"
 tags; they contain a creation date, the tagger name and e-mail, a
 tagging message, and an optional GnuPG signature. Whereas a
 "lightweight" tag is simply a name for an object (usually a commit
index 41f481bd030ba96f8883e64517eda95931344545..80a618fe137811fff6e205104297e7158d0572a9 100644 (file)
@@ -669,6 +669,52 @@ 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,
@@ -792,32 +838,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 (starts_with(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"));
index 9da9e30d9be46a5ed2de06e8545fe61537ba3676..5370412e00620d85a84d94795e9049fda573fb29 100644 (file)
@@ -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)
@@ -880,28 +881,19 @@ 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;
-       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; "
@@ -1550,7 +1542,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);
index 5e8a207474bc6971a0de4f9ff6160a5681ac7b6d..f00fd7ce0358a8a8ace379cd874174a0b6b7d2db 100644 (file)
@@ -266,6 +266,23 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
        return 0;
 }
 
+void append_conflicts_hint(struct strbuf *msgbuf)
+{
+       int i;
+
+       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_commented_addf(msgbuf, "\t%s\n", ce->name);
+                       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,
@@ -306,21 +323,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;
 }
index db43e9cf86dcc0914a674adca63303c44e2ccc24..5ed5cb1d97ced70a9ebffd1dad524a12e7f7810a 100644 (file)
@@ -53,5 +53,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
index 223b98433c502b03c4ba70550cb32d42a5f6295b..7c5ad086264e3d1c140da7fd996f27c1352a26a9 100755 (executable)
@@ -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 <<EOF > expected &&
-picked
 
-Signed-off-by: C O Mitter <committer@example.com>
+       # Do S-o-b and Conflicts appear in the right order?
+       cat <<-\EOF >expect &&
+       Signed-off-by: C O Mitter <committer@example.com>
+       # 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 <committer@example.com>
+       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 <committer@example.com>
+       Conflicts:
+       EOF
+       grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+       test_cmp expect actual
+'
+
 test_done