Skip to content

Commit b6312c2

Browse files
pcloudsgitster
authored andcommitted
checkout: reorder option handling
checkout operates in three different modes. On top of that it tries to be smart by guessing the branch name for switching. This results in messy option handling code. This patch reorders it so that - cmd_checkout() is responsible for parsing, preparing input and determining mode - Code of each mode is in checkout_paths() and checkout_branch(), where sanity checks are performed Another slight improvement is always print branch name (or commit name) when printing errors related ot them. This helps catch the case where an option is mistaken as branch/commit. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e51e305 commit b6312c2

File tree

1 file changed

+112
-77
lines changed

1 file changed

+112
-77
lines changed

builtin/checkout.c

Lines changed: 112 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ static int checkout_merged(int pos, struct checkout *state)
217217
return status;
218218
}
219219

220-
static int checkout_paths(const struct checkout_opts *opts)
220+
static int checkout_paths(const struct checkout_opts *opts,
221+
const char *revision)
221222
{
222223
int pos;
223224
struct checkout state;
@@ -229,7 +230,35 @@ static int checkout_paths(const struct checkout_opts *opts)
229230
int stage = opts->writeout_stage;
230231
int merge = opts->merge;
231232
int newfd;
232-
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
233+
struct lock_file *lock_file;
234+
235+
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
236+
die(_("'%s' cannot be used with updating paths"), "--track");
237+
238+
if (opts->new_branch_log)
239+
die(_("'%s' cannot be used with updating paths"), "-l");
240+
241+
if (opts->force && opts->patch_mode)
242+
die(_("'%s' cannot be used with updating paths"), "-f");
243+
244+
if (opts->force_detach)
245+
die(_("'%s' cannot be used with updating paths"), "--detach");
246+
247+
if (opts->merge && opts->patch_mode)
248+
die(_("'%s' cannot be used with %s"), "--merge", "--patch");
249+
250+
if (opts->force && opts->merge)
251+
die(_("'%s' cannot be used with %s"), "-f", "-m");
252+
253+
if (opts->new_branch)
254+
die(_("Cannot update paths and switch to branch '%s' at the same time."),
255+
opts->new_branch);
256+
257+
if (opts->patch_mode)
258+
return run_add_interactive(revision, "--patch=checkout",
259+
opts->pathspec);
260+
261+
lock_file = xcalloc(1, sizeof(struct lock_file));
233262

234263
newfd = hold_locked_index(lock_file, 1);
235264
if (read_cache_preload(opts->pathspec) < 0)
@@ -763,11 +792,6 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
763792
return git_xmerge_config(var, value, NULL);
764793
}
765794

766-
static int interactive_checkout(const char *revision, const char **pathspec)
767-
{
768-
return run_add_interactive(revision, "--patch=checkout", pathspec);
769-
}
770-
771795
struct tracking_name_data {
772796
const char *name;
773797
char *remote;
@@ -930,6 +954,51 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
930954
return status;
931955
}
932956

957+
static int checkout_branch(struct checkout_opts *opts,
958+
struct branch_info *new)
959+
{
960+
if (opts->pathspec)
961+
die(_("paths cannot be used with switching branches"));
962+
963+
if (opts->patch_mode)
964+
die(_("'%s' cannot be used with switching branches"),
965+
"--patch");
966+
967+
if (opts->writeout_stage)
968+
die(_("'%s' cannot be used with switching branches"),
969+
"--ours/--theirs");
970+
971+
if (opts->force && opts->merge)
972+
die(_("'%s' cannot be used with '%s'"), "-f", "-m");
973+
974+
if (opts->force_detach && opts->new_branch)
975+
die(_("'%s' cannot be used with '%s'"),
976+
"--detach", "-b/-B/--orphan");
977+
978+
if (opts->new_orphan_branch) {
979+
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
980+
die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
981+
} else if (opts->force_detach) {
982+
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
983+
die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
984+
} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
985+
opts->track = git_branch_track;
986+
987+
if (new->name && !new->commit)
988+
die(_("Cannot switch branch to a non-commit '%s'"),
989+
new->name);
990+
991+
if (!new->commit && opts->new_branch) {
992+
unsigned char rev[20];
993+
int flag;
994+
995+
if (!read_ref_full("HEAD", rev, 0, &flag) &&
996+
(flag & REF_ISSYMREF) && is_null_sha1(rev))
997+
return switch_unborn_to_new_branch(opts);
998+
}
999+
return switch_branches(opts, new);
1000+
}
1001+
9331002
int cmd_checkout(int argc, const char **argv, const char *prefix)
9341003
{
9351004
struct checkout_opts opts;
@@ -976,26 +1045,27 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
9761045
argc = parse_options(argc, argv, prefix, options, checkout_usage,
9771046
PARSE_OPT_KEEP_DASHDASH);
9781047

979-
/* we can assume from now on new_branch = !new_branch_force */
980-
if (opts.new_branch && opts.new_branch_force)
981-
die(_("-B cannot be used with -b"));
1048+
if (conflict_style) {
1049+
opts.merge = 1; /* implied */
1050+
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
1051+
}
9821052

983-
/* copy -B over to -b, so that we can just check the latter */
1053+
if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
1054+
die(_("-b, -B and --orphan are mutually exclusive"));
1055+
1056+
/*
1057+
* From here on, new_branch will contain the branch to be checked out,
1058+
* and new_branch_force and new_orphan_branch will tell us which one of
1059+
* -b/-B/--orphan is being used.
1060+
*/
9841061
if (opts.new_branch_force)
9851062
opts.new_branch = opts.new_branch_force;
9861063

987-
if (opts.patch_mode && (opts.track > 0 || opts.new_branch
988-
|| opts.new_branch_log || opts.merge || opts.force
989-
|| opts.force_detach))
990-
die (_("--patch is incompatible with all other options"));
991-
992-
if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
993-
die(_("--detach cannot be used with -b/-B/--orphan"));
994-
if (opts.force_detach && 0 < opts.track)
995-
die(_("--detach cannot be used with -t"));
1064+
if (opts.new_orphan_branch)
1065+
opts.new_branch = opts.new_orphan_branch;
9961066

997-
/* --track without -b should DWIM */
998-
if (0 < opts.track && !opts.new_branch) {
1067+
/* --track without -b/-B/--orphan should DWIM */
1068+
if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
9991069
const char *argv0 = argv[0];
10001070
if (!argc || !strcmp(argv0, "--"))
10011071
die (_("--track needs a branch name"));
@@ -1009,22 +1079,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
10091079
opts.new_branch = argv0 + 1;
10101080
}
10111081

1012-
if (opts.new_orphan_branch) {
1013-
if (opts.new_branch)
1014-
die(_("--orphan and -b|-B are mutually exclusive"));
1015-
if (opts.track > 0)
1016-
die(_("--orphan cannot be used with -t"));
1017-
opts.new_branch = opts.new_orphan_branch;
1018-
}
1019-
1020-
if (conflict_style) {
1021-
opts.merge = 1; /* implied */
1022-
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
1023-
}
1024-
1025-
if (opts.force && opts.merge)
1026-
die(_("git checkout: -f and -m are incompatible"));
1027-
10281082
/*
10291083
* Extract branch name from command line arguments, so
10301084
* all that is left is pathspecs.
@@ -1052,62 +1106,43 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
10521106
argc -= n;
10531107
}
10541108

1055-
if (opts.track == BRANCH_TRACK_UNSPECIFIED)
1056-
opts.track = git_branch_track;
1057-
10581109
if (argc) {
10591110
opts.pathspec = get_pathspec(prefix, argv);
10601111

10611112
if (!opts.pathspec)
10621113
die(_("invalid path specification"));
10631114

1064-
if (opts.patch_mode)
1065-
return interactive_checkout(new.name, opts.pathspec);
1066-
1067-
/* Checkout paths */
1068-
if (opts.new_branch) {
1069-
if (argc == 1) {
1070-
die(_("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?"), argv[0]);
1071-
} else {
1072-
die(_("git checkout: updating paths is incompatible with switching branches."));
1073-
}
1074-
}
1115+
/*
1116+
* Try to give more helpful suggestion.
1117+
* new_branch && argc > 1 will be caught later.
1118+
*/
1119+
if (opts.new_branch && argc == 1)
1120+
die(_("Cannot update paths and switch to branch '%s' at the same time.\n"
1121+
"Did you intend to checkout '%s' which can not be resolved as commit?"),
1122+
opts.new_branch, argv[0]);
10751123

10761124
if (opts.force_detach)
1077-
die(_("git checkout: --detach does not take a path argument"));
1125+
die(_("git checkout: --detach does not take a path argument '%s'"),
1126+
argv[0]);
10781127

10791128
if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
1080-
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
1081-
1082-
return checkout_paths(&opts);
1129+
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
1130+
"checking out of the index."));
10831131
}
10841132

1085-
if (opts.patch_mode)
1086-
return interactive_checkout(new.name, NULL);
1087-
10881133
if (opts.new_branch) {
10891134
struct strbuf buf = STRBUF_INIT;
10901135

1091-
opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
1092-
!!opts.new_branch_force,
1093-
!!opts.new_branch_force);
1136+
opts.branch_exists =
1137+
validate_new_branchname(opts.new_branch, &buf,
1138+
!!opts.new_branch_force,
1139+
!!opts.new_branch_force);
10941140

10951141
strbuf_release(&buf);
10961142
}
10971143

1098-
if (new.name && !new.commit) {
1099-
die(_("Cannot switch branch to a non-commit."));
1100-
}
1101-
if (opts.writeout_stage)
1102-
die(_("--ours/--theirs is incompatible with switching branches."));
1103-
1104-
if (!new.commit && opts.new_branch) {
1105-
unsigned char rev[20];
1106-
int flag;
1107-
1108-
if (!read_ref_full("HEAD", rev, 0, &flag) &&
1109-
(flag & REF_ISSYMREF) && is_null_sha1(rev))
1110-
return switch_unborn_to_new_branch(&opts);
1111-
}
1112-
return switch_branches(&opts, &new);
1144+
if (opts.patch_mode || opts.pathspec)
1145+
return checkout_paths(&opts, new.name);
1146+
else
1147+
return checkout_branch(&opts, &new);
11131148
}

0 commit comments

Comments
 (0)