merge: clarify collect_parents() logic
[gitweb.git] / builtin / merge.c
index dff043dac33ea88a8c5a05ea5ac3da71ca00c0d3..d2e36e2051d11a97f5d93ee4833ef60a14544c76 100644 (file)
@@ -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"
@@ -490,8 +491,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        }
        if (len) {
                struct strbuf truname = STRBUF_INIT;
-               strbuf_addstr(&truname, "refs/heads/");
-               strbuf_addstr(&truname, remote);
+               strbuf_addf(&truname, "refs/heads/%s", remote);
                strbuf_setlen(&truname, truname.len - len);
                if (ref_exists(truname.buf)) {
                        strbuf_addf(msg,
@@ -502,6 +502,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
                        strbuf_release(&truname);
                        goto cleanup;
                }
+               strbuf_release(&truname);
        }
 
        if (!strcmp(remote, "FETCH_HEAD") &&
@@ -656,19 +657,18 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              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;
@@ -696,13 +696,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                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,
@@ -1061,11 +1061,19 @@ static struct commit_list *collect_parents(struct commit *head_commit,
                                         "not something we can merge");
                remotes = &commit_list_insert(commit, remotes)->next;
        }
-       *remotes = NULL;
 
+       /*
+        * Is the current HEAD reachable from another commit being
+        * merged?  If so we do not want to record it as a parent of
+        * the resulting merge, unless --no-ff is given.  We will flip
+        * this variable to 0 when we find HEAD among the independent
+        * tips being merged.
+        */
+       *head_subsumed = 1;
+
+       /* Find what parents to record by checking independent ones. */
        parents = reduce_heads(remoteheads);
 
-       *head_subsumed = 1; /* we will flip this to 0 when we find it */
        for (remoteheads = NULL, remotes = &remoteheads;
             parents;
             parents = next) {
@@ -1075,6 +1083,7 @@ static struct commit_list *collect_parents(struct commit *head_commit,
                        *head_subsumed = 0;
                else
                        remotes = &commit_list_insert(commit, remotes)->next;
+               free(parents);
        }
        return remoteheads;
 }
@@ -1101,7 +1110,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * 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))
@@ -1165,45 +1174,26 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                option_commit = 0;
        }
 
-       if (!abort_current_merge) {
-               if (!argc) {
-                       if (default_to_upstream)
-                               argc = setup_with_upstream(&argv);
-                       else
-                               die(_("No commit specified and merge.defaultToUpstream not set."));
-               } else if (argc == 1 && !strcmp(argv[0], "-"))
-                       argv[0] = "@{-1}";
+       if (!argc) {
+               if (default_to_upstream)
+                       argc = setup_with_upstream(&argv);
+               else
+                       die(_("No commit specified and merge.defaultToUpstream not set."));
+       } else if (argc == 1 && !strcmp(argv[0], "-")) {
+               argv[0] = "@{-1}";
        }
+
        if (!argc)
                usage_with_options(builtin_merge_usage,
                        builtin_merge_options);
 
-       /*
-        * This could be traditional "merge <msg> HEAD <commit>..."  and
-        * the way we can tell it is to see if the second token is HEAD,
-        * but some people might have misused the interface and used a
-        * commit-ish that is the same as HEAD there instead.
-        * Traditional format never would have "-m" so it is an
-        * additional safety measure to check for it.
-        */
-
-       if (!have_message && head_commit &&
-           is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
-               strbuf_addstr(&merge_msg, argv[0]);
-               head_arg = argv[1];
-               argv += 2;
-               argc -= 2;
-               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
-       } else if (!head_commit) {
+       if (!head_commit) {
                struct commit *remote_head;
                /*
                 * If the merged head is a valid one there is no reason
                 * to forbid "git merge" into a branch yet to be born.
                 * We do the same for "git pull".
                 */
-               if (argc != 1)
-                       die(_("Can merge only exactly one commit into "
-                               "empty head"));
                if (squash)
                        die(_("Squash commit into empty head not supported yet"));
                if (fast_forward == FF_NO)
@@ -1213,10 +1203,29 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                remote_head = remoteheads->item;
                if (!remote_head)
                        die(_("%s - not something we can merge"), argv[0]);
+               if (remoteheads->next)
+                       die(_("Can merge only exactly one commit into empty head"));
                read_empty(remote_head->object.sha1, 0);
                update_ref("initial pull", "HEAD", remote_head->object.sha1,
                           NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
+       }
+
+       /*
+        * This could be traditional "merge <msg> HEAD <commit>..."  and
+        * the way we can tell it is to see if the second token is HEAD,
+        * but some people might have misused the interface and used a
+        * commit-ish that is the same as HEAD there instead.
+        * Traditional format never would have "-m" so it is an
+        * additional safety measure to check for it.
+        */
+       if (!have_message &&
+           is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
+               strbuf_addstr(&merge_msg, argv[0]);
+               head_arg = argv[1];
+               argv += 2;
+               argc -= 2;
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
        } else {
                struct strbuf merge_names = STRBUF_INIT;