diff options
Diffstat (limited to 'third_party/git/builtin/checkout.c')
-rw-r--r-- | third_party/git/builtin/checkout.c | 137 |
1 files changed, 48 insertions, 89 deletions
diff --git a/third_party/git/builtin/checkout.c b/third_party/git/builtin/checkout.c index d6773818b803..6123f732a2c8 100644 --- a/third_party/git/builtin/checkout.c +++ b/third_party/git/builtin/checkout.c @@ -70,8 +70,6 @@ struct checkout_opts { int checkout_worktree; const char *ignore_unmerged_opt; int ignore_unmerged; - int pathspec_file_nul; - const char *pathspec_from_file; const char *new_branch; const char *new_branch_force; @@ -128,7 +126,6 @@ static int update_some(const struct object_id *oid, struct strbuf *base, if (pos >= 0) { struct cache_entry *old = active_cache[pos]; if (ce->ce_mode == old->ce_mode && - !ce_intent_to_add(old) && oideq(&ce->oid, &old->oid)) { old->ce_flags |= CE_UPDATE; discard_cache_entry(ce); @@ -524,8 +521,6 @@ static int checkout_paths(const struct checkout_opts *opts, /* Now we are committed to check them out */ if (opts->checkout_worktree) errs |= checkout_worktree(opts); - else - remove_marked_cache_entries(&the_index, 1); /* * Allow updating the index when checking out from the index. @@ -713,11 +708,11 @@ static int merge_working_tree(const struct checkout_opts *opts, * give up or do a real merge, depending on * whether the merge flag was used. */ + struct tree *result; struct tree *work; struct tree *old_tree; struct merge_options o; struct strbuf sb = STRBUF_INIT; - struct strbuf old_commit_shortname = STRBUF_INIT; if (!opts->merge) return 1; @@ -735,6 +730,13 @@ static int merge_working_tree(const struct checkout_opts *opts, "the following files:\n%s"), sb.buf); strbuf_release(&sb); + if (repo_index_has_changes(the_repository, + get_commit_tree(old_branch_info->commit), + &sb)) + warning(_("staged changes in the following files may be lost: %s"), + sb.buf); + strbuf_release(&sb); + /* Do more real merge */ /* @@ -758,7 +760,7 @@ static int merge_working_tree(const struct checkout_opts *opts, */ init_merge_options(&o, the_repository); o.verbosity = 0; - work = write_in_core_index_as_tree(the_repository); + work = write_tree_from_memory(&o); ret = reset_tree(new_tree, opts, 1, @@ -766,25 +768,19 @@ static int merge_working_tree(const struct checkout_opts *opts, if (ret) return ret; o.ancestor = old_branch_info->name; - if (old_branch_info->name == NULL) { - strbuf_add_unique_abbrev(&old_commit_shortname, - &old_branch_info->commit->object.oid, - DEFAULT_ABBREV); - o.ancestor = old_commit_shortname.buf; - } o.branch1 = new_branch_info->name; o.branch2 = "local"; ret = merge_trees(&o, new_tree, work, - old_tree); + old_tree, + &result); if (ret < 0) exit(128); ret = reset_tree(new_tree, opts, 0, writeout_error); strbuf_release(&o.obuf); - strbuf_release(&old_commit_shortname); if (ret) return ret; } @@ -865,7 +861,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts, strbuf_addf(&msg, "checkout: moving from %s to %s", old_desc ? old_desc : "(invalid)", new_branch_info->name); else - strbuf_insertstr(&msg, 0, reflog_msg); + strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg)); if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) { /* Nothing to do. */ @@ -1117,43 +1113,12 @@ static void setup_new_branch_info_and_source_tree( } } -static const char *parse_remote_branch(const char *arg, - struct object_id *rev, - int could_be_checkout_paths) -{ - int num_matches = 0; - const char *remote = unique_tracking_name(arg, rev, &num_matches); - - if (remote && could_be_checkout_paths) { - die(_("'%s' could be both a local file and a tracking branch.\n" - "Please use -- (and optionally --no-guess) to disambiguate"), - arg); - } - - if (!remote && num_matches > 1) { - if (advice_checkout_ambiguous_remote_branch_name) { - advise(_("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.")); - } - - die(_("'%s' matched multiple (%d) remote tracking branches"), - arg, num_matches); - } - - return remote; -} - 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) { const char **new_branch = &opts->new_branch; int argcount = 0; @@ -1258,9 +1223,13 @@ static int parse_branchname_arg(int argc, const char **argv, recover_with_dwim = 0; if (recover_with_dwim) { - const char *remote = parse_remote_branch(arg, rev, - could_be_checkout_paths); + const char *remote = unique_tracking_name(arg, rev, + dwim_remotes_matched); if (remote) { + if (could_be_checkout_paths) + die(_("'%s' could be both a local file and a tracking branch.\n" + "Please use -- (and optionally --no-guess) to disambiguate"), + arg); *new_branch = arg; arg = remote; /* DWIMmed to create local branch, case (3).(b) */ @@ -1511,8 +1480,6 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree, N_("do not limit pathspecs to sparse entries only")), - OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file), - OPT_PATHSPEC_FILE_NUL(&opts->pathspec_file_nul), OPT_END() }; struct option *newopts = parse_options_concat(prevopts, options); @@ -1525,6 +1492,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, const char * const usagestr[]) { struct branch_info new_branch_info; + int dwim_remotes_matched = 0; int parseopt_flags = 0; memset(&new_branch_info, 0, sizeof(new_branch_info)); @@ -1632,7 +1600,8 @@ static int checkout_main(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; } else if (!opts->accept_ref && opts->from_treeish) { @@ -1649,6 +1618,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix, die(_("reference is not a tree: %s"), opts->from_treeish); } + if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc && + !opts->patch_mode) /* patch mode is special */ + die(_("you must specify path(s) to restore")); + if (argc) { parse_pathspec(&opts->pathspec, 0, opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0, @@ -1668,33 +1641,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix, if (opts->force_detach) die(_("git checkout: --detach does not take a path argument '%s'"), argv[0]); - } - if (opts->pathspec_from_file) { - if (opts->pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - if (opts->force_detach) - die(_("--pathspec-from-file is incompatible with --detach")); - - if (opts->patch_mode) - die(_("--pathspec-from-file is incompatible with --patch")); - - parse_pathspec_file(&opts->pathspec, 0, - 0, - prefix, opts->pathspec_from_file, opts->pathspec_file_nul); - } else if (opts->pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - if (opts->pathspec.nr) { if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge) die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n" "checking out of the index.")); - } else { - if (opts->accept_pathspec && !opts->empty_pathspec_ok && - !opts->patch_mode) /* patch mode is special */ - die(_("you must specify path(s) to restore")); } if (opts->new_branch) { @@ -1709,10 +1659,28 @@ static int checkout_main(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); + } } int cmd_checkout(int argc, const char **argv, const char *prefix) @@ -1746,15 +1714,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.checkout_index = -2; /* default on */ opts.checkout_worktree = -2; /* default on */ - if (argc == 3 && !strcmp(argv[1], "-b")) { - /* - * User ran 'git checkout -b <branch>' and expects - * the same behavior as 'git switch -c <branch>'. - */ - opts.switch_branch_doing_nothing_is_ok = 0; - opts.only_merge_on_switching_branches = 1; - } - options = parse_options_dup(checkout_options); options = add_common_options(&opts, options); options = add_common_switch_branch_options(&opts, options); |