about summary refs log tree commit diff
path: root/third_party/git/builtin/checkout.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/git/builtin/checkout.c')
-rw-r--r--third_party/git/builtin/checkout.c137
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);