git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 6/7] checkout: split into switch-branch and checkout-files

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Nov 27, 2018, 16:52 UTC
Message-ID
<20181127165211.24763-7-pclouds@gmail.com>
In-Reply-To
<20181127165211.24763-1-pclouds@gmail.com>

"git checkout" doing too many things is a source of confusion for many users (and it even bites old timers sometimes). To rememdy that, the command is now split in two: switch-branch and checkout-files.

The switch-branch command is all about switching branches, detaching, DWIM-ing new branch... It does not accept pathspecs and it always requires a ref (in contrast, "git checkout" without arguments works)

The checkout-files command on the other hand is all about resetting certain files in worktree, either from the index or from a specific tree. It could accept a tree-ish, but it will never touch HEAD or the ref it points to.

The good old "git checkout" command is still here and will be until all (or most of users) are sick of it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin.h          |  2 +
 builtin/checkout.c | 91 +++++++++++++++++++++++++++++++++++++++-------
 git.c              |  2 +
 3 files changed, 82 insertions(+), 13 deletions(-)
diff --git a/builtin.h b/builtin.h
index 6538932e99..d4a66e5f79 100644
--- a/builtin.h
+++ b/builtin.h
@@ -138,6 +138,7 @@ extern int cmd_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_bundle(int argc, const char **argv, const char *prefix);
 extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
 extern int cmd_checkout(int argc, const char **argv, const char *prefix);
+extern int cmd_checkout_files(int argc, const char **argv, const char *prefix);
 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
@@ -227,6 +228,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_switch_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index d9dbd2d40d..c09d2da47a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -33,6 +33,16 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch-branch [<options>] <branch>"),
+	NULL,
+};
+
+static const char * const checkout_files_usage[] = {
+	N_("git checkout-files [<options>] [<branch>] -- <file>..."),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -45,6 +55,8 @@ struct checkout_opts {
 	int ignore_other_worktrees;
 	int show_progress;
 	int dwim_new_local_branch;
+	int accept_pathspec;
+	int empty_arg_ok;
 
 	/*
 	 * If new checkout options are added, skip_merge_working_tree
@@ -1056,7 +1068,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1067,6 +1079,8 @@ static int parse_branchname_arg(int argc, const char **argv,
 		has_dash_dash = 1; /* case (3) or (1) */
 	else if (dash_dash_pos >= 2)
 		die(_("only one reference expected, %d given."), dash_dash_pos);
+	else if (!opts->accept_pathspec)
+		has_dash_dash = 1;
 
 	if (!strcmp(arg, "-"))
 		arg = "@{-1}";
@@ -1291,30 +1305,23 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 	return newopts;
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
 {
-	struct checkout_opts real_opts;
-	struct checkout_opts *opts = &real_opts;
 	struct branch_info new_branch_info;
 	int dwim_remotes_matched = 0;
-	struct option *options = NULL;
 
-	memset(opts, 0, sizeof(*opts));
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
 	opts->show_progress = -1;
-	opts->dwim_new_local_branch = 1;
 
 	git_config(git_checkout_config, opts);
 
 	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	options = add_common_options(opts, options);
-	options = add_switch_branch_options(opts, options);
-	options = add_checkout_path_options(opts, options);
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+	argc = parse_options(argc, argv, prefix, options, usagestr,
 			     PARSE_OPT_KEEP_DASHDASH);
 
 	if (opts->show_progress < 0) {
@@ -1381,7 +1388,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
-	}
+	} else if (!opts->empty_arg_ok)
+		usage_with_options(usagestr, options);
 
 	if (argc) {
 		parse_pathspec(&opts->pathspec, 0,
@@ -1443,3 +1451,60 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.accept_pathspec = 1;
+	opts.empty_arg_ok = 1;
+
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch_branch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+
+	options = add_common_options(&opts, options);
+	options = add_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_checkout_files(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.accept_pathspec = 1;
+
+	options = add_common_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_files_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/git.c b/git.c
index 2f604a41ea..3b86ba765c 100644
--- a/git.c
+++ b/git.c
@@ -457,6 +457,7 @@ static struct cmd_struct commands[] = {
 	{ "check-mailmap", cmd_check_mailmap, RUN_SETUP },
 	{ "check-ref-format", cmd_check_ref_format, NO_PARSEOPT  },
 	{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
+	{ "checkout-files", cmd_checkout_files, RUN_SETUP | NEED_WORK_TREE },
 	{ "checkout-index", cmd_checkout_index,
 		RUN_SETUP | NEED_WORK_TREE},
 	{ "cherry", cmd_cherry, RUN_SETUP },
@@ -557,6 +558,7 @@ static struct cmd_struct commands[] = {
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch-branch", cmd_switch_branch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
-- 
2.19.1.1327.g328c130451.dirty
Previous: Junio C HamanoNext: Junio C Hamano
Message 24 of 110 in “checkout: print something when checking out paths”
  1. checkout: print something when checking out pathsNguyễn Thái Ngọc Duy, Nov 10, 2018
  2. Junio C HamanoNov 12, 2018
  3. Duy NguyenNov 12, 2018
  4. Junio C HamanoNov 12, 2018
  5. Ævar Arnfjörð BjarmasonNov 19, 2018
  6. Duy NguyenNov 19, 2018
  7. Junio C HamanoNov 20, 2018
  8. [RFC] Introduce two new commands, switch-branch and restore-pathsDuy Nguyen, Nov 20, 2018
  9. Thomas GummererNov 25, 2018
  10. Junio C HamanoNov 26, 2018
  11. Duy NguyenNov 26, 2018
  12. Ævar Arnfjörð BjarmasonNov 26, 2018
  13. Duy NguyenNov 26, 2018
  14. Stefan BellerNov 26, 2018
  15. Junio C HamanoNov 27, 2018
  16. 0/7 Introduce new commands switch-branch and checkout-filesNguyễn Thái Ngọc Duy, Nov 27, 2018
  17. 1/7 parse-options: allow parse_options_concat(NULL, options)Nguyễn Thái Ngọc Duy, Nov 27, 2018
  18. Stefan BellerNov 27, 2018
  19. Duy NguyenNov 28, 2018
  20. Junio C HamanoNov 28, 2018
  21. 2/7 checkout: make "opts" in cmd_checkout() a pointerNguyễn Thái Ngọc Duy, Nov 27, 2018
  22. 5/7 checkout: split options[] array in three piecesNguyễn Thái Ngọc Duy, Nov 27, 2018
  23. Junio C HamanoNov 29, 2018
  24. 6/7 checkout: split into switch-branch and checkout-filesNguyễn Thái Ngọc Duy, Nov 27, 2018
  25. Junio C HamanoNov 28, 2018
  26. Duy NguyenNov 28, 2018
  27. Stefan BellerNov 28, 2018
  28. Duy NguyenNov 28, 2018
  29. Junio C HamanoNov 29, 2018
  30. Stefan XenosNov 28, 2018
  31. Stefan XenosNov 28, 2018
  32. Stefan XenosNov 28, 2018
  33. Junio C HamanoNov 29, 2018
  34. Duy NguyenNov 29, 2018
  35. Duy NguyenNov 29, 2018
  36. Stefan BellerNov 29, 2018
  37. Duy NguyenNov 29, 2018
  38. Stefan XenosNov 29, 2018
  39. 4/7 checkout: move dwim_new_local_branch to checkout_optsNguyễn Thái Ngọc Duy, Nov 27, 2018
  40. Stefan BellerNov 27, 2018
  41. 7/7 Suggest other commands instead of "git checkout"Nguyễn Thái Ngọc Duy, Nov 27, 2018
  42. Junio C HamanoNov 28, 2018
  43. Duy NguyenNov 28, 2018
  44. Junio C HamanoNov 29, 2018
  45. 3/7 checkout: move 'confict_style' to checkout_optsNguyễn Thái Ngọc Duy, Nov 27, 2018
  46. Stefan BellerNov 27, 2018
  47. Duy NguyenNov 28, 2018
  48. Duy NguyenNov 28, 2018
  49. Stefan BellerNov 28, 2018
  50. Duy NguyenNov 29, 2018
  51. Stefan BellerDec 3, 2018
  52. Junio C HamanoNov 30, 2018
  53. 00/14 Introduce new commands switch-branch and restore-filesNguyễn Thái Ngọc Duy, Nov 29, 2018
  54. 01/14 git-checkout.txt: fix one syntax lineNguyễn Thái Ngọc Duy, Nov 29, 2018
  55. 03/14 checkout: factor out some code in parse_branchname_arg()Nguyễn Thái Ngọc Duy, Nov 29, 2018
  56. 04/14 checkout: make "opts" in cmd_checkout() a pointerNguyễn Thái Ngọc Duy, Nov 29, 2018
  57. 05/14 checkout: move 'confict_style' and 'dwim_..' to checkout_optsNguyễn Thái Ngọc Duy, Nov 29, 2018
  58. 06/14 checkout: split options[] array in three piecesNguyễn Thái Ngọc Duy, Nov 29, 2018
  59. 08/14 switch-branch: better names for -b and -BNguyễn Thái Ngọc Duy, Nov 29, 2018
  60. 09/14 switch-branch: stop accepting pathspecNguyễn Thái Ngọc Duy, Nov 29, 2018
  61. 10/14 switch-branch: reject "do nothing" caseNguyễn Thái Ngọc Duy, Nov 29, 2018
  62. 11/14 switch-branch: only allow explicit detached HEADNguyễn Thái Ngọc Duy, Nov 29, 2018
  63. Eckhard MaaßMar 10, 2019
  64. Duy NguyenMar 11, 2019
  65. 12/14 restore-files: take tree-ish from --from option insteadNguyễn Thái Ngọc Duy, Nov 29, 2018
  66. 13/14 restore-files: make pathspec mandatoryNguyễn Thái Ngọc Duy, Nov 29, 2018
  67. 14/14 doc: promote "git switch-branch" and "git restore-files"Nguyễn Thái Ngọc Duy, Nov 29, 2018
  68. 07/14 checkout: split into switch-branch and restore-filesNguyễn Thái Ngọc Duy, Nov 29, 2018
  69. Elijah NewrenDec 4, 2018
  70. Junio C HamanoDec 4, 2018
  71. Duy NguyenDec 4, 2018
  72. Elijah NewrenDec 4, 2018
  73. Duy NguyenDec 4, 2018
  74. Junio C HamanoDec 5, 2018
  75. Elijah NewrenDec 5, 2018
  76. Junio C HamanoDec 5, 2018
  77. Junio C HamanoDec 5, 2018
  78. Elijah NewrenDec 5, 2018
  79. 02/14 git-checkout.txt: split detached head section outNguyễn Thái Ngọc Duy, Nov 29, 2018
  80. Ævar Arnfjörð BjarmasonNov 29, 2018
  81. Ævar Arnfjörð BjarmasonNov 29, 2018
  82. Dan FabulichNov 29, 2018
  83. Dan FabulichNov 30, 2018
  84. Duy NguyenNov 30, 2018
  85. Duy NguyenNov 30, 2018
  86. Junio C HamanoNov 30, 2018
  87. Ævar Arnfjörð BjarmasonNov 30, 2018
  88. Duy NguyenNov 30, 2018
  89. Junio C HamanoNov 30, 2018
  90. Duy NguyenNov 30, 2018
  91. Junio C HamanoNov 30, 2018
  92. Thomas GummererDec 2, 2018
  93. Junio C HamanoDec 2, 2018
  94. Elijah NewrenDec 4, 2018
  95. Duy NguyenDec 4, 2018
  96. Elijah NewrenDec 4, 2018
  97. Duy NguyenDec 4, 2018
  98. Elijah NewrenDec 4, 2018
  99. Duy NguyenDec 4, 2018
  100. Eric SunshineDec 4, 2018
  101. checkout: print something when checking out pathsNguyễn Thái Ngọc Duy, Nov 13, 2018
  102. Junio C HamanoNov 14, 2018
  103. Duy NguyenNov 14, 2018
  104. Junio C HamanoJan 28, 2019
  105. Duy NguyenJan 29, 2019
  106. 0/2 nd/checkout-noisy updatesNguyễn Thái Ngọc Duy, Feb 6, 2019
  107. 1/2 checkout: update count-checkouts messagesNguyễn Thái Ngọc Duy, Feb 6, 2019
  108. 2/2 checkout: count and print -m paths separatelyNguyễn Thái Ngọc Duy, Feb 6, 2019
  109. Stefan XenosNov 28, 2018
  110. Junio C HamanoNov 29, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.