diff options
Diffstat (limited to 'builtin/checkout.c')
-rw-r--r-- | builtin/checkout.c | 97 |
1 files changed, 66 insertions, 31 deletions
diff --git a/builtin/checkout.c b/builtin/checkout.c index d76e13c852..516136a23a 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -4,6 +4,7 @@ #include "lockfile.h" #include "parse-options.h" #include "refs.h" +#include "object-store.h" #include "commit.h" #include "tree.h" #include "tree-walk.h" @@ -22,6 +23,7 @@ #include "resolve-undo.h" #include "submodule-config.h" #include "submodule.h" +#include "advice.h" static const char * const checkout_usage[] = { N_("git checkout [<options>] <branch>"), @@ -66,7 +68,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm } -static int update_some(const unsigned char *sha1, struct strbuf *base, +static int update_some(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { int len; @@ -77,8 +79,8 @@ static int update_some(const unsigned char *sha1, struct strbuf *base, return READ_TREE_RECURSIVE; len = base->len + strlen(pathname); - ce = xcalloc(1, cache_entry_size(len)); - hashcpy(ce->oid.hash, sha1); + ce = make_empty_cache_entry(&the_index, len); + oidcpy(&ce->oid, oid); 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; @@ -96,7 +98,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base, if (ce->ce_mode == old->ce_mode && !oidcmp(&ce->oid, &old->oid)) { old->ce_flags |= CE_UPDATE; - free(ce); + discard_cache_entry(ce); return 0; } } @@ -230,11 +232,11 @@ static int checkout_merged(int pos, const struct checkout *state) if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid)) die(_("Unable to add merge result for '%s'"), path); free(result_buf.ptr); - ce = make_cache_entry(mode, oid.hash, path, 2, 0); + ce = make_transient_cache_entry(mode, &oid, path, 2); if (!ce) die(_("make_cache_entry failed for path '%s'"), path); status = checkout_entry(ce, state, NULL); - free(ce); + discard_cache_entry(ce); return status; } @@ -378,7 +380,7 @@ static int checkout_paths(const struct checkout_opts *opts, die(_("unable to write new index file")); read_ref_full("HEAD", 0, &rev, NULL); - head = lookup_commit_reference_gently(&rev, 1); + head = lookup_commit_reference_gently(the_repository, &rev, 1); errs |= post_checkout_hook(head, head, 0); return errs; @@ -405,10 +407,10 @@ static void describe_detached_head(const char *msg, struct commit *commit) pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); if (print_sha1_ellipsis()) { fprintf(stderr, "%s %s... %s\n", msg, - find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf); + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); } else { fprintf(stderr, "%s %s %s\n", msg, - find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf); + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); } strbuf_release(&sb); } @@ -484,7 +486,8 @@ static int merge_working_tree(const struct checkout_opts *opts, resolve_undo_clear(); if (opts->force) { - ret = reset_tree(new_branch_info->commit->tree, opts, 1, writeout_error); + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 1, writeout_error); if (ret) return ret; } else { @@ -526,6 +529,7 @@ static int merge_working_tree(const struct checkout_opts *opts, init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); + clear_unpack_trees_porcelain(&topts); if (ret == -1) { /* * Unpack couldn't do a trivial merge; either @@ -570,18 +574,23 @@ static int merge_working_tree(const struct checkout_opts *opts, o.verbosity = 0; work = write_tree_from_memory(&o); - ret = reset_tree(new_branch_info->commit->tree, opts, 1, + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 1, writeout_error); if (ret) return ret; o.ancestor = old_branch_info->name; o.branch1 = new_branch_info->name; o.branch2 = "local"; - ret = merge_trees(&o, new_branch_info->commit->tree, work, - old_branch_info->commit->tree, &result); + ret = merge_trees(&o, + get_commit_tree(new_branch_info->commit), + work, + get_commit_tree(old_branch_info->commit), + &result); if (ret < 0) exit(128); - ret = reset_tree(new_branch_info->commit->tree, opts, 0, + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 0, writeout_error); strbuf_release(&o.obuf); if (ret) @@ -720,7 +729,7 @@ static int add_pending_uninteresting_ref(const char *refname, static void describe_one_orphan(struct strbuf *sb, struct commit *commit) { strbuf_addstr(sb, " "); - strbuf_add_unique_abbrev(sb, commit->object.oid.hash, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV); strbuf_addch(sb, ' '); if (!parse_commit(commit)) pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); @@ -778,7 +787,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.oid.hash, DEFAULT_ABBREV)); + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); } /* @@ -822,7 +831,7 @@ static int switch_branches(const struct checkout_opts *opts, memset(&old_branch_info, 0, sizeof(old_branch_info)); old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag); if (old_branch_info.path) - old_branch_info.commit = lookup_commit_reference_gently(&rev, 1); + old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1); if (!(flag & REF_ISSYMREF)) old_branch_info.path = NULL; @@ -871,7 +880,8 @@ static int parse_branchname_arg(int argc, const char **argv, int dwim_new_local_branch_ok, struct branch_info *new_branch_info, struct checkout_opts *opts, - struct object_id *rev) + struct object_id *rev, + int *dwim_remotes_matched) { struct tree **source_tree = &opts->source_tree; const char **new_branch = &opts->new_branch; @@ -903,8 +913,10 @@ static int parse_branchname_arg(int argc, const char **argv, * (b) If <something> is _not_ a commit, either "--" is present * 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. + * in one and only one remote (or if the branch exists on the + * remote named in checkout.defaultRemote), then this is a + * short-hand to fork local <something> from that + * remote-tracking branch. * * (c) Otherwise, if "--" is present, treat it like case (1). * @@ -965,7 +977,8 @@ static int parse_branchname_arg(int argc, const char **argv, recover_with_dwim = 0; if (recover_with_dwim) { - const char *remote = unique_tracking_name(arg, rev); + const char *remote = unique_tracking_name(arg, rev, + dwim_remotes_matched); if (remote) { *new_branch = arg; arg = remote; @@ -996,13 +1009,13 @@ static int parse_branchname_arg(int argc, const char **argv, else new_branch_info->path = NULL; /* not an existing branch */ - new_branch_info->commit = lookup_commit_reference_gently(rev, 1); + new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1); if (!new_branch_info->commit) { /* not a commit */ *source_tree = parse_tree_indirect(rev); } else { parse_commit_or_die(new_branch_info->commit); - *source_tree = new_branch_info->commit->tree; + *source_tree = get_commit_tree(new_branch_info->commit); } if (!*source_tree) /* case (1): want a tree */ @@ -1102,6 +1115,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) struct branch_info new_branch_info; char *conflict_style = NULL; int dwim_new_local_branch = 1; + int dwim_remotes_matched = 0; struct option options[] = { OPT__QUIET(&opts.quiet, N_("suppress progress reporting")), OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), @@ -1113,10 +1127,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) 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_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"), - 3), + OPT_SET_INT_F('2', "ours", &opts.writeout_stage, + N_("checkout our version for unmerged files"), + 2, PARSE_OPT_NONEG), + OPT_SET_INT_F('3', "theirs", &opts.writeout_stage, + N_("checkout their version for unmerged files"), + 3, PARSE_OPT_NONEG), OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"), PARSE_OPT_NOCOMPLETE), OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), @@ -1212,7 +1228,8 @@ 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_branch_info, &opts, &rev); + &new_branch_info, &opts, &rev, + &dwim_remotes_matched); argv += n; argc -= n; } @@ -1254,8 +1271,26 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) } UNLEAK(opts); - if (opts.patch_mode || opts.pathspec.nr) - return checkout_paths(&opts, new_branch_info.name); - else + if (opts.patch_mode || opts.pathspec.nr) { + int ret = checkout_paths(&opts, new_branch_info.name); + if (ret && dwim_remotes_matched > 1 && + advice_checkout_ambiguous_remote_branch_name) + advise(_("'%s' matched more than one remote tracking branch.\n" + "We found %d remotes with a reference that matched. So we fell back\n" + "on trying to resolve the argument as a path, but failed there too!\n" + "\n" + "If you meant to check out a remote tracking branch on, e.g. 'origin',\n" + "you can do so by fully qualifying the name with the --track option:\n" + "\n" + " git checkout --track origin/<name>\n" + "\n" + "If you'd like to always have checkouts of an ambiguous <name> prefer\n" + "one remote, e.g. the 'origin' remote, consider setting\n" + "checkout.defaultRemote=origin in your config."), + argv[0], + dwim_remotes_matched); + return ret; + } else { return checkout_branch(&opts, &new_branch_info); + } } |