Code

Merge branch 'ph/rerere-doc' into maint-1.7.8
[git.git] / builtin / checkout.c
index 31aa248c0e8f2bdba8a6c666dd8a4c2a62b63c3f..e79003f5bffc8806ea6cfd67f866c3ee4e2f5c7d 100644 (file)
@@ -19,6 +19,7 @@
 #include "ll-merge.h"
 #include "resolve-undo.h"
 #include "submodule.h"
+#include "argv-array.h"
 
 static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@ -71,7 +72,7 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, base, baselen);
        memcpy(ce->name + baselen, pathname, len - baselen);
-       ce->ce_flags = create_ce_flags(len, 0);
+       ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE;
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
@@ -79,7 +80,10 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
 
 static int read_tree_some(struct tree *tree, const char **pathspec)
 {
-       read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
+       struct pathspec ps;
+       init_pathspec(&ps, pathspec);
+       read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL);
+       free_pathspec(&ps);
 
        /* update the index with the given tree's info
         * for all args, expanding wildcards, and exit
@@ -152,7 +156,7 @@ static int checkout_merged(int pos, struct checkout *state)
        unsigned char sha1[20];
        mmbuffer_t result_buf;
        unsigned char threeway[3][20];
-       unsigned mode;
+       unsigned mode = 0;
 
        memset(threeway, 0, sizeof(threeway));
        while (pos < active_nr) {
@@ -210,7 +214,7 @@ static int checkout_merged(int pos, struct checkout *state)
 }
 
 static int checkout_paths(struct tree *source_tree, const char **pathspec,
-                         struct checkout_opts *opts)
+                         const char *prefix, struct checkout_opts *opts)
 {
        int pos;
        struct checkout state;
@@ -237,10 +241,12 @@ 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 (source_tree && !(ce->ce_flags & CE_UPDATE))
+                       continue;
                match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
        }
 
-       if (report_path_error(ps_matched, pathspec, 0))
+       if (report_path_error(ps_matched, pathspec, prefix))
                return 1;
 
        /* "checkout -m path" to recreate conflicted state */
@@ -275,6 +281,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
        state.refresh_cache = 1;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
+               if (source_tree && !(ce->ce_flags & CE_UPDATE))
+                       continue;
                if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
                        if (!ce_stage(ce)) {
                                errs |= checkout_entry(ce, &state, NULL);
@@ -315,9 +323,8 @@ static void show_local_changes(struct object *head, struct diff_options *opts)
 static void describe_detached_head(const char *msg, struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
-       struct pretty_print_context ctx = {0};
        parse_commit(commit);
-       pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, &ctx);
+       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);
        strbuf_release(&sb);
@@ -416,7 +423,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.fn = twoway_merge;
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
-               topts.dir->exclude_per_dir = ".gitignore";
+               setup_standard_excludes(topts.dir);
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           EMPTY_TREE_SHA1_BIN);
@@ -545,7 +552,9 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                else
                        create_branch(old->name, opts->new_branch, new->name,
                                      opts->new_branch_force ? 1 : 0,
-                                     opts->new_branch_log, opts->track);
+                                     opts->new_branch_log,
+                                     opts->new_branch_force ? 1 : 0,
+                                     opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
        }
@@ -570,8 +579,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                create_symref("HEAD", new->path, msg.buf);
                if (!opts->quiet) {
                        if (old->path && !strcmp(new->path, old->path)) {
-                               fprintf(stderr, _("Already on '%s'\n"),
-                                       new->name);
+                               if (opts->new_branch_force)
+                                       fprintf(stderr, _("Reset branch '%s'\n"),
+                                               new->name);
+                               else
+                                       fprintf(stderr, _("Already on '%s'\n"),
+                                               new->name);
                        } else if (opts->new_branch) {
                                if (opts->branch_exists)
                                        fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
@@ -598,48 +611,22 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                report_tracking(new);
 }
 
-struct rev_list_args {
-       int argc;
-       int alloc;
-       const char **argv;
-};
-
-static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
+static int add_pending_uninteresting_ref(const char *refname,
+                                        const unsigned char *sha1,
+                                        int flags, void *cb_data)
 {
-       ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
-       args->argv[args->argc++] = s;
-}
-
-static int add_one_ref_to_rev_list_arg(const char *refname,
-                                      const unsigned char *sha1,
-                                      int flags,
-                                      void *cb_data)
-{
-       add_one_rev_list_arg(cb_data, refname);
-       return 0;
-}
-
-static int clear_commit_marks_from_one_ref(const char *refname,
-                                     const unsigned char *sha1,
-                                     int flags,
-                                     void *cb_data)
-{
-       struct commit *commit = lookup_commit_reference_gently(sha1, 1);
-       if (commit)
-               clear_commit_marks(commit, -1);
+       add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING);
        return 0;
 }
 
 static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
 {
-       struct pretty_print_context ctx = { 0 };
-
        parse_commit(commit);
        strbuf_addstr(sb, "  ");
        strbuf_addstr(sb,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
        strbuf_addch(sb, ' ');
-       pretty_print_commit(CMIT_FMT_ONELINE, commit, sb, &ctx);
+       pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
        strbuf_addch(sb, '\n');
 }
 
@@ -660,21 +647,34 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
                if (more == 1)
                        describe_one_orphan(&sb, last);
                else
-                       strbuf_addf(&sb, " ... and %d more.\n", more);
+                       strbuf_addf(&sb, _(" ... and %d more.\n"), more);
        }
 
        fprintf(stderr,
-               "Warning: you are leaving %d commit%s behind, "
+               Q_(
+               /* The singular version */
+               "Warning: you are leaving %d commit behind, "
                "not connected to\n"
                "any of your branches:\n\n"
-               "%s\n"
-               "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",
-               lost, ((1 < lost) ? "s" : ""),
-               sb.buf,
-               sha1_to_hex(commit->object.sha1));
+               "%s\n",
+               /* The plural version */
+               "Warning: you are leaving %d commits behind, "
+               "not connected to\n"
+               "any of your branches:\n\n"
+               "%s\n",
+               /* Give ngettext() the count */
+               lost),
+               lost,
+               sb.buf);
        strbuf_release(&sb);
+
+       if (advice_detached_head)
+               fprintf(stderr,
+                       _(
+                       "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"),
+                       sha1_to_hex(commit->object.sha1));
 }
 
 /*
@@ -684,19 +684,21 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
  */
 static void orphaned_commit_warning(struct commit *commit)
 {
-       struct rev_list_args args = { 0, 0, NULL };
        struct rev_info revs;
-
-       add_one_rev_list_arg(&args, "(internal)");
-       add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
-       add_one_rev_list_arg(&args, "--not");
-       for_each_ref(add_one_ref_to_rev_list_arg, &args);
-       add_one_rev_list_arg(&args, "--");
-       add_one_rev_list_arg(&args, NULL);
+       struct object *object = &commit->object;
+       struct object_array refs;
 
        init_revisions(&revs, NULL);
-       if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
-               die(_("internal error: only -- alone should have been left"));
+       setup_revisions(0, NULL, &revs, NULL);
+
+       object->flags &= ~UNINTERESTING;
+       add_pending_object(&revs, object, sha1_to_hex(object->sha1));
+
+       for_each_ref(add_pending_uninteresting_ref, &revs);
+
+       refs = revs.pending;
+       revs.leak_pending = 1;
+
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
        if (!(commit->object.flags & UNINTERESTING))
@@ -704,8 +706,8 @@ static void orphaned_commit_warning(struct commit *commit)
        else
                describe_detached_head(_("Previous HEAD position was"), commit);
 
-       clear_commit_marks(commit, -1);
-       for_each_ref(clear_commit_marks_from_one_ref, NULL);
+       clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
+       free(refs.objects);
 }
 
 static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
@@ -715,10 +717,12 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        unsigned char rev[20];
        int flag;
        memset(&old, 0, sizeof(old));
-       old.path = resolve_ref("HEAD", rev, 0, &flag);
+       old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
        old.commit = lookup_commit_reference_gently(rev, 1);
-       if (!(flag & REF_ISSYMREF))
+       if (!(flag & REF_ISSYMREF)) {
+               free((char *)old.path);
                old.path = NULL;
+       }
 
        if (old.path && !prefixcmp(old.path, "refs/heads/"))
                old.name = old.path + strlen("refs/heads/");
@@ -741,6 +745,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        update_refs_for_switch(opts, &old, new);
 
        ret = post_checkout_hook(old.commit, new->commit, 1);
+       free((char *)old.path);
        return ret || opts->writeout_error;
 }
 
@@ -878,7 +883,7 @@ static int parse_branchname_arg(int argc, const char **argv,
        new->name = arg;
        setup_branch_path(new);
 
-       if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
+       if (!check_refname_format(new->path, 0) &&
            resolve_ref(new->path, branch_rev, 1, NULL))
                hashcpy(rev, branch_rev);
        else
@@ -973,9 +978,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                die (_("--patch is incompatible with all other options"));
 
        if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
-               die("--detach cannot be used with -b/-B/--orphan");
+               die(_("--detach cannot be used with -b/-B/--orphan"));
        if (opts.force_detach && 0 < opts.track)
-               die("--detach cannot be used with -t");
+               die(_("--detach cannot be used with -t"));
 
        /* --track without -b should DWIM */
        if (0 < opts.track && !opts.new_branch) {
@@ -1055,12 +1060,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                }
 
                if (opts.force_detach)
-                       die("git checkout: --detach does not take a path argument");
+                       die(_("git checkout: --detach does not take a path argument"));
 
                if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
                        die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
 
-               return checkout_paths(source_tree, pathspec, &opts);
+               return checkout_paths(source_tree, pathspec, prefix, &opts);
        }
 
        if (patch_mode)
@@ -1068,15 +1073,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
-               if (strbuf_check_branch_ref(&buf, opts.new_branch))
-                       die(_("git checkout: we do not like '%s' as a branch name."),
-                           opts.new_branch);
-               if (!get_sha1(buf.buf, rev)) {
-                       opts.branch_exists = 1;
-                       if (!opts.new_branch_force)
-                               die(_("git checkout: branch %s already exists"),
-                                   opts.new_branch);
-               }
+
+               opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
+                                                            !!opts.new_branch_force,
+                                                            !!opts.new_branch_force);
+
                strbuf_release(&buf);
        }