Code

compat/mingw: Support a timeout in the poll emulation if no fds are given
[git.git] / builtin-checkout.c
index f6f8f086de980b5bc19ca59985a2b0d8201831d9..075667c9cc8aabe4157900635b2aabc4f55f448c 100644 (file)
@@ -76,6 +76,15 @@ static int read_tree_some(struct tree *tree, const char **pathspec)
        return 0;
 }
 
+static int skip_same_name(struct cache_entry *ce, int pos)
+{
+       while (++pos < active_nr &&
+              !strcmp(active_cache[pos]->name, ce->name))
+               ; /* skip */
+       return pos;
+}
+
+
 static int checkout_paths(struct tree *source_tree, const char **pathspec)
 {
        int pos;
@@ -107,6 +116,20 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
        if (report_path_error(ps_matched, pathspec, 0))
                return 1;
 
+       /* Any unmerged paths? */
+       for (pos = 0; pos < active_nr; pos++) {
+               struct cache_entry *ce = active_cache[pos];
+               if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+                       if (!ce_stage(ce))
+                               continue;
+                       errs = 1;
+                       error("path '%s' is unmerged", ce->name);
+                       pos = skip_same_name(ce, pos) - 1;
+               }
+       }
+       if (errs)
+               return 1;
+
        /* Now we are committed to check them out */
        memset(&state, 0, sizeof(state));
        state.force = 1;
@@ -114,7 +137,11 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                if (pathspec_match(pathspec, NULL, ce->name, 0)) {
-                       errs |= checkout_entry(ce, &state, NULL);
+                       if (!ce_stage(ce)) {
+                               errs |= checkout_entry(ce, &state, NULL);
+                               continue;
+                       }
+                       pos = skip_same_name(ce, pos) - 1;
                }
        }
 
@@ -157,7 +184,7 @@ struct checkout_opts {
        int force;
        int writeout_error;
 
-       char *new_branch;
+       const char *new_branch;
        int new_branch_log;
        enum branch_track track;
 };
@@ -242,6 +269,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;
@@ -264,6 +293,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);
@@ -282,13 +312,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;
@@ -299,7 +333,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;
@@ -435,13 +469,28 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       opts.track = git_branch_track;
+       opts.track = BRANCH_TRACK_UNSPECIFIED;
 
        argc = parse_options(argc, argv, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
-       if (!opts.new_branch && (opts.track != git_branch_track))
-               die("git checkout: --track and --no-track require -b");
+       /* --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 (opts.force && opts.merge)
                die("git checkout: -f and -m are incompatible");
@@ -536,6 +585,18 @@ no_reference:
                return checkout_paths(source_tree, pathspec);
        }
 
+       if (opts.new_branch) {
+               struct strbuf buf;
+               strbuf_init(&buf, 0);
+               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.");
        }