i18n: unpack-trees: avoid substituting only a verb in sentences
[gitweb.git] / builtin / checkout.c
index 02d78baae08461bdfcb82c45a979e64dbe9f4ab5..efcbd8f6b5e0b7d551c88c02b6d7d3069a47944d 100644 (file)
 #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[] = {
-       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 +37,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;
@@ -54,31 +56,49 @@ 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),
+                          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;
        struct cache_entry *ce;
+       int pos;
 
        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);
+
+       /*
+        * If the entry is the same as the current index, we can leave the old
+        * entry in place. Whether it is UPTODATE or not, checkout_entry will
+        * do the right thing.
+        */
+       pos = cache_name_pos(ce->name, ce->ce_namelen);
+       if (pos >= 0) {
+               struct cache_entry *old = active_cache[pos];
+               if (ce->ce_mode == old->ce_mode &&
+                   !hashcmp(ce->sha1, old->sha1)) {
+                       old->ce_flags |= CE_UPDATE;
+                       free(ce);
+                       return 0;
+               }
+       }
+
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
 }
@@ -262,7 +282,7 @@ static int checkout_paths(const struct checkout_opts *opts,
        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
@@ -381,7 +401,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);
 }
 
@@ -398,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);
@@ -482,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));
@@ -490,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);
@@ -592,22 +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;
-                               struct strbuf log_file = STRBUF_INIT;
                                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);
+                               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'\n"),
-                                           opts->new_orphan_branch);
+                                       fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
+                                               opts->new_orphan_branch, err.buf);
+                                       strbuf_release(&err);
                                        return;
                                }
+                               strbuf_release(&err);
                        }
                }
                else
@@ -623,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)
@@ -635,7 +653,7 @@ 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)
@@ -643,7 +661,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                        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)
@@ -675,10 +694,10 @@ 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;
 }
 
@@ -686,7 +705,7 @@ 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));
+               find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
        strbuf_addch(sb, ' ');
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
@@ -733,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));
 }
 
 /*
@@ -755,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;
@@ -956,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 --"
@@ -1129,9 +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(),
        };
 
@@ -1139,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);
@@ -1148,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);