Code

Merge branch 'jk/maint-commit-v-strip' into jk/commit-v-strip
[git.git] / builtin-checkout.c
index 79214327b0d99369b3ce384ed1900342f5874c87..06904c3bd3595b4c8cbc6c66d3a1b9c364381bd6 100644 (file)
@@ -47,7 +47,7 @@ static int post_checkout_hook(struct commit *old, struct commit *new,
 
        memset(&proc, 0, sizeof(proc));
        argv[0] = name;
-       argv[1] = xstrdup(sha1_to_hex(old->object.sha1));
+       argv[1] = xstrdup(sha1_to_hex(old ? old->object.sha1 : null_sha1));
        argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
        argv[3] = changed ? "1" : "0";
        argv[4] = NULL;
@@ -206,6 +206,8 @@ static int checkout_merged(int pos, struct checkout *state)
        ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
                              sha1,
                              path, 2, 0);
+       if (!ce)
+               die("make_cache_entry failed for path '%s'", path);
        status = checkout_entry(ce, state, NULL);
        return status;
 }
@@ -308,8 +310,7 @@ static void show_local_changes(struct object *head)
 
 static void describe_detached_head(char *msg, struct commit *commit)
 {
-       struct strbuf sb;
-       strbuf_init(&sb, 0);
+       struct strbuf sb = STRBUF_INIT;
        parse_commit(commit);
        pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, NULL, NULL, 0, 0);
        fprintf(stderr, "%s %s... %s\n", msg,
@@ -358,8 +359,7 @@ struct branch_info {
 
 static void setup_branch_path(struct branch_info *branch)
 {
-       struct strbuf buf;
-       strbuf_init(&buf, 0);
+       struct strbuf buf = STRBUF_INIT;
        strbuf_addstr(&buf, "refs/heads/");
        strbuf_addstr(&buf, branch->name);
        branch->path = strbuf_detach(&buf, NULL);
@@ -397,6 +397,8 @@ static int merge_working_tree(struct checkout_opts *opts,
                }
 
                /* 2-way merge to the new branch */
+               topts.initial_checkout = (!active_nr &&
+                                         (old->commit == new->commit));
                topts.update = 1;
                topts.merge = 1;
                topts.gently = opts->merge;
@@ -419,6 +421,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                         */
                        struct tree *result;
                        struct tree *work;
+                       struct merge_options o;
                        if (!opts->merge)
                                return 1;
                        parse_commit(old->commit);
@@ -437,13 +440,17 @@ static int merge_working_tree(struct checkout_opts *opts,
                         */
 
                        add_files_to_cache(NULL, NULL, 0);
-                       work = write_tree_from_memory();
+                       init_merge_options(&o);
+                       o.verbosity = 0;
+                       work = write_tree_from_memory(&o);
 
                        ret = reset_tree(new->commit->tree, opts, 1);
                        if (ret)
                                return ret;
-                       merge_trees(new->commit->tree, work, old->commit->tree,
-                                   new->name, "local", &result);
+                       o.branch1 = new->name;
+                       o.branch2 = "local";
+                       merge_trees(&o, new->commit->tree, work,
+                               old->commit->tree, &result);
                        ret = reset_tree(new->commit->tree, opts, 0);
                        if (ret)
                                return ret;
@@ -454,7 +461,7 @@ static int merge_working_tree(struct checkout_opts *opts,
            commit_locked_index(lock_file))
                die("unable to write new index file");
 
-       if (!opts->force)
+       if (!opts->force && !opts->quiet)
                show_local_changes(&new->commit->object);
 
        return 0;
@@ -475,7 +482,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                                   struct branch_info *old,
                                   struct branch_info *new)
 {
-       struct strbuf msg;
+       struct strbuf msg = STRBUF_INIT;
        const char *old_desc;
        if (opts->new_branch) {
                create_branch(old->name, opts->new_branch, new->name, 0,
@@ -484,12 +491,11 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                setup_branch_path(new);
        }
 
-       strbuf_init(&msg, 0);
        old_desc = old->name;
-       if (!old_desc)
+       if (!old_desc && old->commit)
                old_desc = sha1_to_hex(old->commit->object.sha1);
        strbuf_addf(&msg, "checkout: moving from %s to %s",
-                   old_desc, new->name);
+                   old_desc ? old_desc : "(invalid)", new->name);
 
        if (new->path) {
                create_symref("HEAD", new->path, msg.buf);
@@ -541,13 +547,11 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        }
 
        /*
-        * If the new thing isn't a branch and isn't HEAD and we're
-        * not starting a new branch, and we want messages, and we
-        * weren't on a branch, and we're moving to a new commit,
-        * describe the old commit.
+        * If we were on a detached HEAD, but we are now moving to
+        * a new commit, we want to mention the old commit once more
+        * to remind the user that it might be lost.
         */
-       if (!new->path && strcmp(new->name, "HEAD") && !opts->new_branch &&
-           !opts->quiet && !old.path && new->commit != old.commit)
+       if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
                describe_detached_head("Previous HEAD position was", old.commit);
 
        if (!old.commit) {
@@ -604,19 +608,33 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
        git_config(git_checkout_config, NULL);
 
-       opts.track = git_branch_track;
+       opts.track = BRANCH_TRACK_UNSPECIFIED;
 
        argc = parse_options(argc, argv, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
+       /* --track without -b should DWIM */
+       if (0 < opts.track && !opts.new_branch) {
+               const char *argv0 = argv[0];
+               if (!argc || !strcmp(argv0, "--"))
+                       die ("--track needs a branch name");
+               if (!prefixcmp(argv0, "refs/"))
+                       argv0 += 5;
+               if (!prefixcmp(argv0, "remotes/"))
+                       argv0 += 8;
+               argv0 = strchr(argv0, '/');
+               if (!argv0 || !argv0[1])
+                       die ("Missing branch name; try -b");
+               opts.new_branch = argv0 + 1;
+       }
+
+       if (opts.track == BRANCH_TRACK_UNSPECIFIED)
+               opts.track = git_branch_track;
        if (conflict_style) {
                opts.merge = 1; /* implied */
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
        }
 
-       if (!opts.new_branch && (opts.track != git_branch_track))
-               die("git checkout: --track and --no-track require -b");
-
        if (opts.force && opts.merge)
                die("git checkout: -f and -m are incompatible");
 
@@ -713,6 +731,17 @@ no_reference:
                return checkout_paths(source_tree, pathspec, &opts);
        }
 
+       if (opts.new_branch) {
+               struct strbuf buf = STRBUF_INIT;
+               strbuf_addstr(&buf, "refs/heads/");
+               strbuf_addstr(&buf, opts.new_branch);
+               if (!get_sha1(buf.buf, rev))
+                       die("git checkout: branch %s already exists", opts.new_branch);
+               if (check_ref_format(buf.buf))
+                       die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
+               strbuf_release(&buf);
+       }
+
        if (new.name && !new.commit) {
                die("Cannot switch branch to a non-commit.");
        }