Allow cherry-picking root commits
authorJohannes Schindelin <Johannes.Schindelin@gmx.de>
Fri, 4 Jul 2008 15:19:52 +0000 (16:19 +0100)
committerJunio C Hamano <gitster@pobox.com>
Mon, 7 Jul 2008 20:23:42 +0000 (13:23 -0700)
A root commit couldn't be cherry-picked.  But its semantics can be
defined as simply merging two trees by overlaying disjoint parts
and merging overlapping files without any common ancestor.  You
should be able to rebase originally independent branches on top of
another branch by using this.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-revert.c
t/t3503-cherry-pick-root.sh [new file with mode: 0755]

index 0270f9b85a8229d30ab3fab5ce73661f383b005e..f3d452418c25b0d95a911a050f75ba190d6513c1 100644 (file)
@@ -206,6 +206,7 @@ static int merge_recursive(const char *base_sha1,
 {
        char buffer[256];
        const char *argv[6];
+       int i = 0;
 
        sprintf(buffer, "GITHEAD_%s", head_sha1);
        setenv(buffer, head_name, 1);
@@ -218,12 +219,13 @@ static int merge_recursive(const char *base_sha1,
         * and $prev on top of us (when reverting), or the change between
         * $prev and $commit on top of us (when cherry-picking or replaying).
         */
-       argv[0] = "merge-recursive";
-       argv[1] = base_sha1;
-       argv[2] = "--";
-       argv[3] = head_sha1;
-       argv[4] = next_sha1;
-       argv[5] = NULL;
+       argv[i++] = "merge-recursive";
+       if (base_sha1)
+               argv[i++] = base_sha1;
+       argv[i++] = "--";
+       argv[i++] = head_sha1;
+       argv[i++] = next_sha1;
+       argv[i++] = NULL;
 
        return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
 }
@@ -297,9 +299,12 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                discard_cache();
        }
 
-       if (!commit->parents)
-               die ("Cannot %s a root commit", me);
-       if (commit->parents->next) {
+       if (!commit->parents) {
+               if (action == REVERT)
+                       die ("Cannot revert a root commit");
+               parent = NULL;
+       }
+       else if (commit->parents->next) {
                /* Reverting or cherry-picking a merge commit */
                int cnt;
                struct commit_list *p;
@@ -368,7 +373,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                }
        }
 
-       if (merge_recursive(sha1_to_hex(base->object.sha1),
+       if (merge_recursive(base == NULL ?
+                               NULL : sha1_to_hex(base->object.sha1),
                                sha1_to_hex(head), "HEAD",
                                sha1_to_hex(next->object.sha1), oneline) ||
                        write_cache_as_tree(head, 0, NULL)) {
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
new file mode 100755 (executable)
index 0000000..b0faa29
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='test cherry-picking a root commit'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       echo first > file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit -m "first" &&
+
+       git symbolic-ref HEAD refs/heads/second &&
+       rm .git/index file1 &&
+       echo second > file2 &&
+       git add file2 &&
+       test_tick &&
+       git commit -m "second"
+
+'
+
+test_expect_success 'cherry-pick a root commit' '
+
+       git cherry-pick master &&
+       test first = $(cat file1)
+
+'
+
+test_done