Support creation of merge commits in fast-import.
authorShawn O. Pearce <spearce@spearce.org>
Fri, 12 Jan 2007 03:21:38 +0000 (22:21 -0500)
committerShawn O. Pearce <spearce@spearce.org>
Sun, 14 Jan 2007 07:15:12 +0000 (02:15 -0500)
Some importers are able to determine when branch merges occurred
within their source data.  In these cases they will want to supply
the correct commits to fast-import so that a proper merge commit
will exist in Git.  This is now supported by supplying a 'merge '
command after the commit message and optional from command.

A merge is not actually performed by fast-import, its assumed that
the frontend performed any sort of merging activity already and
that fast-import should simply be storing its result.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
fast-import.c

index e9a46c6c3ace0a8c7646eaf4e8ae29ba4d5fc65f..15db4b39d136403b66ac1ca7d5d5745f24ef577d 100644 (file)
@@ -20,6 +20,7 @@ Format of STDIN stream:
     'committer' sp name '<' email '>' ts tz lf
     commit_msg
     ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
+    ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
     file_change*
     lf;
   commit_msg ::= data;
@@ -202,6 +203,11 @@ struct dbuf
        size_t capacity;
 };
 
+struct hash_list
+{
+       struct hash_list *next;
+       unsigned char sha1[20];
+};
 
 /* Stats and misc. counters */
 static unsigned long max_depth = 10;
@@ -1502,6 +1508,48 @@ static void cmd_from(struct branch *b)
        read_next_command();
 }
 
+static struct hash_list* cmd_merge(unsigned int *count)
+{
+       struct hash_list *list = NULL, *n, *e;
+       const char *from, *endp;
+       char *str_uq;
+       struct branch *s;
+
+       *count = 0;
+       while (!strncmp("merge ", command_buf.buf, 6)) {
+               from = strchr(command_buf.buf, ' ') + 1;
+               str_uq = unquote_c_style(from, &endp);
+               if (str_uq) {
+                       if (*endp)
+                               die("Garbage after string in: %s", command_buf.buf);
+                       from = str_uq;
+               }
+
+               n = xmalloc(sizeof(*n));
+               s = lookup_branch(from);
+               if (s)
+                       hashcpy(n->sha1, s->sha1);
+               else if (*from == ':') {
+                       unsigned long idnum = strtoul(from + 1, NULL, 10);
+                       struct object_entry *oe = find_mark(idnum);
+                       if (oe->type != OBJ_COMMIT)
+                               die("Mark :%lu not a commit", idnum);
+                       hashcpy(n->sha1, oe->sha1);
+               } else if (get_sha1(from, n->sha1))
+                       die("Invalid ref name or SHA1 expression: %s", from);
+
+               n->next = NULL;
+               if (list)
+                       e->next = n;
+               else
+                       list = n;
+               e = n;
+               *count++;
+               read_next_command();
+       }
+       return list;
+}
+
 static void cmd_new_commit()
 {
        struct branch *b;
@@ -1512,6 +1560,8 @@ static void cmd_new_commit()
        char *sp;
        char *author = NULL;
        char *committer = NULL;
+       struct hash_list *merge_list = NULL;
+       unsigned int merge_count;
 
        /* Obtain the branch name from the rest of our command */
        sp = strchr(command_buf.buf, ' ') + 1;
@@ -1542,6 +1592,7 @@ static void cmd_new_commit()
        msg = cmd_data(&msglen);
        read_next_command();
        cmd_from(b);
+       merge_list = cmd_merge(&merge_count);
 
        /* ensure the branch is active/loaded */
        if (!b->branch_tree.tree || !max_active_branches) {
@@ -1567,6 +1618,7 @@ static void cmd_new_commit()
        hashcpy(b->branch_tree.versions[0].sha1,
                b->branch_tree.versions[1].sha1);
        size_dbuf(&new_data, 97 + msglen
+               + merge_count * 49
                + (author
                        ? strlen(author) + strlen(committer)
                        : 2 * strlen(committer)));
@@ -1575,6 +1627,12 @@ static void cmd_new_commit()
                sha1_to_hex(b->branch_tree.versions[1].sha1));
        if (!is_null_sha1(b->sha1))
                sp += sprintf(sp, "parent %s\n", sha1_to_hex(b->sha1));
+       while (merge_list) {
+               struct hash_list *next = merge_list->next;
+               sp += sprintf(sp, "parent %s\n", sha1_to_hex(merge_list->sha1));
+               free(merge_list);
+               merge_list = next;
+       }
        if (author)
                sp += sprintf(sp, "%s\n", author);
        else