i18n: unpack-trees: avoid substituting only a verb in sentences
[gitweb.git] / builtin / checkout.c
index 8129eee837d68df4238c898829a945a6145f71c3..efcbd8f6b5e0b7d551c88c02b6d7d3069a47944d 100644 (file)
@@ -1,5 +1,5 @@
-#include "cache.h"
 #include "builtin.h"
+#include "lockfile.h"
 #include "parse-options.h"
 #include "refs.h"
 #include "commit.h"
 #include "xdiff-interface.h"
 #include "ll-merge.h"
 #include "resolve-undo.h"
+#include "submodule-config.h"
 #include "submodule.h"
-#include "argv-array.h"
 
 static const char * const checkout_usage[] = {
-       N_("git checkout [options] <branch>"),
-       N_("git checkout [options] [<branch>] -- <file>..."),
+       N_("git checkout [<options>] <branch>"),
+       N_("git checkout [<options>] [<branch>] -- <file>..."),
        NULL,
 };
 
@@ -36,6 +36,8 @@ struct checkout_opts {
        int writeout_stage;
        int overwrite_ignore;
        int ignore_skipworktree;
+       int ignore_other_worktrees;
+       int show_progress;
 
        const char *new_branch;
        const char *new_branch_force;
@@ -53,16 +55,16 @@ struct checkout_opts {
 static int post_checkout_hook(struct commit *old, struct commit *new,
                              int changed)
 {
-       return run_hook(NULL, "post-checkout",
-                       sha1_to_hex(old ? old->object.sha1 : null_sha1),
-                       sha1_to_hex(new ? new->object.sha1 : null_sha1),
-                       changed ? "1" : "0", NULL);
+       return run_hook_le(NULL, "post-checkout",
+                          sha1_to_hex(old ? old->object.oid.hash : null_sha1),
+                          sha1_to_hex(new ? new->object.oid.hash : null_sha1),
+                          changed ? "1" : "0", NULL);
        /* "new" can be NULL when checking out from the index before
           a commit exists. */
 
 }
 
-static int update_some(const unsigned char *sha1, const char *base, int baselen,
+static int update_some(const unsigned char *sha1, struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
 {
        int len;
@@ -72,11 +74,11 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
        if (S_ISDIR(mode))
                return READ_TREE_RECURSIVE;
 
-       len = baselen + strlen(pathname);
+       len = base->len + strlen(pathname);
        ce = xcalloc(1, cache_entry_size(len));
        hashcpy(ce->sha1, sha1);
-       memcpy(ce->name, base, baselen);
-       memcpy(ce->name + baselen, pathname, len - baselen);
+       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;
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
@@ -243,7 +245,6 @@ static int checkout_paths(const struct checkout_opts *opts,
        int flag;
        struct commit *head;
        int errs = 0;
-       int newfd;
        struct lock_file *lock_file;
 
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
@@ -274,14 +275,14 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       newfd = hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, 1);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("corrupt index file"));
 
        if (opts->source_tree)
                read_tree_some(opts->source_tree, &opts->pathspec);
 
-       ps_matched = xcalloc(1, opts->pathspec.nr);
+       ps_matched = xcalloc(opts->pathspec.nr, 1);
 
        /*
         * Make sure all pathspecs participated in locating the paths
@@ -315,8 +316,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                 * match_pathspec() for _all_ entries when
                 * opts->source_tree != NULL.
                 */
-               if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce),
-                                  0, ps_matched))
+               if (ce_path_match(ce, &opts->pathspec, ps_matched))
                        ce->ce_flags |= CE_MATCHED;
        }
 
@@ -356,6 +356,7 @@ static int checkout_paths(const struct checkout_opts *opts,
        memset(&state, 0, sizeof(state));
        state.force = 1;
        state.refresh_cache = 1;
+       state.istate = &the_index;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
@@ -371,11 +372,10 @@ static int checkout_paths(const struct checkout_opts *opts,
                }
        }
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
-       read_ref_full("HEAD", rev, 0, &flag);
+       read_ref_full("HEAD", 0, rev, &flag);
        head = lookup_commit_reference_gently(rev, 1);
 
        errs |= post_checkout_hook(head, head, 0);
@@ -398,10 +398,10 @@ static void show_local_changes(struct object *head,
 static void describe_detached_head(const char *msg, struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
-       parse_commit(commit);
-       pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
+       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 +418,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);
@@ -443,6 +443,11 @@ struct branch_info {
        const char *name; /* The short name used */
        const char *path; /* The full name of a real branch */
        struct commit *commit; /* The named commit */
+       /*
+        * if not null the branch is detached because it's already
+        * checked out in this checkout
+        */
+       char *checkout;
 };
 
 static void setup_branch_path(struct branch_info *branch)
@@ -463,8 +468,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 {
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-       int newfd = hold_locked_index(lock_file, 1);
 
+       hold_locked_index(lock_file, 1);
        if (read_cache_preload(NULL) < 0)
                return error(_("corrupt index file"));
 
@@ -497,7 +502,7 @@ 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));
@@ -505,10 +510,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        setup_standard_excludes(topts.dir);
                }
                tree = parse_tree_indirect(old->commit ?
-                                          old->commit->object.sha1 :
+                                          old->commit->object.oid.hash :
                                           EMPTY_TREE_SHA1_BIN);
                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.hash);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
 
                ret = unpack_trees(2, trees, &topts);
@@ -572,8 +577,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
                }
        }
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (!active_cache_tree)
+               active_cache_tree = cache_tree();
+
+       if (!cache_tree_fully_valid(active_cache_tree))
+               cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        if (!opts->force && !opts->quiet)
@@ -602,19 +612,20 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        if (opts->new_branch) {
                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;
+                               int ret;
+                               char *refname;
+                               struct strbuf err = STRBUF_INIT;
+
+                               refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
+                               ret = safe_create_reflog(refname, 1, &err);
+                               free(refname);
+                               if (ret) {
+                                       fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
+                                               opts->new_orphan_branch, err.buf);
+                                       strbuf_release(&err);
                                        return;
                                }
-                               log_all_ref_updates = temp;
+                               strbuf_release(&err);
                        }
                }
                else
@@ -630,7 +641,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)
@@ -642,15 +653,16 @@ 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,
-                          REF_NODEREF, DIE_ON_ERR);
+               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)
                                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)
@@ -670,12 +682,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                        }
                }
                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);
+                       if (!ref_exists(old->path) && reflog_exists(old->path))
+                               delete_reflog(old->path);
                }
        }
        remove_branch_state();
@@ -686,21 +694,21 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 }
 
 static int add_pending_uninteresting_ref(const char *refname,
-                                        const unsigned char *sha1,
+                                        const struct object_id *oid,
                                         int flags, void *cb_data)
 {
-       add_pending_sha1(cb_data, refname, sha1, UNINTERESTING);
+       add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
        return 0;
 }
 
 static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
 {
-       parse_commit(commit);
        strbuf_addstr(sb, "  ");
        strbuf_addstr(sb,
-               find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+               find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
        strbuf_addch(sb, ' ');
-       pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
+       if (!parse_commit(commit))
+               pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
        strbuf_addch(sb, '\n');
 }
 
@@ -744,11 +752,18 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
 
        if (advice_detached_head)
                fprintf(stderr,
-                       _(
+                       Q_(
+                       /* The singular version */
+                       "If you want to keep it by creating a new branch, "
+                       "this may be a good time\nto do so with:\n\n"
+                       " git branch <new-branch-name> %s\n\n",
+                       /* The plural version */
                        "If you want to keep them by creating a new branch, "
                        "this may be a good time\nto do so with:\n\n"
-                       " git branch new_branch_name %s\n\n"),
-                       find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+                       " git branch <new-branch-name> %s\n\n",
+                       /* Give ngettext() the count */
+                       lost),
+                       find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
 }
 
 /*
@@ -766,10 +781,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_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING);
 
        refs = revs.pending;
        revs.leak_pending = 1;
@@ -794,20 +809,20 @@ static int switch_branches(const struct checkout_opts *opts,
        unsigned char rev[20];
        int flag, writeout_error = 0;
        memset(&old, 0, sizeof(old));
-       old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
+       old.path = path_to_free = resolve_refdup("HEAD", 0, rev, &flag);
        old.commit = lookup_commit_reference_gently(rev, 1);
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
 
-       if (old.path && !prefixcmp(old.path, "refs/heads/"))
-               old.name = old.path + strlen("refs/heads/");
+       if (old.path)
+               skip_prefix(old.path, "refs/heads/", &old.name);
 
        if (!new->name) {
                new->name = "HEAD";
                new->commit = old.commit;
                if (!new->commit)
                        die(_("You are on a branch yet to be born"));
-               parse_commit(new->commit);
+               parse_commit_or_die(new->commit);
        }
 
        ret = merge_working_tree(opts, &old, new, &writeout_error);
@@ -834,7 +849,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
 
        return git_xmerge_config(var, value, NULL);
@@ -884,10 +899,11 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
 static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
                                struct branch_info *new,
-                               struct tree **source_tree,
-                               unsigned char rev[20],
-                               const char **new_branch)
+                               struct checkout_opts *opts,
+                               unsigned char rev[20])
 {
+       struct tree **source_tree = &opts->source_tree;
+       const char **new_branch = &opts->new_branch;
        int argcount = 0;
        unsigned char branch_rev[20];
        const char *arg;
@@ -914,7 +930,7 @@ static int parse_branchname_arg(int argc, const char **argv,
         *       between A and B, A...B names that merge base.
         *
         *   (b) If <something> is _not_ a commit, either "--" is present
-        *       or <something> is not a path, no -t nor -b was given, and
+        *       or <something> is not a path, no -t or -b was given, and
         *       and there is a tracking branch whose name is <something>
         *       in one and only one remote, then this is a short-hand to
         *       fork local <something> from that remote-tracking branch.
@@ -966,7 +982,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(NULL, arg) || !no_wildcard(arg)))
                        recover_with_dwim = 0;
                /*
                 * Accept "git checkout foo" and "git checkout foo --"
@@ -1013,7 +1030,7 @@ static int parse_branchname_arg(int argc, const char **argv,
                /* not a commit */
                *source_tree = parse_tree_indirect(rev);
        } else {
-               parse_commit(new->commit);
+               parse_commit_or_die(new->commit);
                *source_tree = new->commit->tree;
        }
 
@@ -1087,11 +1104,22 @@ 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 &&
+           !opts->ignore_other_worktrees) {
+               unsigned char sha1[20];
+               int flag;
+               char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
+               if (head_ref &&
+                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
+                       die_if_checked_out(new->path);
+               free(head_ref);
+       }
+
        if (!new->commit && opts->new_branch) {
                unsigned char rev[20];
                int flag;
 
-               if (!read_ref_full("HEAD", rev, 0, &flag) &&
+               if (!read_ref_full("HEAD", 0, rev, &flag) &&
                    (flag & REF_ISSYMREF) && is_null_sha1(rev))
                        return switch_unborn_to_new_branch(opts);
        }
@@ -1114,7 +1142,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the 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")),
+               OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
                OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"),
                            2),
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
@@ -1128,7 +1156,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
                         N_("do not limit pathspecs to sparse entries only")),
                OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
-                               N_("second guess 'git checkout no-such-branch'")),
+                               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")),
+               OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
                OPT_END(),
        };
 
@@ -1136,6 +1167,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);
@@ -1145,6 +1177,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
+       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 */
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
@@ -1169,10 +1208,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                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;
+               skip_prefix(argv0, "refs/", &argv0);
+               skip_prefix(argv0, "remotes/", &argv0);
                argv0 = strchr(argv0, '/');
                if (!argv0 || !argv0[1])
                        die (_("Missing branch name; try -b"));
@@ -1200,8 +1237,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                        opts.track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts.new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
-                                            &new, &opts.source_tree,
-                                            rev, &opts.new_branch);
+                                            &new, &opts, rev);
                argv += n;
                argc -= n;
        }