merge-base --octopus to mimic show-branch --merge-base
[gitweb.git] / builtin / checkout.c
index c5ab7835e1fe260b9ec61f216bf198011eabf6f7..1994be92c66257da18c31502bda605be170fd092 100644 (file)
@@ -33,6 +33,7 @@ struct checkout_opts {
        int writeout_error;
 
        const char *new_branch;
+       const char *new_orphan_branch;
        int new_branch_log;
        enum branch_track track;
 };
@@ -128,24 +129,6 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos,
                     (stage == 2) ? "our" : "their");
 }
 
-/* NEEDSWORK: share with merge-recursive */
-static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
-{
-       unsigned long size;
-       enum object_type type;
-
-       if (!hashcmp(sha1, null_sha1)) {
-               mm->ptr = xstrdup("");
-               mm->size = 0;
-               return;
-       }
-
-       mm->ptr = read_sha1_file(sha1, &type, &size);
-       if (!mm->ptr || type != OBJ_BLOB)
-               die("unable to read blob object %s", sha1_to_hex(sha1));
-       mm->size = size;
-}
-
 static int checkout_merged(int pos, struct checkout *state)
 {
        struct cache_entry *ce = active_cache[pos];
@@ -163,11 +146,11 @@ static int checkout_merged(int pos, struct checkout *state)
            ce_stage(active_cache[pos+2]) != 3)
                return error("path '%s' does not have all 3 versions", path);
 
-       fill_mm(active_cache[pos]->sha1, &ancestor);
-       fill_mm(active_cache[pos+1]->sha1, &ours);
-       fill_mm(active_cache[pos+2]->sha1, &theirs);
+       read_mmblob(&ancestor, active_cache[pos]->sha1);
+       read_mmblob(&ours, active_cache[pos+1]->sha1);
+       read_mmblob(&theirs, active_cache[pos+2]->sha1);
 
-       status = ll_merge(&result_buf, path, &ancestor,
+       status = ll_merge(&result_buf, path, &ancestor, "base",
                          &ours, "ours", &theirs, "theirs", 0);
        free(ancestor.ptr);
        free(ours.ptr);
@@ -457,6 +440,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                        ret = reset_tree(new->commit->tree, opts, 1);
                        if (ret)
                                return ret;
+                       o.ancestor = old->name;
                        o.branch1 = new->name;
                        o.branch2 = "local";
                        merge_trees(&o, new->commit->tree, work,
@@ -509,8 +493,26 @@ static void update_refs_for_switch(struct checkout_opts *opts,
        struct strbuf msg = STRBUF_INIT;
        const char *old_desc;
        if (opts->new_branch) {
-               create_branch(old->name, opts->new_branch, new->name, 0,
-                             opts->new_branch_log, opts->track);
+               if (opts->new_orphan_branch) {
+                       if (opts->new_branch_log && !log_all_ref_updates) {
+                               int temp;
+                               char log_file[PATH_MAX];
+                               char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+
+                               temp = log_all_ref_updates;
+                               log_all_ref_updates = 1;
+                               if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+                                       fprintf(stderr, "Can not do reflog for '%s'\n",
+                                           opts->new_orphan_branch);
+                                       log_all_ref_updates = temp;
+                                       return;
+                               }
+                               log_all_ref_updates = temp;
+                       }
+               }
+               else
+                       create_branch(old->name, opts->new_branch, new->name, 0,
+                                     opts->new_branch_log, opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
        }
@@ -532,6 +534,14 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                                        opts->new_branch ? " a new" : "",
                                        new->name);
                }
+               if (old->path && old->name) {
+                       char log_file[PATH_MAX], ref_file[PATH_MAX];
+
+                       git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
+                       git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
+                       if (!file_exists(ref_file) && file_exists(log_file))
+                               remove_path(log_file);
+               }
        } else if (strcmp(new->name, "HEAD")) {
                update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
                           REF_NODEREF, DIE_ON_ERR);
@@ -626,7 +636,8 @@ static int check_tracking_name(const char *refname, const unsigned char *sha1,
 
 static const char *unique_tracking_name(const char *name)
 {
-       struct tracking_name_data cb_data = { name, NULL, 1 };
+       struct tracking_name_data cb_data = { NULL, NULL, 1 };
+       cb_data.name = name;
        for_each_ref(check_tracking_name, &cb_data);
        if (cb_data.unique)
                return cb_data.remote;
@@ -650,6 +661,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
                OPT_SET_INT('t', "track",  &opts.track, "track",
                        BRANCH_TRACK_EXPLICIT),
+               OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
                OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
                            2),
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
@@ -695,6 +707,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                opts.new_branch = argv0 + 1;
        }
 
+       if (opts.new_orphan_branch) {
+               if (opts.new_branch)
+                       die("--orphan and -b are mutually exclusive");
+               if (opts.track > 0)
+                       die("--orphan cannot be used with -t");
+               opts.new_branch = opts.new_orphan_branch;
+       }
+
        if (conflict_style) {
                opts.merge = 1; /* implied */
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);