]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'cc/interpret-trailers-more'
authorJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:26:23 +0000 (12:26 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:26:24 +0000 (12:26 -0800)
"git interpret-trailers" learned to properly handle the
"Conflicts:" block at the end.

* cc/interpret-trailers-more:
  trailer: add test with an old style conflict block
  trailer: reuse ignore_non_trailer() to ignore conflict lines
  commit: make ignore_non_trailer() non static
  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

1  2 
builtin/commit.c
builtin/merge.c
commit.c
commit.h
sequencer.c
trailer.c

diff --combined builtin/commit.c
index e108c5301564a86d396d72169f40cd24a9916a1f,537e228c3a67177ae26e85377b42ce7bbfd188ae..cda74e9a68f75fe3809a6dfa3901448be88eb7e5
@@@ -6,7 -6,6 +6,7 @@@
   */
  
  #include "cache.h"
 +#include "lockfile.h"
  #include "cache-tree.h"
  #include "color.h"
  #include "dir.h"
@@@ -316,8 -315,8 +316,8 @@@ static void refresh_cache_or_die(int re
                die_resolve_conflict("commit");
  }
  
 -static char *prepare_index(int argc, const char **argv, const char *prefix,
 -                         const struct commit *current_head, int is_status)
 +static const char *prepare_index(int argc, const char **argv, const char *prefix,
 +                               const struct commit *current_head, int is_status)
  {
        struct string_list partial;
        struct pathspec pathspec;
                        die(_("unable to create temporary index"));
  
                old_index_env = getenv(INDEX_ENVIRONMENT);
 -              setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
 +              setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
  
                if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
                        die(_("interactive add failed"));
                        unsetenv(INDEX_ENVIRONMENT);
  
                discard_cache();
 -              read_cache_from(index_lock.filename);
 +              read_cache_from(index_lock.filename.buf);
                if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
                        if (reopen_lock_file(&index_lock) < 0)
                                die(_("unable to write index file"));
                        warning(_("Failed to update main cache tree"));
  
                commit_style = COMMIT_NORMAL;
 -              return index_lock.filename;
 +              return index_lock.filename.buf;
        }
  
        /*
                if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
 -              return index_lock.filename;
 +              return index_lock.filename.buf;
        }
  
        /*
                die(_("unable to write temporary index file"));
  
        discard_cache();
 -      read_cache_from(false_lock.filename);
 +      read_cache_from(false_lock.filename.buf);
  
 -      return false_lock.filename;
 +      return false_lock.filename.buf;
  }
  
  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
@@@ -546,80 -545,77 +546,80 @@@ static int sane_ident_split(struct iden
        return 1;
  }
  
 -static int parse_force_date(const char *in, char *out, int len)
 +static int parse_force_date(const char *in, struct strbuf *out)
  {
 -      if (len < 1)
 -              return -1;
 -      *out++ = '@';
 -      len--;
 +      strbuf_addch(out, '@');
  
 -      if (parse_date(in, out, len) < 0) {
 +      if (parse_date(in, out) < 0) {
                int errors = 0;
                unsigned long t = approxidate_careful(in, &errors);
                if (errors)
                        return -1;
 -              snprintf(out, len, "%lu", t);
 +              strbuf_addf(out, "%lu", t);
        }
  
        return 0;
  }
  
 +static void set_ident_var(char **buf, char *val)
 +{
 +      free(*buf);
 +      *buf = val;
 +}
 +
 +static char *envdup(const char *var)
 +{
 +      const char *val = getenv(var);
 +      return val ? xstrdup(val) : NULL;
 +}
 +
  static void determine_author_info(struct strbuf *author_ident)
  {
        char *name, *email, *date;
        struct ident_split author;
 -      char date_buf[64];
  
 -      name = getenv("GIT_AUTHOR_NAME");
 -      email = getenv("GIT_AUTHOR_EMAIL");
 -      date = getenv("GIT_AUTHOR_DATE");
 +      name = envdup("GIT_AUTHOR_NAME");
 +      email = envdup("GIT_AUTHOR_EMAIL");
 +      date = envdup("GIT_AUTHOR_DATE");
  
        if (author_message) {
 -              const char *a, *lb, *rb, *eol;
 +              struct ident_split ident;
                size_t len;
 +              const char *a;
  
 -              a = strstr(author_message_buffer, "\nauthor ");
 +              a = find_commit_header(author_message_buffer, "author", &len);
                if (!a)
 -                      die(_("invalid commit: %s"), author_message);
 -
 -              lb = strchrnul(a + strlen("\nauthor "), '<');
 -              rb = strchrnul(lb, '>');
 -              eol = strchrnul(rb, '\n');
 -              if (!*lb || !*rb || !*eol)
 -                      die(_("invalid commit: %s"), author_message);
 -
 -              if (lb == a + strlen("\nauthor "))
 -                      /* \nauthor <foo@example.com> */
 -                      name = xcalloc(1, 1);
 -              else
 -                      name = xmemdupz(a + strlen("\nauthor "),
 -                                      (lb - strlen(" ") -
 -                                       (a + strlen("\nauthor "))));
 -              email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
 -              len = eol - (rb + strlen("> "));
 -              date = xmalloc(len + 2);
 -              *date = '@';
 -              memcpy(date + 1, rb + strlen("> "), len);
 -              date[len + 1] = '\0';
 +                      die(_("commit '%s' lacks author header"), author_message);
 +              if (split_ident_line(&ident, a, len) < 0)
 +                      die(_("commit '%s' has malformed author line"), author_message);
 +
 +              set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
 +              set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
 +
 +              if (ident.date_begin) {
 +                      struct strbuf date_buf = STRBUF_INIT;
 +                      strbuf_addch(&date_buf, '@');
 +                      strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin);
 +                      strbuf_addch(&date_buf, ' ');
 +                      strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin);
 +                      set_ident_var(&date, strbuf_detach(&date_buf, NULL));
 +              }
        }
  
        if (force_author) {
 -              const char *lb = strstr(force_author, " <");
 -              const char *rb = strchr(force_author, '>');
 +              struct ident_split ident;
  
 -              if (!lb || !rb)
 +              if (split_ident_line(&ident, force_author, strlen(force_author)) < 0)
                        die(_("malformed --author parameter"));
 -              name = xstrndup(force_author, lb - force_author);
 -              email = xstrndup(lb + 2, rb - (lb + 2));
 +              set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
 +              set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
        }
  
        if (force_date) {
 -              if (parse_force_date(force_date, date_buf, sizeof(date_buf)))
 +              struct strbuf date_buf = STRBUF_INIT;
 +              if (parse_force_date(force_date, &date_buf))
                        die(_("invalid date format: %s"), force_date);
 -              date = date_buf;
 +              set_ident_var(&date, strbuf_detach(&date_buf, NULL));
        }
  
        strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
                export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
                export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
        }
 +
 +      free(name);
 +      free(email);
 +      free(date);
  }
  
  static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
@@@ -800,32 -792,8 +800,8 @@@ static int prepare_to_commit(const cha
        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"));
@@@ -1272,21 -1240,22 +1248,21 @@@ static int dry_run_commit(int argc, con
        return commitable ? 0 : 1;
  }
  
 -static int parse_status_slot(const char *var, int offset)
 +static int parse_status_slot(const char *slot)
  {
 -      if (!strcasecmp(var+offset, "header"))
 +      if (!strcasecmp(slot, "header"))
                return WT_STATUS_HEADER;
 -      if (!strcasecmp(var+offset, "branch"))
 +      if (!strcasecmp(slot, "branch"))
                return WT_STATUS_ONBRANCH;
 -      if (!strcasecmp(var+offset, "updated")
 -              || !strcasecmp(var+offset, "added"))
 +      if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added"))
                return WT_STATUS_UPDATED;
 -      if (!strcasecmp(var+offset, "changed"))
 +      if (!strcasecmp(slot, "changed"))
                return WT_STATUS_CHANGED;
 -      if (!strcasecmp(var+offset, "untracked"))
 +      if (!strcasecmp(slot, "untracked"))
                return WT_STATUS_UNTRACKED;
 -      if (!strcasecmp(var+offset, "nobranch"))
 +      if (!strcasecmp(slot, "nobranch"))
                return WT_STATUS_NOBRANCH;
 -      if (!strcasecmp(var+offset, "unmerged"))
 +      if (!strcasecmp(slot, "unmerged"))
                return WT_STATUS_UNMERGED;
        return -1;
  }
  static int git_status_config(const char *k, const char *v, void *cb)
  {
        struct wt_status *s = cb;
 +      const char *slot_name;
  
        if (starts_with(k, "column."))
                return git_column_config(k, v, "status", &s->colopts);
                s->display_comment_prefix = git_config_bool(k, v);
                return 0;
        }
 -      if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
 -              int slot = parse_status_slot(k, 13);
 +      if (skip_prefix(k, "status.color.", &slot_name) ||
 +          skip_prefix(k, "color.status.", &slot_name)) {
 +              int slot = parse_status_slot(slot_name);
                if (slot < 0)
                        return 0;
                if (!v)
                        return config_error_nonbool(k);
 -              color_parse(v, k, s->color_palette[slot]);
 -              return 0;
 +              return color_parse(v, s->color_palette[slot]);
        }
        if (!strcmp(k, "status.relativepaths")) {
                s->relative_paths = git_config_bool(k, v);
@@@ -1513,12 -1481,14 +1489,12 @@@ static void print_summary(const char *p
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
  
 -      head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
 -      printf("[%s%s ",
 -              starts_with(head, "refs/heads/") ?
 -                      head + 11 :
 -                      !strcmp(head, "HEAD") ?
 -                              _("detached HEAD") :
 -                              head,
 -              initial_commit ? _(" (root-commit)") : "");
 +      head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
 +      if (!strcmp(head, "HEAD"))
 +              head = _("detached HEAD");
 +      else
 +              skip_prefix(head, "refs/heads/", &head);
 +      printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
  
        if (!log_tree_commit(&rev, commit)) {
                rev.always_show_header = 1;
@@@ -1809,8 -1779,8 +1785,8 @@@ int cmd_commit(int argc, const char **a
            ref_transaction_update(transaction, "HEAD", sha1,
                                   current_head
                                   ? current_head->object.sha1 : NULL,
 -                                 0, !!current_head, &err) ||
 -          ref_transaction_commit(transaction, sb.buf, &err)) {
 +                                 0, !!current_head, sb.buf, &err) ||
 +          ref_transaction_commit(transaction, &err)) {
                rollback_index_files();
                die("%s", err.buf);
        }
  
        if (commit_index_files())
                die (_("Repository has been updated, but unable to write\n"
 -                   "new_index file. Check that disk is not full or quota is\n"
 +                   "new_index file. Check that disk is not full and quota is\n"
                     "not exceeded, and then \"git reset HEAD\" to recover."));
  
        rerere(0);
diff --combined builtin/merge.c
index bebbe5b3081ebe2602abaf0dc9508a1342f01086,5370412e00620d85a84d94795e9049fda573fb29..215d4856e507f671139f41c73b57e661d8d1bf87
@@@ -9,7 -9,6 +9,7 @@@
  #include "cache.h"
  #include "parse-options.h"
  #include "builtin.h"
 +#include "lockfile.h"
  #include "run-command.h"
  #include "diff.h"
  #include "refs.h"
@@@ -29,6 -28,7 +29,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)
@@@ -557,7 -557,7 +558,7 @@@ static void parse_branch_merge_options(
        if (argc < 0)
                die(_("Bad branch.%s.mergeoptions string: %s"), branch,
                    split_cmdline_strerror(argc));
 -      argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
 +      REALLOC_ARRAY(argv, argc + 2);
        memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
        argc++;
        argv[0] = "branch.*.mergeoptions";
@@@ -657,18 -657,19 +658,18 @@@ static int try_merge_strategy(const cha
                              struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
  {
 -      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +      static struct lock_file lock;
  
 -      hold_locked_index(lock, 1);
 +      hold_locked_index(&lock, 1);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
 -          write_locked_index(&the_index, lock, COMMIT_LOCK))
 +          write_locked_index(&the_index, &lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
 -      rollback_lock_file(lock);
 +      rollback_lock_file(&lock);
  
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
                int clean, x;
                struct commit *result;
 -              struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
                struct commit_list *reversed = NULL;
                struct merge_options o;
                struct commit_list *j;
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
  
 -              hold_locked_index(lock, 1);
 +              hold_locked_index(&lock, 1);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (active_cache_changed &&
 -                  write_locked_index(&the_index, lock, COMMIT_LOCK))
 +                  write_locked_index(&the_index, &lock, COMMIT_LOCK))
                        die (_("unable to write %s"), get_index_file());
 -              rollback_lock_file(lock);
 +              rollback_lock_file(&lock);
                return clean ? 0 : 1;
        } else {
                return try_merge_command(strategy, xopts_nr, xopts,
@@@ -880,28 -881,19 +881,19 @@@ static int finish_automerge(struct comm
        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; "
@@@ -1101,7 -1093,7 +1093,7 @@@ int cmd_merge(int argc, const char **ar
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
         */
 -      branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
 +      branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
        if (branch && starts_with(branch, "refs/heads/"))
                branch += 11;
        if (!branch || is_null_sha1(head_sha1))
                 */
                if (advice_resolve_conflict)
                        die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
 -                                "Please, commit your changes before you can merge."));
 +                                "Please, commit your changes before you merge."));
                else
                        die(_("You have not concluded your merge (MERGE_HEAD exists)."));
        }
        if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
                if (advice_resolve_conflict)
                        die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 -                          "Please, commit your changes before you can merge."));
 +                          "Please, commit your changes before you merge."));
                else
                        die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
        }
                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);
diff --combined commit.c
index 19cf8f9c67bd0f4d71172f33424f7d2371a3af68,07d2290d0d0d2646ac9c3163315dfa1686816ca7..a54cb9a454fdce3ccf9dff58338e43d73945d728
+++ b/commit.c
@@@ -584,19 -584,25 +584,19 @@@ define_commit_slab(author_date_slab, un
  static void record_author_date(struct author_date_slab *author_date,
                               struct commit *commit)
  {
 -      const char *buf, *line_end, *ident_line;
        const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
 +      const char *ident_line;
 +      size_t ident_len;
        char *date_end;
        unsigned long date;
  
 -      for (buf = buffer; buf; buf = line_end + 1) {
 -              line_end = strchrnul(buf, '\n');
 -              if (!skip_prefix(buf, "author ", &ident_line)) {
 -                      if (!line_end[0] || line_end[1] == '\n')
 -                              return; /* end of header */
 -                      continue;
 -              }
 -              if (split_ident_line(&ident,
 -                                   ident_line, line_end - ident_line) ||
 -                  !ident.date_begin || !ident.date_end)
 -                      goto fail_exit; /* malformed "author" line */
 -              break;
 -      }
 +      ident_line = find_commit_header(buffer, "author", &ident_len);
 +      if (!ident_line)
 +              goto fail_exit; /* no author line */
 +      if (split_ident_line(&ident, ident_line, ident_len) ||
 +          !ident.date_begin || !ident.date_end)
 +              goto fail_exit; /* malformed "author" line */
  
        date = strtoul(ident.date_begin, &date_end, 10);
        if (date_end != ident.date_end)
@@@ -1214,7 -1220,43 +1214,7 @@@ free_return
        free(buf);
  }
  
 -static struct {
 -      char result;
 -      const char *check;
 -} sigcheck_gpg_status[] = {
 -      { 'G', "\n[GNUPG:] GOODSIG " },
 -      { 'B', "\n[GNUPG:] BADSIG " },
 -      { 'U', "\n[GNUPG:] TRUST_NEVER" },
 -      { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
 -};
 -
 -static void parse_gpg_output(struct signature_check *sigc)
 -{
 -      const char *buf = sigc->gpg_status;
 -      int i;
 -
 -      /* Iterate over all search strings */
 -      for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
 -              const char *found, *next;
 -
 -              if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
 -                      found = strstr(buf, sigcheck_gpg_status[i].check);
 -                      if (!found)
 -                              continue;
 -                      found += strlen(sigcheck_gpg_status[i].check);
 -              }
 -              sigc->result = sigcheck_gpg_status[i].result;
 -              /* The trust messages are not followed by key/signer information */
 -              if (sigc->result != 'U') {
 -                      sigc->key = xmemdupz(found, 16);
 -                      found += 17;
 -                      next = strchrnul(found, '\n');
 -                      sigc->signer = xmemdupz(found, next - found);
 -              }
 -      }
 -}
 -
 -void check_commit_signature(const struct commit* commit, struct signature_check *sigc)
 +void check_commit_signature(const struct commit *commit, struct signature_check *sigc)
  {
        struct strbuf payload = STRBUF_INIT;
        struct strbuf signature = STRBUF_INIT;
@@@ -1619,24 -1661,48 +1619,70 @@@ void print_commit_list(struct commit_li
        }
  }
  
 +const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
 +{
 +      int key_len = strlen(key);
 +      const char *line = msg;
 +
 +      while (line) {
 +              const char *eol = strchrnul(line, '\n');
 +
 +              if (line == eol)
 +                      return NULL;
 +
 +              if (eol - line > key_len &&
 +                  !strncmp(line, key, key_len) &&
 +                  line[key_len] == ' ') {
 +                      *out_len = eol - line - key_len - 1;
 +                      return line + key_len + 1;
 +              }
 +              line = *eol ? eol + 1 : NULL;
 +      }
 +      return NULL;
 +}
++
+ /*
+  * 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 --combined commit.h
index bc68ccbe691a28ed9b22fa935c78d2e9640347ea,f87b39250637a93f38a31beb583ae4002fd2c779..cd35ac150acd076047eed2e3200d1be12e4e7d5e
+++ b/commit.h
@@@ -326,17 -326,9 +326,20 @@@ extern struct commit_extra_header *read
  
  extern void free_commit_extra_headers(struct commit_extra_header *extra);
  
 +/*
 + * Search the commit object contents given by "msg" for the header "key".
 + * Returns a pointer to the start of the header contents, or NULL. The length
 + * of the header, up to the first newline, is returned via out_len.
 + *
 + * Note that some headers (like mergetag) may be multi-line. It is the caller's
 + * responsibility to parse further in this case!
 + */
 +extern const char *find_commit_header(const char *msg, const char *key,
 +                                    size_t *out_len);
 +
+ /* 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);
  
@@@ -370,7 -362,7 +373,7 @@@ extern void print_commit_list(struct co
   * at all.  This may allocate memory for sig->gpg_output, sig->gpg_status,
   * sig->signer and sig->key.
   */
 -extern void check_commit_signature(const struct commitcommit, struct signature_check *sigc);
 +extern void check_commit_signature(const struct commit *commit, struct signature_check *sigc);
  
  int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
  
diff --combined sequencer.c
index a03d4fa2521fbc119a8d6b471b9e3c1046610b84,f00fd7ce0358a8a8ace379cd874174a0b6b7d2db..77a1266760718b8e5b029636409634e241981888
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "lockfile.h"
  #include "sequencer.h"
  #include "dir.h"
  #include "object.h"
@@@ -252,8 -251,8 +252,8 @@@ static int fast_forward_to(const unsign
        if (!transaction ||
            ref_transaction_update(transaction, "HEAD",
                                   to, unborn ? null_sha1 : from,
 -                                 0, 1, &err) ||
 -          ref_transaction_commit(transaction, sb.buf, &err)) {
 +                                 0, 1, sb.buf, &err) ||
 +          ref_transaction_commit(transaction, &err)) {
                ref_transaction_free(transaction);
                error("%s", err.buf);
                strbuf_release(&sb);
        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,
        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;
  }
@@@ -331,7 -334,7 +335,7 @@@ static int is_index_unchanged(void
        unsigned char head_sha1[20];
        struct commit *head_commit;
  
 -      if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
 +      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
                return error(_("Could not resolve HEAD commit\n"));
  
        head_commit = lookup_commit(head_sha1);
@@@ -871,7 -874,7 +875,7 @@@ static int rollback_single_pick(void
        if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
            !file_exists(git_path("REVERT_HEAD")))
                return error(_("no cherry-pick or revert in progress"));
 -      if (read_ref_full("HEAD", head_sha1, 0, NULL))
 +      if (read_ref_full("HEAD", 0, head_sha1, NULL))
                return error(_("cannot resolve HEAD"));
        if (is_null_sha1(head_sha1))
                return error(_("cannot abort from a branch yet to be born"));
diff --combined trailer.c
index a905f5c50cef099c23d8a05acefc279168de69ec,30636d20a0637787fd397034e0142c94106cffc6..623adeb02d0e06a83d7b17a9b605ff8c6475a0bd
+++ b/trailer.c
@@@ -2,6 -2,7 +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 <chriscool@tuxfamily.org>
@@@ -228,7 -229,7 +229,7 @@@ static const char *apply_command(const 
  {
        struct strbuf cmd = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
 -      struct child_process cp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
        const char *argv[] = {NULL, NULL};
        const char *result;
  
                strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
  
        argv[0] = cmd.buf;
 -      memset(&cp, 0, sizeof(cp));
        cp.argv = argv;
        cp.env = local_repo_env;
        cp.no_stdin = 1;
@@@ -768,6 -770,22 +769,22 @@@ static int find_trailer_start(struct st
        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--) {
@@@ -790,14 -808,15 +807,15 @@@ static int process_input_file(struct st
                              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);
                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)
@@@ -830,7 -849,7 +848,7 @@@ void process_trailers(const char *file
        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);
        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);
  
        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);
  }