trailers: introduce struct new_trailer_item
[gitweb.git] / builtin / checkout.c
index 3b6e4999299484d30a41e0d787086a367ee70e5c..9661e1bcba31ffa4f7b8a2fb1a9d2060cb8efda7 100644 (file)
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "config.h"
 #include "lockfile.h"
 #include "parse-options.h"
 #include "refs.h"
@@ -18,6 +19,7 @@
 #include "xdiff-interface.h"
 #include "ll-merge.h"
 #include "resolve-undo.h"
+#include "submodule-config.h"
 #include "submodule.h"
 
 static const char * const checkout_usage[] = {
@@ -36,6 +38,7 @@ struct checkout_opts {
        int overwrite_ignore;
        int ignore_skipworktree;
        int ignore_other_worktrees;
+       int show_progress;
 
        const char *new_branch;
        const char *new_branch_force;
@@ -48,16 +51,14 @@ struct checkout_opts {
        const char *prefix;
        struct pathspec pathspec;
        struct tree *source_tree;
-
-       int new_worktree_mode;
 };
 
 static int post_checkout_hook(struct commit *old, struct commit *new,
                              int changed)
 {
        return run_hook_le(NULL, "post-checkout",
-                          sha1_to_hex(old ? old->object.sha1 : null_sha1),
-                          sha1_to_hex(new ? new->object.sha1 : null_sha1),
+                          oid_to_hex(old ? &old->object.oid : &null_oid),
+                          oid_to_hex(new ? &new->object.oid : &null_oid),
                           changed ? "1" : "0", NULL);
        /* "new" can be NULL when checking out from the index before
           a commit exists. */
@@ -76,7 +77,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base,
 
        len = base->len + strlen(pathname);
        ce = xcalloc(1, cache_entry_size(len));
-       hashcpy(ce->sha1, sha1);
+       hashcpy(ce->oid.hash, sha1);
        memcpy(ce->name, base->buf, base->len);
        memcpy(ce->name + base->len, pathname, len - base->len);
        ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
@@ -92,7 +93,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base,
        if (pos >= 0) {
                struct cache_entry *old = active_cache[pos];
                if (ce->ce_mode == old->ce_mode &&
-                   !hashcmp(ce->sha1, old->sha1)) {
+                   !oidcmp(&ce->oid, &old->oid)) {
                        old->ce_flags |= CE_UPDATE;
                        free(ce);
                        return 0;
@@ -154,8 +155,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
        return 0;
 }
 
-static int checkout_stage(int stage, struct cache_entry *ce, int pos,
-                         struct checkout *state)
+static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
+                         const struct checkout *state)
 {
        while (pos < active_nr &&
               !strcmp(active_cache[pos]->name, ce->name)) {
@@ -169,15 +170,15 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos,
                return error(_("path '%s' does not have their version"), ce->name);
 }
 
-static int checkout_merged(int pos, struct checkout *state)
+static int checkout_merged(int pos, const struct checkout *state)
 {
        struct cache_entry *ce = active_cache[pos];
        const char *path = ce->name;
        mmfile_t ancestor, ours, theirs;
        int status;
-       unsigned char sha1[20];
+       struct object_id oid;
        mmbuffer_t result_buf;
-       unsigned char threeway[3][20];
+       struct object_id threeway[3];
        unsigned mode = 0;
 
        memset(threeway, 0, sizeof(threeway));
@@ -186,18 +187,18 @@ static int checkout_merged(int pos, struct checkout *state)
                stage = ce_stage(ce);
                if (!stage || strcmp(path, ce->name))
                        break;
-               hashcpy(threeway[stage - 1], ce->sha1);
+               oidcpy(&threeway[stage - 1], &ce->oid);
                if (stage == 2)
                        mode = create_ce_mode(ce->ce_mode);
                pos++;
                ce = active_cache[pos];
        }
-       if (is_null_sha1(threeway[1]) || is_null_sha1(threeway[2]))
+       if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
                return error(_("path '%s' does not have necessary versions"), path);
 
-       read_mmblob(&ancestor, threeway[0]);
-       read_mmblob(&ours, threeway[1]);
-       read_mmblob(&theirs, threeway[2]);
+       read_mmblob(&ancestor, &threeway[0]);
+       read_mmblob(&ours, &threeway[1]);
+       read_mmblob(&theirs, &threeway[2]);
 
        /*
         * NEEDSWORK: re-create conflicts from merges with
@@ -216,22 +217,24 @@ static int checkout_merged(int pos, struct checkout *state)
        /*
         * NEEDSWORK:
         * There is absolutely no reason to write this as a blob object
-        * and create a phony cache entry just to leak.  This hack is
-        * primarily to get to the write_entry() machinery that massages
-        * the contents to work-tree format and writes out which only
-        * allows it for a cache entry.  The code in write_entry() needs
-        * to be refactored to allow us to feed a <buffer, size, mode>
-        * instead of a cache entry.  Such a refactoring would help
-        * merge_recursive as well (it also writes the merge result to the
-        * object database even when it may contain conflicts).
+        * and create a phony cache entry.  This hack is primarily to get
+        * to the write_entry() machinery that massages the contents to
+        * work-tree format and writes out which only allows it for a
+        * cache entry.  The code in write_entry() needs to be refactored
+        * to allow us to feed a <buffer, size, mode> instead of a cache
+        * entry.  Such a refactoring would help merge_recursive as well
+        * (it also writes the merge result to the object database even
+        * when it may contain conflicts).
         */
        if (write_sha1_file(result_buf.ptr, result_buf.size,
-                           blob_type, sha1))
+                           blob_type, oid.hash))
                die(_("Unable to add merge result for '%s'"), path);
-       ce = make_cache_entry(mode, sha1, path, 2, 0);
+       free(result_buf.ptr);
+       ce = make_cache_entry(mode, oid.hash, path, 2, 0);
        if (!ce)
                die(_("make_cache_entry failed for path '%s'"), path);
        status = checkout_entry(ce, state, NULL);
+       free(ce);
        return status;
 }
 
@@ -239,10 +242,9 @@ static int checkout_paths(const struct checkout_opts *opts,
                          const char *revision)
 {
        int pos;
-       struct checkout state;
+       struct checkout state = CHECKOUT_INIT;
        static char *ps_matched;
-       unsigned char rev[20];
-       int flag;
+       struct object_id rev;
        struct commit *head;
        int errs = 0;
        struct lock_file *lock_file;
@@ -275,9 +277,9 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(&opts->pathspec) < 0)
-               return error(_("corrupt index file"));
+               return error(_("index file corrupt"));
 
        if (opts->source_tree)
                read_tree_some(opts->source_tree, &opts->pathspec);
@@ -353,7 +355,6 @@ static int checkout_paths(const struct checkout_opts *opts,
                return 1;
 
        /* Now we are committed to check them out */
-       memset(&state, 0, sizeof(state));
        state.force = 1;
        state.refresh_cache = 1;
        state.istate = &the_index;
@@ -375,8 +376,8 @@ static int checkout_paths(const struct checkout_opts *opts,
        if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
-       read_ref_full("HEAD", 0, rev, &flag);
-       head = lookup_commit_reference_gently(rev, 1);
+       read_ref_full("HEAD", 0, rev.hash, NULL);
+       head = lookup_commit_reference_gently(&rev, 1);
 
        errs |= post_checkout_hook(head, head, 0);
        return errs;
@@ -401,7 +402,7 @@ static void describe_detached_head(const char *msg, struct commit *commit)
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        fprintf(stderr, "%s %s... %s\n", msg,
-               find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
+               find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
        strbuf_release(&sb);
 }
 
@@ -418,7 +419,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
        opts.reset = 1;
        opts.merge = 1;
        opts.fn = oneway_merge;
-       opts.verbose_update = !o->quiet && isatty(2);
+       opts.verbose_update = o->show_progress;
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
        parse_tree(tree);
@@ -454,7 +455,7 @@ static void setup_branch_path(struct branch_info *branch)
 {
        struct strbuf buf = STRBUF_INIT;
 
-       strbuf_branchname(&buf, branch->name);
+       strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
        if (strcmp(buf.buf, branch->name))
                branch->name = xstrdup(buf.buf);
        strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
@@ -469,9 +470,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(NULL) < 0)
-               return error(_("corrupt index file"));
+               return error(_("index file corrupt"));
 
        resolve_undo_clear();
        if (opts->force) {
@@ -502,18 +503,18 @@ static int merge_working_tree(const struct checkout_opts *opts,
                topts.update = 1;
                topts.merge = 1;
                topts.gently = opts->merge && old->commit;
-               topts.verbose_update = !opts->quiet && isatty(2);
+               topts.verbose_update = opts->show_progress;
                topts.fn = twoway_merge;
                if (opts->overwrite_ignore) {
                        topts.dir = xcalloc(1, sizeof(*topts.dir));
                        topts.dir->flags |= DIR_SHOW_IGNORED;
                        setup_standard_excludes(topts.dir);
                }
-               tree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ?
-                                          old->commit->object.sha1 :
-                                          EMPTY_TREE_SHA1_BIN);
+               tree = parse_tree_indirect(old->commit ?
+                                          &old->commit->object.oid :
+                                          &empty_tree_oid);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
-               tree = parse_tree_indirect(new->commit->object.sha1);
+               tree = parse_tree_indirect(&new->commit->object.oid);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
 
                ret = unpack_trees(2, trees, &topts);
@@ -568,10 +569,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        o.ancestor = old->name;
                        o.branch1 = new->name;
                        o.branch2 = "local";
-                       merge_trees(&o, new->commit->tree, work,
+                       ret = merge_trees(&o, new->commit->tree, work,
                                old->commit->tree, &result);
+                       if (ret < 0)
+                               exit(128);
                        ret = reset_tree(new->commit->tree, opts, 0,
                                         writeout_error);
+                       strbuf_release(&o.obuf);
                        if (ret)
                                return ret;
                }
@@ -611,27 +615,28 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        const char *old_desc, *reflog_msg;
        if (opts->new_branch) {
                if (opts->new_orphan_branch) {
-                       if (opts->new_branch_log && !log_all_ref_updates) {
-                               int temp;
-                               struct strbuf log_file = STRBUF_INIT;
+                       char *refname;
+
+                       refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
+                       if (opts->new_branch_log &&
+                           !should_autocreate_reflog(refname)) {
                                int ret;
-                               const char *ref_name;
-
-                               ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
-                               temp = log_all_ref_updates;
-                               log_all_ref_updates = 1;
-                               ret = log_ref_setup(ref_name, &log_file);
-                               log_all_ref_updates = temp;
-                               strbuf_release(&log_file);
+                               struct strbuf err = STRBUF_INIT;
+
+                               ret = safe_create_reflog(refname, 1, &err);
                                if (ret) {
-                                       fprintf(stderr, _("Can not do reflog for '%s'\n"),
-                                           opts->new_orphan_branch);
+                                       fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
+                                               opts->new_orphan_branch, err.buf);
+                                       strbuf_release(&err);
+                                       free(refname);
                                        return;
                                }
+                               strbuf_release(&err);
                        }
+                       free(refname);
                }
                else
-                       create_branch(old->name, opts->new_branch, new->name,
+                       create_branch(opts->new_branch, new->name,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log,
                                      opts->new_branch_force ? 1 : 0,
@@ -643,7 +648,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 
        old_desc = old->name;
        if (!old_desc && old->commit)
-               old_desc = sha1_to_hex(old->commit->object.sha1);
+               old_desc = oid_to_hex(&old->commit->object.oid);
 
        reflog_msg = getenv("GIT_REFLOG_ACTION");
        if (!reflog_msg)
@@ -655,15 +660,17 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
                /* Nothing to do. */
        } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
-               update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
+               update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL,
                           REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
-                       if (old->path && advice_detached_head)
+                       if (old->path &&
+                           advice_detached_head && !opts->force_detach)
                                detach_advice(new->name);
                        describe_detached_head(_("HEAD is now at"), new->commit);
                }
        } else if (new->path) { /* Switch branches. */
-               create_symref("HEAD", new->path, msg.buf);
+               if (create_symref("HEAD", new->path, msg.buf) < 0)
+                       die(_("unable to update HEAD"));
                if (!opts->quiet) {
                        if (old->path && !strcmp(new->path, old->path)) {
                                if (opts->new_branch_force)
@@ -698,15 +705,14 @@ static int add_pending_uninteresting_ref(const char *refname,
                                         const struct object_id *oid,
                                         int flags, void *cb_data)
 {
-       add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
+       add_pending_oid(cb_data, refname, oid, UNINTERESTING);
        return 0;
 }
 
 static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
 {
        strbuf_addstr(sb, "  ");
-       strbuf_addstr(sb,
-               find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+       strbuf_add_unique_abbrev(sb, commit->object.oid.hash, DEFAULT_ABBREV);
        strbuf_addch(sb, ' ');
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
@@ -764,7 +770,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
                        " git branch <new-branch-name> %s\n\n",
                        /* Give ngettext() the count */
                        lost),
-                       find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+                       find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
 }
 
 /*
@@ -782,10 +788,10 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
        setup_revisions(0, NULL, &revs, NULL);
 
        object->flags &= ~UNINTERESTING;
-       add_pending_object(&revs, object, sha1_to_hex(object->sha1));
+       add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
        for_each_ref(add_pending_uninteresting_ref, &revs);
-       add_pending_sha1(&revs, "HEAD", new->object.sha1, UNINTERESTING);
+       add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
 
        refs = revs.pending;
        revs.leak_pending = 1;
@@ -807,11 +813,12 @@ static int switch_branches(const struct checkout_opts *opts,
        int ret = 0;
        struct branch_info old;
        void *path_to_free;
-       unsigned char rev[20];
+       struct object_id rev;
        int flag, writeout_error = 0;
        memset(&old, 0, sizeof(old));
-       old.path = path_to_free = resolve_refdup("HEAD", 0, rev, &flag);
-       old.commit = lookup_commit_reference_gently(rev, 1);
+       old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
+       if (old.path)
+               old.commit = lookup_commit_reference_gently(&rev, 1);
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
 
@@ -832,8 +839,7 @@ static int switch_branches(const struct checkout_opts *opts,
                return ret;
        }
 
-       if (!opts->quiet && !old.path && old.commit &&
-           new->commit != old.commit && !opts->new_worktree_mode)
+       if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
                orphaned_commit_warning(old.commit, new->commit);
 
        update_refs_for_switch(opts, &old, new);
@@ -852,7 +858,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
        }
 
        if (starts_with(var, "submodule."))
-               return parse_submodule_config_option(var, value);
+               return submodule_config(var, value, NULL);
 
        return git_xmerge_config(var, value, NULL);
 }
@@ -860,7 +866,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 struct tracking_name_data {
        /* const */ char *src_ref;
        char *dst_ref;
-       unsigned char *dst_sha1;
+       struct object_id *dst_oid;
        int unique;
 };
 
@@ -871,7 +877,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
        memset(&query, 0, sizeof(struct refspec));
        query.src = cb->src_ref;
        if (remote_find_tracking(remote, &query) ||
-           get_sha1(query.dst, cb->dst_sha1)) {
+           get_oid(query.dst, cb->dst_oid)) {
                free(query.dst);
                return 0;
        }
@@ -884,95 +890,29 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
        return 0;
 }
 
-static const char *unique_tracking_name(const char *name, unsigned char *sha1)
+static const char *unique_tracking_name(const char *name, struct object_id *oid)
 {
        struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
-       char src_ref[PATH_MAX];
-       snprintf(src_ref, PATH_MAX, "refs/heads/%s", name);
-       cb_data.src_ref = src_ref;
-       cb_data.dst_sha1 = sha1;
+       cb_data.src_ref = xstrfmt("refs/heads/%s", name);
+       cb_data.dst_oid = oid;
        for_each_remote(check_tracking_name, &cb_data);
+       free(cb_data.src_ref);
        if (cb_data.unique)
                return cb_data.dst_ref;
        free(cb_data.dst_ref);
        return NULL;
 }
 
-static void check_linked_checkout(struct branch_info *new, const char *id)
-{
-       struct strbuf sb = STRBUF_INIT;
-       struct strbuf path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
-       const char *start, *end;
-
-       if (id)
-               strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
-       else
-               strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
-
-       if (strbuf_read_file(&sb, path.buf, 0) < 0 ||
-           !skip_prefix(sb.buf, "ref:", &start))
-               goto done;
-       while (isspace(*start))
-               start++;
-       end = start;
-       while (*end && !isspace(*end))
-               end++;
-       if (strncmp(start, new->path, end - start) || new->path[end - start] != '\0')
-               goto done;
-       if (id) {
-               strbuf_reset(&path);
-               strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
-               if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
-                       goto done;
-               strbuf_rtrim(&gitdir);
-       } else
-               strbuf_addstr(&gitdir, get_git_common_dir());
-       die(_("'%s' is already checked out at '%s'"), new->name, gitdir.buf);
-done:
-       strbuf_release(&path);
-       strbuf_release(&sb);
-       strbuf_release(&gitdir);
-}
-
-static void check_linked_checkouts(struct branch_info *new)
-{
-       struct strbuf path = STRBUF_INIT;
-       DIR *dir;
-       struct dirent *d;
-
-       strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
-       if ((dir = opendir(path.buf)) == NULL) {
-               strbuf_release(&path);
-               return;
-       }
-
-       /*
-        * $GIT_COMMON_DIR/HEAD is practically outside
-        * $GIT_DIR so resolve_ref_unsafe() won't work (it
-        * uses git_path). Parse the ref ourselves.
-        */
-       check_linked_checkout(new, NULL);
-
-       while ((d = readdir(dir)) != NULL) {
-               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
-                       continue;
-               check_linked_checkout(new, d->d_name);
-       }
-       strbuf_release(&path);
-       closedir(dir);
-}
-
 static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
                                struct branch_info *new,
                                struct checkout_opts *opts,
-                               unsigned char rev[20])
+                               struct object_id *rev)
 {
        struct tree **source_tree = &opts->source_tree;
        const char **new_branch = &opts->new_branch;
        int argcount = 0;
-       unsigned char branch_rev[20];
+       struct object_id branch_rev;
        const char *arg;
        int dash_dash_pos;
        int has_dash_dash = 0;
@@ -1038,7 +978,7 @@ static int parse_branchname_arg(int argc, const char **argv,
        if (!strcmp(arg, "-"))
                arg = "@{-1}";
 
-       if (get_sha1_mb(arg, rev)) {
+       if (get_oid_mb(arg, rev)) {
                /*
                 * Either case (3) or (4), with <something> not being
                 * a commit, or an attempt to use case (1) with an
@@ -1049,7 +989,8 @@ static int parse_branchname_arg(int argc, const char **argv,
                 */
                int recover_with_dwim = dwim_new_local_branch_ok;
 
-               if (check_filename(NULL, arg) && !has_dash_dash)
+               if (!has_dash_dash &&
+                   (check_filename(opts->prefix, arg) || !no_wildcard(arg)))
                        recover_with_dwim = 0;
                /*
                 * Accept "git checkout foo" and "git checkout foo --"
@@ -1086,8 +1027,8 @@ static int parse_branchname_arg(int argc, const char **argv,
        setup_branch_path(new);
 
        if (!check_refname_format(new->path, 0) &&
-           !read_ref(new->path, branch_rev))
-               hashcpy(rev, branch_rev);
+           !read_ref(new->path, branch_rev.hash))
+               oidcpy(rev, &branch_rev);
        else
                new->path = NULL; /* not an existing branch */
 
@@ -1102,7 +1043,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 
        if (!*source_tree)                   /* case (1): want a tree */
                die(_("reference is not a tree: %s"), arg);
-       if (!has_dash_dash) {/* case (3).(d) -> (1) */
+       if (!has_dash_dash) {   /* case (3).(d) -> (1) */
                /*
                 * Do not complain the most common case
                 *      git checkout branch
@@ -1110,7 +1051,7 @@ static int parse_branchname_arg(int argc, const char **argv,
                 * it would be extremely annoying.
                 */
                if (argc)
-                       verify_non_filename(NULL, arg);
+                       verify_non_filename(opts->prefix, arg);
        } else {
                argcount++;
                argv++;
@@ -1170,23 +1111,23 @@ static int checkout_branch(struct checkout_opts *opts,
                die(_("Cannot switch branch to a non-commit '%s'"),
                    new->name);
 
-       if (new->path && !opts->force_detach && !opts->new_branch) {
-               unsigned char sha1[20];
+       if (new->path && !opts->force_detach && !opts->new_branch &&
+           !opts->ignore_other_worktrees) {
+               struct object_id oid;
                int flag;
-               char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
+               char *head_ref = resolve_refdup("HEAD", 0, oid.hash, &flag);
                if (head_ref &&
-                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
-                   !opts->ignore_other_worktrees)
-                       check_linked_checkouts(new);
+                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
+                       die_if_checked_out(new->path, 1);
                free(head_ref);
        }
 
        if (!new->commit && opts->new_branch) {
-               unsigned char rev[20];
+               struct object_id rev;
                int flag;
 
-               if (!read_ref_full("HEAD", 0, rev, &flag) &&
-                   (flag & REF_ISSYMREF) && is_null_sha1(rev))
+               if (!read_ref_full("HEAD", 0, rev.hash, &flag) &&
+                   (flag & REF_ISSYMREF) && is_null_oid(&rev))
                        return switch_unborn_to_new_branch(opts);
        }
        return switch_branches(opts, new);
@@ -1205,7 +1146,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
                           N_("create/reset and checkout a branch")),
                OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-               OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
+               OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
                OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
@@ -1225,6 +1166,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                                N_("second guess 'git checkout <no-such-branch>'")),
                OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
                         N_("do not check if another worktree is holding the given ref")),
+               { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+                           "checkout", "control recursive updating of submodules",
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+               OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
                OPT_END(),
        };
 
@@ -1232,6 +1177,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        memset(&new, 0, sizeof(new));
        opts.overwrite_ignore = 1;
        opts.prefix = prefix;
+       opts.show_progress = -1;
 
        gitmodules_config();
        git_config(git_checkout_config, &opts);
@@ -1241,7 +1187,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
-       opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
+       if (opts.show_progress < 0) {
+               if (opts.quiet)
+                       opts.show_progress = 0;
+               else
+                       opts.show_progress = isatty(2);
+       }
 
        if (conflict_style) {
                opts.merge = 1; /* implied */
@@ -1289,14 +1240,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
         * remote branches, erroring out for invalid or ambiguous cases.
         */
        if (argc) {
-               unsigned char rev[20];
+               struct object_id rev;
                int dwim_ok =
                        !opts.patch_mode &&
                        dwim_new_local_branch &&
                        opts.track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts.new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
-                                            &new, &opts, rev);
+                                            &new, &opts, &rev);
                argv += n;
                argc -= n;
        }
@@ -1314,9 +1265,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                 * new_branch && argc > 1 will be caught later.
                 */
                if (opts.new_branch && argc == 1)
-                       die(_("Cannot update paths and switch to branch '%s' at the same time.\n"
-                             "Did you intend to checkout '%s' which can not be resolved as commit?"),
-                           opts.new_branch, argv[0]);
+                       die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
+                               argv[0], opts.new_branch);
 
                if (opts.force_detach)
                        die(_("git checkout: --detach does not take a path argument '%s'"),