diff options
Diffstat (limited to 'builtin/checkout.c')
-rw-r--r-- | builtin/checkout.c | 129 |
1 files changed, 72 insertions, 57 deletions
diff --git a/builtin/checkout.c b/builtin/checkout.c index e79003f5bf..d287ee6e48 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -22,8 +22,8 @@ #include "argv-array.h" static const char * const checkout_usage[] = { - "git checkout [options] <branch>", - "git checkout [options] [<branch>] -- <file>...", + N_("git checkout [options] <branch>"), + N_("git checkout [options] [<branch>] -- <file>..."), NULL, }; @@ -34,6 +34,7 @@ struct checkout_opts { int force_detach; int writeout_stage; int writeout_error; + int overwrite_ignore; /* not set by parse_options */ int branch_exists; @@ -72,7 +73,8 @@ 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_UPDATE; + ce->ce_flags = create_ce_flags(0) | CE_UPDATE; + ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); return 0; @@ -300,7 +302,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, commit_locked_index(lock_file)) die(_("unable to write new index file")); - resolve_ref("HEAD", rev, 0, &flag); + read_ref_full("HEAD", rev, 0, &flag); head = lookup_commit_reference_gently(rev, 1); errs |= post_checkout_hook(head, head, 0); @@ -314,8 +316,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts) init_revisions(&rev, NULL); rev.diffopt.flags = opts->flags; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; - if (diff_setup_done(&rev.diffopt) < 0) - die(_("diff_setup_done failed")); + diff_setup_done(&rev.diffopt); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); } @@ -342,7 +343,7 @@ static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) opts.reset = 1; opts.merge = 1; opts.fn = oneway_merge; - opts.verbose_update = !o->quiet; + opts.verbose_update = !o->quiet && isatty(2); opts.src_index = &the_index; opts.dst_index = &the_index; parse_tree(tree); @@ -419,11 +420,13 @@ static int merge_working_tree(struct checkout_opts *opts, topts.update = 1; topts.merge = 1; topts.gently = opts->merge && old->commit; - topts.verbose_update = !opts->quiet; + topts.verbose_update = !opts->quiet && isatty(2); topts.fn = twoway_merge; - topts.dir = xcalloc(1, sizeof(*topts.dir)); - topts.dir->flags |= DIR_SHOW_IGNORED; - setup_standard_excludes(topts.dir); + 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 ? old->commit->object.sha1 : EMPTY_TREE_SHA1_BIN); @@ -511,20 +514,6 @@ static void report_tracking(struct branch_info *new) strbuf_release(&sb); } -static void detach_advice(const char *old_path, const char *new_name) -{ - const char fmt[] = - "Note: checking out '%s'.\n\n" - "You are in 'detached HEAD' state. You can look around, make experimental\n" - "changes and commit them, and you can discard any commits you make in this\n" - "state without impacting any branches by performing another checkout.\n\n" - "If you want to create a new branch to retain commits you create, you may\n" - "do so (now or later) by using -b with the checkout command again. Example:\n\n" - " git checkout -b new_branch_name\n\n"; - - fprintf(stderr, fmt, new_name); -} - static void update_refs_for_switch(struct checkout_opts *opts, struct branch_info *old, struct branch_info *new) @@ -554,6 +543,7 @@ static void update_refs_for_switch(struct checkout_opts *opts, opts->new_branch_force ? 1 : 0, opts->new_branch_log, opts->new_branch_force ? 1 : 0, + opts->quiet, opts->track); new->name = opts->new_branch; setup_branch_path(new); @@ -572,7 +562,7 @@ static void update_refs_for_switch(struct checkout_opts *opts, REF_NODEREF, DIE_ON_ERR); if (!opts->quiet) { if (old->path && advice_detached_head) - detach_advice(old->path, new->name); + detach_advice(new->name); describe_detached_head(_("HEAD is now at"), new->commit); } } else if (new->path) { /* Switch branches. */ @@ -615,7 +605,7 @@ static int add_pending_uninteresting_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { - add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING); + add_pending_sha1(cb_data, refname, sha1, UNINTERESTING); return 0; } @@ -682,10 +672,10 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs) * HEAD. If it is not reachable from any ref, this is the last chance * for the user to do so without resorting to reflog. */ -static void orphaned_commit_warning(struct commit *commit) +static void orphaned_commit_warning(struct commit *old, struct commit *new) { struct rev_info revs; - struct object *object = &commit->object; + struct object *object = &old->object; struct object_array refs; init_revisions(&revs, NULL); @@ -695,16 +685,17 @@ static void orphaned_commit_warning(struct commit *commit) add_pending_object(&revs, object, sha1_to_hex(object->sha1)); for_each_ref(add_pending_uninteresting_ref, &revs); + add_pending_sha1(&revs, "HEAD", new->object.sha1, UNINTERESTING); refs = revs.pending; revs.leak_pending = 1; if (prepare_revision_walk(&revs)) die(_("internal error in revision walk")); - if (!(commit->object.flags & UNINTERESTING)) - suggest_reattach(commit, &revs); + if (!(old->object.flags & UNINTERESTING)) + suggest_reattach(old, &revs); else - describe_detached_head(_("Previous HEAD position was"), commit); + describe_detached_head(_("Previous HEAD position was"), old); clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS); free(refs.objects); @@ -714,15 +705,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) { int ret = 0; struct branch_info old; + void *path_to_free; unsigned char rev[20]; int flag; memset(&old, 0, sizeof(old)); - old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag)); + old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag); old.commit = lookup_commit_reference_gently(rev, 1); - if (!(flag & REF_ISSYMREF)) { - free((char *)old.path); + if (!(flag & REF_ISSYMREF)) old.path = NULL; - } if (old.path && !prefixcmp(old.path, "refs/heads/")) old.name = old.path + strlen("refs/heads/"); @@ -736,16 +726,18 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) } ret = merge_working_tree(opts, &old, new); - if (ret) + if (ret) { + free(path_to_free); return ret; + } if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) - orphaned_commit_warning(old.commit); + orphaned_commit_warning(old.commit, new->commit); update_refs_for_switch(opts, &old, new); ret = post_checkout_hook(old.commit, new->commit, 1); - free((char *)old.path); + free(path_to_free); return ret || opts->writeout_error; } @@ -884,7 +876,7 @@ static int parse_branchname_arg(int argc, const char **argv, setup_branch_path(new); if (!check_refname_format(new->path, 0) && - resolve_ref(new->path, branch_rev, 1, NULL)) + !read_ref(new->path, branch_rev)) hashcpy(rev, branch_rev); else new->path = NULL; /* not an existing branch */ @@ -918,6 +910,19 @@ static int parse_branchname_arg(int argc, const char **argv, return argcount; } +static int switch_unborn_to_new_branch(struct checkout_opts *opts) +{ + int status; + struct strbuf branch_ref = STRBUF_INIT; + + if (!opts->new_branch) + die(_("You are on a branch yet to be born")); + strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch); + status = create_symref("HEAD", branch_ref.buf, "checkout -b"); + strbuf_release(&branch_ref); + return status; +} + int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; @@ -928,33 +933,35 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) int patch_mode = 0; int dwim_new_local_branch = 1; struct option options[] = { - OPT__QUIET(&opts.quiet, "suppress progress reporting"), - OPT_STRING('b', NULL, &opts.new_branch, "branch", - "create and checkout a new branch"), - OPT_STRING('B', NULL, &opts.new_branch_force, "branch", - "create/reset and checkout a branch"), - OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"), - OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"), - OPT_SET_INT('t', "track", &opts.track, "set upstream info for new branch", + OPT__QUIET(&opts.quiet, N_("suppress progress reporting")), + OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), + N_("create and checkout a new branch")), + OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"), + N_("create/reset and checkout a branch")), + OPT_BOOLEAN('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")), + OPT_BOOLEAN(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, "new branch", "new unparented branch"), - OPT_SET_INT('2', "ours", &opts.writeout_stage, "checkout our version for unmerged files", + 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, "checkout their version for unmerged files", + OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"), 3), - OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"), - OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"), - OPT_STRING(0, "conflict", &conflict_style, "style", - "conflict style (merge or diff3)"), - OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), + OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")), + OPT_BOOLEAN('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), + OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")), + OPT_STRING(0, "conflict", &conflict_style, N_("style"), + N_("conflict style (merge or diff3)")), + OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")), { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL, - "second guess 'git checkout no-such-branch'", + N_("second guess 'git checkout no-such-branch'"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_END(), }; memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); + opts.overwrite_ignore = 1; gitmodules_config(); git_config(git_checkout_config, &opts); @@ -1087,5 +1094,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (opts.writeout_stage) die(_("--ours/--theirs is incompatible with switching branches.")); + if (!new.commit && opts.new_branch) { + unsigned char rev[20]; + int flag; + + if (!read_ref_full("HEAD", rev, 0, &flag) && + (flag & REF_ISSYMREF) && is_null_sha1(rev)) + return switch_unborn_to_new_branch(&opts); + } return switch_branches(&opts, &new); } |