Merge branch 'jn/merge-diff3-label'
authorJunio C Hamano <gitster@pobox.com>
Sat, 3 Apr 2010 19:28:41 +0000 (12:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 3 Apr 2010 19:28:41 +0000 (12:28 -0700)
* jn/merge-diff3-label:
merge-recursive: add a label for ancestor
cherry-pick, revert: add a label for ancestor
revert: clarify label on conflict hunks
compat: add mempcpy()
checkout -m --conflict=diff3: add a label for ancestor
merge_trees(): add ancestor label parameter for diff3-style output
merge_file(): add comment explaining behavior wrt conflict style
checkout --conflict=diff3: add a label for ancestor
ll_merge(): add ancestor label parameter for diff3-style output
merge-file --diff3: add a label for ancestor
xdl_merge(): move file1 and file2 labels to xmparam structure
xdl_merge(): add optional ancestor label to diff3-style output
tests: document cherry-pick behavior in face of conflicts
tests: document format of conflicts from checkout -m

Conflicts:
builtin/revert.c

1  2 
builtin/revert.c
git-compat-util.h
diff --combined builtin/revert.c
index 9a3c14c329713f16d14147b81a4b8031028a4f82,1ddfac15b55a1b0bed7c7e359cb952749225a4f2..778a56eb512edde3aac6c2a4d668cc73ab3d8460
@@@ -13,7 -13,6 +13,7 @@@
  #include "revision.h"
  #include "rerere.h"
  #include "merge-recursive.h"
 +#include "refs.h"
  
  /*
   * This implements the builtins revert and cherry-pick.
@@@ -36,7 -35,7 +36,7 @@@ static const char * const cherry_pick_u
        NULL
  };
  
 -static int edit, no_replay, no_commit, mainline, signoff;
 +static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
  static enum { REVERT, CHERRY_PICK } action;
  static struct commit *commit;
  static const char *commit_name;
@@@ -46,6 -45,8 +46,8 @@@ static const char *me
  
  #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
  
+ static char *get_encoding(const char *message);
  static void parse_args(int argc, const char **argv)
  {
        const char * const * usage_str =
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
                OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
                OPT_END(),
 +              OPT_END(),
 +              OPT_END(),
        };
  
 +      if (action == CHERRY_PICK) {
 +              struct option cp_extra[] = {
 +                      OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
 +                      OPT_END(),
 +              };
 +              if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
 +                      die("program error");
 +      }
 +
        if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
                usage_with_options(usage_str, options);
  
                exit(1);
  }
  
- static char *get_oneline(const char *message)
+ struct commit_message {
+       char *parent_label;
+       const char *label;
+       const char *subject;
+       char *reencoded_message;
+       const char *message;
+ };
+ static int get_message(const char *raw_message, struct commit_message *out)
  {
-       char *result;
-       const char *p = message, *abbrev, *eol;
+       const char *encoding;
+       const char *p, *abbrev, *eol;
+       char *q;
        int abbrev_len, oneline_len;
  
-       if (!p)
-               die ("Could not read commit message of %s",
-                               sha1_to_hex(commit->object.sha1));
+       if (!raw_message)
+               return -1;
+       encoding = get_encoding(raw_message);
+       if (!encoding)
+               encoding = "UTF-8";
+       if (!git_commit_encoding)
+               git_commit_encoding = "UTF-8";
+       if ((out->reencoded_message = reencode_string(raw_message,
+                                       git_commit_encoding, encoding)))
+               out->message = out->reencoded_message;
+       abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+       abbrev_len = strlen(abbrev);
+       /* Find beginning and end of commit subject. */
+       p = out->message;
        while (*p && (*p != '\n' || p[1] != '\n'))
                p++;
        if (*p) {
                p += 2;
                for (eol = p + 1; *eol && *eol != '\n'; eol++)
                        ; /* do nothing */
        } else
                eol = p;
-       abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
-       abbrev_len = strlen(abbrev);
        oneline_len = eol - p;
-       result = xmalloc(abbrev_len + 5 + oneline_len);
-       memcpy(result, abbrev, abbrev_len);
-       memcpy(result + abbrev_len, "... ", 4);
-       memcpy(result + abbrev_len + 4, p, oneline_len);
-       result[abbrev_len + 4 + oneline_len] = '\0';
-       return result;
+       out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
+                             strlen("... ") + oneline_len + 1);
+       q = out->parent_label;
+       q = mempcpy(q, "parent of ", strlen("parent of "));
+       out->label = q;
+       q = mempcpy(q, abbrev, abbrev_len);
+       q = mempcpy(q, "... ", strlen("... "));
+       out->subject = q;
+       q = mempcpy(q, p, oneline_len);
+       *q = '\0';
+       return 0;
+ }
+ static void free_message(struct commit_message *msg)
+ {
+       free(msg->parent_label);
+       free(msg->reencoded_message);
  }
  
  static char *get_encoding(const char *message)
@@@ -256,25 -277,15 +289,25 @@@ static NORETURN void die_dirty_index(co
        }
  }
  
 +static int fast_forward_to(const unsigned char *to, const unsigned char *from)
 +{
 +      struct ref_lock *ref_lock;
 +
 +      read_cache();
 +      if (checkout_fast_forward(from, to))
 +              exit(1); /* the callee should have complained already */
 +      ref_lock = lock_any_ref_for_update("HEAD", from, 0);
 +      return write_ref_sha1(ref_lock, to, "cherry-pick");
 +}
 +
  static int revert_or_cherry_pick(int argc, const char **argv)
  {
        unsigned char head[20];
        struct commit *base, *next, *parent;
+       const char *base_label, *next_label;
        int i, index_fd, clean;
-       char *oneline, *reencoded_message = NULL;
-       const char *message, *encoding;
+       struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
 -
 -      char *defmsg = git_pathdup("MERGE_MSG");
 +      char *defmsg = NULL;
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
        static struct lock_file index_lock;
        if (action == REVERT && !no_replay)
                die("revert is incompatible with replay");
  
 +      if (allow_ff) {
 +              if (signoff)
 +                      die("cherry-pick --ff cannot be used with --signoff");
 +              if (no_commit)
 +                      die("cherry-pick --ff cannot be used with --no-commit");
 +              if (no_replay)
 +                      die("cherry-pick --ff cannot be used with -x");
 +              if (edit)
 +                      die("cherry-pick --ff cannot be used with --edit");
 +      }
 +
        if (read_cache() < 0)
                die("git %s: failed to read the index", me);
        if (no_commit) {
        }
        discard_cache();
  
 -      index_fd = hold_locked_index(&index_lock, 1);
 -
        if (!commit->parents) {
                if (action == REVERT)
                        die ("Cannot revert a root commit");
        else
                parent = commit->parents->item;
  
-       if (!(message = commit->buffer))
-               die ("Cannot get commit message for %s",
-                               sha1_to_hex(commit->object.sha1));
 +      if (allow_ff && !hashcmp(parent->object.sha1, head))
 +              return fast_forward_to(commit->object.sha1, head);
 +
        if (parent && parse_commit(parent) < 0)
                die("%s: cannot parse parent commit %s",
                    me, sha1_to_hex(parent->object.sha1));
  
+       if (get_message(commit->buffer, &msg) != 0)
+               die("Cannot get commit message for %s",
+                               sha1_to_hex(commit->object.sha1));
        /*
         * "commit" is an existing commit.  We would want to apply
         * the difference it introduces since its first parent "prev"
         * reverse of it if we are revert.
         */
  
 +      defmsg = git_pathdup("MERGE_MSG");
        msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
                                           LOCK_DIE_ON_ERROR);
  
-       encoding = get_encoding(message);
-       if (!encoding)
-               encoding = "UTF-8";
-       if (!git_commit_encoding)
-               git_commit_encoding = "UTF-8";
-       if ((reencoded_message = reencode_string(message,
-                                       git_commit_encoding, encoding)))
-               message = reencoded_message;
-       oneline = get_oneline(message);
 +      index_fd = hold_locked_index(&index_lock, 1);
 +
        if (action == REVERT) {
-               char *oneline_body = strchr(oneline, ' ');
                base = commit;
+               base_label = msg.label;
                next = parent;
+               next_label = msg.parent_label;
                add_to_msg("Revert \"");
-               add_to_msg(oneline_body + 1);
+               add_to_msg(msg.subject);
                add_to_msg("\"\n\nThis reverts commit ");
                add_to_msg(sha1_to_hex(commit->object.sha1));
  
                add_to_msg(".\n");
        } else {
                base = parent;
+               base_label = msg.parent_label;
                next = commit;
-               set_author_ident_env(message);
-               add_message_to_msg(message);
+               next_label = msg.label;
+               set_author_ident_env(msg.message);
+               add_message_to_msg(msg.message);
                if (no_replay) {
                        add_to_msg("(cherry picked from commit ");
                        add_to_msg(sha1_to_hex(commit->object.sha1));
  
        read_cache();
        init_merge_options(&o);
+       o.ancestor = base ? base_label : "(empty tree)";
        o.branch1 = "HEAD";
-       o.branch2 = oneline;
+       o.branch2 = next ? next_label : "(empty tree)";
  
        head_tree = parse_tree_indirect(head);
        next_tree = next ? next->tree : empty_tree();
                args[i] = NULL;
                return execv_git_cmd(args);
        }
-       free(reencoded_message);
+       free_message(&msg);
        free(defmsg);
  
        return 0;
diff --combined git-compat-util.h
index e292926fec2d6910ed8a44bb0501fb6e219af3f9,9bed5a0b47b57d64c559faf09250166c77906ceb..7e62b552700a580646dbb6a8921c40761d6b57a2
@@@ -55,8 -55,7 +55,8 @@@
  # else
  # define _XOPEN_SOURCE 500
  # endif
 -#elif !defined(__APPLE__) && !defined(__FreeBSD__)  && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
 +#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
 +      !defined(_M_UNIX) && !defined(sgi) && !defined(__DragonFly__)
  #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
  #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
  #endif
@@@ -332,6 -331,7 +332,7 @@@ extern int git_vsnprintf(char *str, siz
  #ifdef __GLIBC_PREREQ
  #if __GLIBC_PREREQ(2, 1)
  #define HAVE_STRCHRNUL
+ #define HAVE_MEMPCPY
  #endif
  #endif
  
@@@ -345,6 -345,14 +346,14 @@@ static inline char *gitstrchrnul(const 
  }
  #endif
  
+ #ifndef HAVE_MEMPCPY
+ #define mempcpy gitmempcpy
+ static inline void *gitmempcpy(void *dest, const void *src, size_t n)
+ {
+       return (char *)memcpy(dest, src, n) + n;
+ }
+ #endif
  extern void release_pack_memory(size_t, int);
  
  extern char *xstrdup(const char *str);