* [PATCH] checkout: verify new branch name's validity early @ 2012-08-28 2:29 Nguyễn Thái Ngọc Duy 2012-08-28 3:55 ` Junio C Hamano 0 siblings, 1 reply; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-28 2:29 UTC (permalink / raw) To: git Cc: Junio C Hamano, 乙酸鋰, Nguyễn Thái Ngọc Duy If the new branch name to -b/-B happens to be missing, the next argument may be mistaken as branch name and no longer recognized by checkout as argument. This may lead to confusing error messages. By checking branch name early and printing out the invalid name, users may realize they forgot to specify branch name. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- On Tue, Aug 28, 2012 at 12:03 AM, Junio C Hamano <gitster@pobox.com> wrote: > Ideally you would want > > fatal: "-t" is not an acceptable name for a branch > > in this case; if it is cumbersome to arrange, at the very least, > > updating paths is incompatible with checking out the branch "-t". > > would be clearer. Fair enough. It turns out we do check branch name's validity. It's just too late. builtin/checkout.c | 20 ++++++++++---------- t/t2018-checkout-branch.sh | 5 +++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index d812219..03b0f25 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1049,6 +1049,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (opts.track == BRANCH_TRACK_UNSPECIFIED) opts.track = git_branch_track; + if (opts.new_branch) { + struct strbuf buf = STRBUF_INIT; + + opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, + !!opts.new_branch_force, + !!opts.new_branch_force); + + strbuf_release(&buf); + } + if (argc) { const char **pathspec = get_pathspec(prefix, argv); @@ -1079,16 +1089,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (patch_mode) return interactive_checkout(new.name, NULL, &opts); - if (opts.new_branch) { - struct strbuf buf = STRBUF_INIT; - - opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, - !!opts.new_branch_force, - !!opts.new_branch_force); - - strbuf_release(&buf); - } - if (new.name && !new.commit) { die(_("Cannot switch branch to a non-commit.")); } diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh index 2741262..48ab6a2 100755 --- a/t/t2018-checkout-branch.sh +++ b/t/t2018-checkout-branch.sh @@ -198,4 +198,9 @@ test_expect_success 'checkout -B to the current branch works' ' test_dirty_mergeable ' +test_expect_success 'checkout -b checks branch validitity early' ' + test_must_fail git checkout -b -t origin/master 2>err && + grep "not a valid branch name" err +' + test_done -- 1.7.12.rc2 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH] checkout: verify new branch name's validity early 2012-08-28 2:29 [PATCH] checkout: verify new branch name's validity early Nguyễn Thái Ngọc Duy @ 2012-08-28 3:55 ` Junio C Hamano 2012-08-28 13:49 ` [PATCH 0/3] Refactor checkout option handling Nguyễn Thái Ngọc Duy 0 siblings, 1 reply; 16+ messages in thread From: Junio C Hamano @ 2012-08-28 3:55 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git, 乙酸鋰 Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > If the new branch name to -b/-B happens to be missing, the next > argument may be mistaken as branch name and no longer recognized by > checkout as argument. This may lead to confusing error messages. > > By checking branch name early and printing out the invalid name, users > may realize they forgot to specify branch name. > > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> > --- > On Tue, Aug 28, 2012 at 12:03 AM, Junio C Hamano <gitster@pobox.com> wrote: > > Ideally you would want > > > > fatal: "-t" is not an acceptable name for a branch > > > > in this case; if it is cumbersome to arrange, at the very least, > > > > updating paths is incompatible with checking out the branch "-t". > > > > would be clearer. > > Fair enough. It turns out we do check branch name's validity. It's > just too late. The surrounding code is somewhat tricky and the code structure is brittle; there are places that update the opts.new_branch so the new location of this check has to be after them, and there is one codepath that having a bad value in it does not matter. I had to check the code outside the context of this patch a few times to convince myself that this patch does not break them. I'll queue the patch as-is for now, but we probably would want to see how we can structure it to be less brittle. Thanks. > builtin/checkout.c | 20 ++++++++++---------- > t/t2018-checkout-branch.sh | 5 +++++ > 2 files changed, 15 insertions(+), 10 deletions(-) > > diff --git a/builtin/checkout.c b/builtin/checkout.c > index d812219..03b0f25 100644 > --- a/builtin/checkout.c > +++ b/builtin/checkout.c > @@ -1049,6 +1049,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) > if (opts.track == BRANCH_TRACK_UNSPECIFIED) > opts.track = git_branch_track; > > + if (opts.new_branch) { > + struct strbuf buf = STRBUF_INIT; > + > + opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, > + !!opts.new_branch_force, > + !!opts.new_branch_force); > + > + strbuf_release(&buf); > + } > + > if (argc) { > const char **pathspec = get_pathspec(prefix, argv); > > @@ -1079,16 +1089,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) > if (patch_mode) > return interactive_checkout(new.name, NULL, &opts); > > - if (opts.new_branch) { > - struct strbuf buf = STRBUF_INIT; > - > - opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, > - !!opts.new_branch_force, > - !!opts.new_branch_force); > - > - strbuf_release(&buf); > - } > - > if (new.name && !new.commit) { > die(_("Cannot switch branch to a non-commit.")); > } > diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh > index 2741262..48ab6a2 100755 > --- a/t/t2018-checkout-branch.sh > +++ b/t/t2018-checkout-branch.sh > @@ -198,4 +198,9 @@ test_expect_success 'checkout -B to the current branch works' ' > test_dirty_mergeable > ' > > +test_expect_success 'checkout -b checks branch validitity early' ' > + test_must_fail git checkout -b -t origin/master 2>err && > + grep "not a valid branch name" err > +' > + > test_done ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 0/3] Refactor checkout option handling 2012-08-28 3:55 ` Junio C Hamano @ 2012-08-28 13:49 ` Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy ` (2 more replies) 0 siblings, 3 replies; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-28 13:49 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy On Tue, Aug 28, 2012 at 10:55 AM, Junio C Hamano <gitster@pobox.com> wrote: > The surrounding code is somewhat tricky and the code structure is > brittle; there are places that update the opts.new_branch so the new > location of this check has to be after them, and there is one > codepath that having a bad value in it does not matter. > > I had to check the code outside the context of this patch a few > times to convince myself that this patch does not break them. I'll > queue the patch as-is for now, but we probably would want to see how > we can structure it to be less brittle. I'll give it a shot. On top of master, not the "checkout -b -t" patch. The test suite passes, but we'll need more eyeballs for code moves like this. Nguyễn Thái Ngọc Duy (3): checkout: pass "struct checkout_opts *" as const pointer checkout: reorder option handling checkout: move branch guessing code out as a separate function builtin/checkout.c | 290 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 164 insertions(+), 126 deletions(-) -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/3] checkout: pass "struct checkout_opts *" as const pointer 2012-08-28 13:49 ` [PATCH 0/3] Refactor checkout option handling Nguyễn Thái Ngọc Duy @ 2012-08-28 13:49 ` Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 2/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 3/3] checkout: move branch guessing code out as a separate function Nguyễn Thái Ngọc Duy 2 siblings, 0 replies; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-28 13:49 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy This struct contains various switches to change checkout behavior and it feels somewhat safer to have the compiler reassure us that nowhere else changes it. One field that is changed, writeout_error, is split out and passed as another argument. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- builtin/checkout.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 7d922c6..3e3f086 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -33,7 +33,6 @@ struct checkout_opts { int force; int force_detach; int writeout_stage; - int writeout_error; int overwrite_ignore; /* not set by parse_options */ @@ -216,7 +215,7 @@ static int checkout_merged(int pos, struct checkout *state) } static int checkout_paths(struct tree *source_tree, const char **pathspec, - const char *prefix, struct checkout_opts *opts) + const char *prefix, const struct checkout_opts *opts) { int pos; struct checkout state; @@ -309,7 +308,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, return errs; } -static void show_local_changes(struct object *head, struct diff_options *opts) +static void show_local_changes(struct object *head, + const struct diff_options *opts) { struct rev_info rev; /* I think we want full paths, even if we're in a subdirectory. */ @@ -331,7 +331,8 @@ static void describe_detached_head(const char *msg, struct commit *commit) strbuf_release(&sb); } -static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) +static int reset_tree(struct tree *tree, const struct checkout_opts *o, + int worktree, int *writeout_error) { struct unpack_trees_options opts; struct tree_desc tree_desc; @@ -350,7 +351,7 @@ static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) init_tree_desc(&tree_desc, tree->buffer, tree->size); switch (unpack_trees(1, &tree_desc, &opts)) { case -2: - o->writeout_error = 1; + *writeout_error = 1; /* * We return 0 nevertheless, as the index is all right * and more importantly we have made best efforts to @@ -381,8 +382,10 @@ static void setup_branch_path(struct branch_info *branch) branch->path = strbuf_detach(&buf, NULL); } -static int merge_working_tree(struct checkout_opts *opts, - struct branch_info *old, struct branch_info *new) +static int merge_working_tree(const struct checkout_opts *opts, + struct branch_info *old, + struct branch_info *new, + int *writeout_error) { int ret; struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); @@ -393,7 +396,7 @@ static int merge_working_tree(struct checkout_opts *opts, resolve_undo_clear(); if (opts->force) { - ret = reset_tree(new->commit->tree, opts, 1); + ret = reset_tree(new->commit->tree, opts, 1, writeout_error); if (ret) return ret; } else { @@ -479,7 +482,8 @@ static int merge_working_tree(struct checkout_opts *opts, o.verbosity = 0; work = write_tree_from_memory(&o); - ret = reset_tree(new->commit->tree, opts, 1); + ret = reset_tree(new->commit->tree, opts, 1, + writeout_error); if (ret) return ret; o.ancestor = old->name; @@ -487,7 +491,8 @@ static int merge_working_tree(struct checkout_opts *opts, o.branch2 = "local"; merge_trees(&o, new->commit->tree, work, old->commit->tree, &result); - ret = reset_tree(new->commit->tree, opts, 0); + ret = reset_tree(new->commit->tree, opts, 0, + writeout_error); if (ret) return ret; } @@ -514,7 +519,7 @@ static void report_tracking(struct branch_info *new) strbuf_release(&sb); } -static void update_refs_for_switch(struct checkout_opts *opts, +static void update_refs_for_switch(const struct checkout_opts *opts, struct branch_info *old, struct branch_info *new) { @@ -701,13 +706,13 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) free(refs.objects); } -static int switch_branches(struct checkout_opts *opts, struct branch_info *new) +static int switch_branches(const struct checkout_opts *opts, struct branch_info *new) { int ret = 0; struct branch_info old; void *path_to_free; unsigned char rev[20]; - int flag; + int flag, writeout_error = 0; memset(&old, 0, sizeof(old)); old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag); old.commit = lookup_commit_reference_gently(rev, 1); @@ -725,7 +730,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) parse_commit(new->commit); } - ret = merge_working_tree(opts, &old, new); + ret = merge_working_tree(opts, &old, new, &writeout_error); if (ret) { free(path_to_free); return ret; @@ -738,7 +743,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) ret = post_checkout_hook(old.commit, new->commit, 1); free(path_to_free); - return ret || opts->writeout_error; + return ret || writeout_error; } static int git_checkout_config(const char *var, const char *value, void *cb) @@ -910,7 +915,7 @@ static int parse_branchname_arg(int argc, const char **argv, return argcount; } -static int switch_unborn_to_new_branch(struct checkout_opts *opts) +static int switch_unborn_to_new_branch(const struct checkout_opts *opts) { int status; struct strbuf branch_ref = STRBUF_INIT; -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 2/3] checkout: reorder option handling 2012-08-28 13:49 ` [PATCH 0/3] Refactor checkout option handling Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy @ 2012-08-28 13:49 ` Nguyễn Thái Ngọc Duy 2012-08-28 20:45 ` Junio C Hamano 2012-08-28 13:49 ` [PATCH 3/3] checkout: move branch guessing code out as a separate function Nguyễn Thái Ngọc Duy 2 siblings, 1 reply; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-28 13:49 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy 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: - easy option handling comes first - the big chunk of branch name guessing comes next - mode detection comes last. Once the mode is known, check again to see if users specify any incompatible options - the actual action is done 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 <pclouds@gmail.com> --- builtin/checkout.c | 153 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 64 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 3e3f086..3f1a436 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -760,8 +760,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb) return git_xmerge_config(var, value, NULL); } -static int interactive_checkout(const char *revision, const char **pathspec, - struct checkout_opts *opts) +static int interactive_checkout(const char *revision, const char **pathspec) { return run_add_interactive(revision, "--patch=checkout", pathspec); } @@ -937,6 +936,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) char *conflict_style = NULL; int patch_mode = 0; int dwim_new_local_branch = 1; + const char **pathspec = NULL; struct option options[] = { OPT__QUIET(&opts.quiet, "suppress progress reporting"), OPT_STRING('b', NULL, &opts.new_branch, "branch", @@ -976,23 +976,45 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - /* we can assume from now on new_branch = !new_branch_force */ - if (opts.new_branch && opts.new_branch_force) - die(_("-B cannot be used with -b")); - - /* copy -B over to -b, so that we can just check the latter */ - if (opts.new_branch_force) - opts.new_branch = opts.new_branch_force; - - if (patch_mode && (opts.track > 0 || opts.new_branch - || opts.new_branch_log || opts.merge || opts.force - || opts.force_detach)) - die (_("--patch is incompatible with all other options")); - + /* Easy mode-independent incompatible checks */ if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch)) die(_("--detach cannot be used with -b/-B/--orphan")); if (opts.force_detach && 0 < opts.track) die(_("--detach cannot be used with -t")); + if (opts.force && opts.merge) + die(_("git checkout: -f and -m are incompatible")); + + if (conflict_style) { + opts.merge = 1; /* implied */ + git_xmerge_config("merge.conflictstyle", conflict_style, NULL); + } + + /* + * opts.new_branch calculation, it could be from -b or -B or + * --track or --orphan or other command line arguments. + * + * The following code until new branch validation should not + * update any fields in opts but opts.new_branch. + */ + if (opts.new_branch_force) { + /* we can assume from now on new_branch = !new_branch_force */ + if (opts.new_branch) + die(_("New branch '%s' is already specified by other options"), + opts.new_branch); + + /* copy -B over to -b, so that we can just check the latter */ + if (opts.new_branch_force) + opts.new_branch = opts.new_branch_force; + } + + if (opts.new_orphan_branch) { + if (opts.track > 0) + die(_("--orphan cannot be used with -t")); + if (opts.new_branch) + die(_("New branch '%s' is already specified by other options"), + opts.new_branch); + opts.new_branch = opts.new_orphan_branch; + } /* --track without -b should DWIM */ if (0 < opts.track && !opts.new_branch) { @@ -1009,22 +1031,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.new_branch = argv0 + 1; } - if (opts.new_orphan_branch) { - if (opts.new_branch) - die(_("--orphan and -b|-B are mutually exclusive")); - if (opts.track > 0) - die(_("--orphan cannot be used with -t")); - opts.new_branch = opts.new_orphan_branch; - } - - if (conflict_style) { - opts.merge = 1; /* implied */ - git_xmerge_config("merge.conflictstyle", conflict_style, NULL); - } - - if (opts.force && opts.merge) - die(_("git checkout: -f and -m are incompatible")); - /* * Extract branch name from command line arguments, so * all that is left is pathspecs. @@ -1050,39 +1056,29 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc -= n; } - if (opts.track == BRANCH_TRACK_UNSPECIFIED) - opts.track = git_branch_track; - + /* After DWIM, these must be pathspec */ if (argc) { - const char **pathspec = get_pathspec(prefix, argv); + pathspec = get_pathspec(prefix, argv); if (!pathspec) die(_("invalid path specification")); - if (patch_mode) - return interactive_checkout(new.name, pathspec, &opts); - - /* Checkout paths */ - if (opts.new_branch) { - if (argc == 1) { - 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]); - } else { - die(_("git checkout: updating paths is incompatible with switching branches.")); - } - } + /* Try to give more helpful suggestion, new_branch && + argc > 1 will be caught later */ + if (opts.new_branch && argc == 1) + die(_("Cannot update paths and switch to branch '%s' at the same time.\n" + "Did you intend to checkout '%s' which can not be resolved as commit?"), + opts.new_branch, argv[0]); if (opts.force_detach) die(_("git checkout: --detach does not take a path argument")); if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge) - die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.")); - - return checkout_paths(source_tree, pathspec, prefix, &opts); + die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n" + "checking out of the index.")); } - if (patch_mode) - return interactive_checkout(new.name, NULL, &opts); - + /* new branch calculation done, validate it */ if (opts.new_branch) { struct strbuf buf = STRBUF_INIT; @@ -1093,19 +1089,48 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) strbuf_release(&buf); } - if (new.name && !new.commit) { - die(_("Cannot switch branch to a non-commit.")); - } - if (opts.writeout_stage) - die(_("--ours/--theirs is incompatible with switching branches.")); + if (patch_mode || pathspec) { /* checking out entries */ + if (opts.track > 0) + die(_("%s cannot be used while checking out paths"), + "--track"); + if (opts.new_branch_log) + die(_("%s cannot be used while checking out paths"), + "-l"); + if (opts.merge && patch_mode) + die(_("--merge cannot be used with --patch")); + if (opts.force && patch_mode) + die(_("%s cannot be used while checking out paths"), + "-f"); + if (opts.force_detach) + die(_("%s cannot be used while checking out paths"), + "--detach"); + if (opts.new_branch) + die(_("Cannot update paths and switch to branch '%s' at the same time."), + opts.new_branch); + + if (patch_mode) + return interactive_checkout(new.name, pathspec); + else + return checkout_paths(source_tree, pathspec, prefix, &opts); + } else { /* switch branch */ + if (new.name && !new.commit) + die(_("Cannot switch branch to a non-commit '%s'."), + new.name); - if (!new.commit && opts.new_branch) { - unsigned char rev[20]; - int flag; + if (opts.writeout_stage) + die(_("--ours/--theirs is incompatible with switching branches.")); - if (!read_ref_full("HEAD", rev, 0, &flag) && - (flag & REF_ISSYMREF) && is_null_sha1(rev)) - return switch_unborn_to_new_branch(&opts); + if (opts.track == BRANCH_TRACK_UNSPECIFIED) + opts.track = git_branch_track; + + if (!new.commit && opts.new_branch) { + unsigned char rev[20]; + int flag; + + if (!read_ref_full("HEAD", rev, 0, &flag) && + (flag & REF_ISSYMREF) && is_null_sha1(rev)) + return switch_unborn_to_new_branch(&opts); + } + return switch_branches(&opts, &new); } - return switch_branches(&opts, &new); } -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 2/3] checkout: reorder option handling 2012-08-28 13:49 ` [PATCH 2/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy @ 2012-08-28 20:45 ` Junio C Hamano 2012-08-29 12:16 ` Nguyen Thai Ngoc Duy 0 siblings, 1 reply; 16+ messages in thread From: Junio C Hamano @ 2012-08-28 20:45 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > 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: > > - easy option handling comes first > - the big chunk of branch name guessing comes next > - mode detection comes last. Once the mode is known, check again to > see if users specify any incompatible options > - the actual action is done > > 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 <pclouds@gmail.com> > --- > ... > + /* Easy mode-independent incompatible checks */ > if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch)) > die(_("--detach cannot be used with -b/-B/--orphan")); Did you catch "--detach -B" combination without checking new_branch_force? > if (opts.force_detach && 0 < opts.track) > die(_("--detach cannot be used with -t")); > + if (opts.force && opts.merge) > + die(_("git checkout: -f and -m are incompatible")); > + > + if (conflict_style) { > + opts.merge = 1; /* implied */ > + git_xmerge_config("merge.conflictstyle", conflict_style, NULL); > + } "checkout --conflict=diff3 -f <branch>" would imply combination of "-m" and "-f", which is supposed to be forbidden in the previous check, no? I very much like the idea of separating things in phases like your proposed log message explains. But I wonder if the order should be to do dwimming and parameter canonicalization first, then decide the mode (these might have to be swapped, as the same parameter may dwim down to different things depending on the mode), and finally check for sanity before performing. To avoid confusion, it also might not be a bad idea to remove new_branch_force and new_orphan_branch from the structure and introduce "enum branch_creation_type" or something, and always have the new branch name in "new_branch" field (this needs to get various pointers into opts out of the parseopt options[] array; parse into separate variables and decide what to put in "struct checkout_opts"), independent from how that branch is going to be created (either -b, -B, or --orphan). ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/3] checkout: reorder option handling 2012-08-28 20:45 ` Junio C Hamano @ 2012-08-29 12:16 ` Nguyen Thai Ngoc Duy 2012-08-29 13:55 ` [PATCH v2 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy 0 siblings, 1 reply; 16+ messages in thread From: Nguyen Thai Ngoc Duy @ 2012-08-29 12:16 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Wed, Aug 29, 2012 at 3:45 AM, Junio C Hamano <gitster@pobox.com> wrote: >> + /* Easy mode-independent incompatible checks */ >> if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch)) >> die(_("--detach cannot be used with -b/-B/--orphan")); > > Did you catch "--detach -B" combination without checking new_branch_force? I did not. Will move this check further down once opts.new_branch is final. >> if (opts.force_detach && 0 < opts.track) >> die(_("--detach cannot be used with -t")); >> + if (opts.force && opts.merge) >> + die(_("git checkout: -f and -m are incompatible")); >> + >> + if (conflict_style) { >> + opts.merge = 1; /* implied */ >> + git_xmerge_config("merge.conflictstyle", conflict_style, NULL); >> + } > > "checkout --conflict=diff3 -f <branch>" would imply combination of > "-m" and "-f", which is supposed to be forbidden in the previous > check, no? Yep. That last "if" must be moved up. > I very much like the idea of separating things in phases like your > proposed log message explains. But I wonder if the order should be > to do dwimming and parameter canonicalization first, then decide the > mode (these might have to be swapped, as the same parameter may dwim > down to different things depending on the mode), and finally check > for sanity before performing. I do a few sanity checks after the mode has been decided and obviously missed some as you pointed out above. Will move them all down. So parameter canonicalization, dwim, then separate code paths for each mode, which includes sanity checks. > To avoid confusion, it also might not be a bad idea to remove > new_branch_force and new_orphan_branch from the structure and > introduce "enum branch_creation_type" or something, and always have > the new branch name in "new_branch" field (this needs to get various > pointers into opts out of the parseopt options[] array; parse into > separate variables and decide what to put in "struct checkout_opts"), > independent from how that branch is going to be created (either -b, > -B, or --orphan). OK -- Duy ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 1/3] checkout: pass "struct checkout_opts *" as const pointer 2012-08-29 12:16 ` Nguyen Thai Ngoc Duy @ 2012-08-29 13:55 ` Nguyễn Thái Ngọc Duy 2012-08-29 13:55 ` [PATCH v2 2/3] checkout: move more parameters to struct checkout_opts Nguyễn Thái Ngọc Duy 2012-08-29 13:55 ` [PATCH v2 3/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy 0 siblings, 2 replies; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-29 13:55 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy This struct contains various switches to system and it feels somewhat safer to have the compiler reassure us that nowhere else changes it. One field that is changed, writeout_error, is split out and passed as another argument. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- builtin/checkout.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 7d922c6..3e3f086 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -33,7 +33,6 @@ struct checkout_opts { int force; int force_detach; int writeout_stage; - int writeout_error; int overwrite_ignore; /* not set by parse_options */ @@ -216,7 +215,7 @@ static int checkout_merged(int pos, struct checkout *state) } static int checkout_paths(struct tree *source_tree, const char **pathspec, - const char *prefix, struct checkout_opts *opts) + const char *prefix, const struct checkout_opts *opts) { int pos; struct checkout state; @@ -309,7 +308,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, return errs; } -static void show_local_changes(struct object *head, struct diff_options *opts) +static void show_local_changes(struct object *head, + const struct diff_options *opts) { struct rev_info rev; /* I think we want full paths, even if we're in a subdirectory. */ @@ -331,7 +331,8 @@ static void describe_detached_head(const char *msg, struct commit *commit) strbuf_release(&sb); } -static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) +static int reset_tree(struct tree *tree, const struct checkout_opts *o, + int worktree, int *writeout_error) { struct unpack_trees_options opts; struct tree_desc tree_desc; @@ -350,7 +351,7 @@ static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) init_tree_desc(&tree_desc, tree->buffer, tree->size); switch (unpack_trees(1, &tree_desc, &opts)) { case -2: - o->writeout_error = 1; + *writeout_error = 1; /* * We return 0 nevertheless, as the index is all right * and more importantly we have made best efforts to @@ -381,8 +382,10 @@ static void setup_branch_path(struct branch_info *branch) branch->path = strbuf_detach(&buf, NULL); } -static int merge_working_tree(struct checkout_opts *opts, - struct branch_info *old, struct branch_info *new) +static int merge_working_tree(const struct checkout_opts *opts, + struct branch_info *old, + struct branch_info *new, + int *writeout_error) { int ret; struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); @@ -393,7 +396,7 @@ static int merge_working_tree(struct checkout_opts *opts, resolve_undo_clear(); if (opts->force) { - ret = reset_tree(new->commit->tree, opts, 1); + ret = reset_tree(new->commit->tree, opts, 1, writeout_error); if (ret) return ret; } else { @@ -479,7 +482,8 @@ static int merge_working_tree(struct checkout_opts *opts, o.verbosity = 0; work = write_tree_from_memory(&o); - ret = reset_tree(new->commit->tree, opts, 1); + ret = reset_tree(new->commit->tree, opts, 1, + writeout_error); if (ret) return ret; o.ancestor = old->name; @@ -487,7 +491,8 @@ static int merge_working_tree(struct checkout_opts *opts, o.branch2 = "local"; merge_trees(&o, new->commit->tree, work, old->commit->tree, &result); - ret = reset_tree(new->commit->tree, opts, 0); + ret = reset_tree(new->commit->tree, opts, 0, + writeout_error); if (ret) return ret; } @@ -514,7 +519,7 @@ static void report_tracking(struct branch_info *new) strbuf_release(&sb); } -static void update_refs_for_switch(struct checkout_opts *opts, +static void update_refs_for_switch(const struct checkout_opts *opts, struct branch_info *old, struct branch_info *new) { @@ -701,13 +706,13 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) free(refs.objects); } -static int switch_branches(struct checkout_opts *opts, struct branch_info *new) +static int switch_branches(const struct checkout_opts *opts, struct branch_info *new) { int ret = 0; struct branch_info old; void *path_to_free; unsigned char rev[20]; - int flag; + int flag, writeout_error = 0; memset(&old, 0, sizeof(old)); old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag); old.commit = lookup_commit_reference_gently(rev, 1); @@ -725,7 +730,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) parse_commit(new->commit); } - ret = merge_working_tree(opts, &old, new); + ret = merge_working_tree(opts, &old, new, &writeout_error); if (ret) { free(path_to_free); return ret; @@ -738,7 +743,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) ret = post_checkout_hook(old.commit, new->commit, 1); free(path_to_free); - return ret || opts->writeout_error; + return ret || writeout_error; } static int git_checkout_config(const char *var, const char *value, void *cb) @@ -910,7 +915,7 @@ static int parse_branchname_arg(int argc, const char **argv, return argcount; } -static int switch_unborn_to_new_branch(struct checkout_opts *opts) +static int switch_unborn_to_new_branch(const struct checkout_opts *opts) { int status; struct strbuf branch_ref = STRBUF_INIT; -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 2/3] checkout: move more parameters to struct checkout_opts 2012-08-29 13:55 ` [PATCH v2 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy @ 2012-08-29 13:55 ` Nguyễn Thái Ngọc Duy 2012-08-29 13:55 ` [PATCH v2 3/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy 1 sibling, 0 replies; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-29 13:55 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- builtin/checkout.c | 68 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 3e3f086..78abaeb 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -28,6 +28,7 @@ static const char * const checkout_usage[] = { }; struct checkout_opts { + int patch_mode; int quiet; int merge; int force; @@ -35,15 +36,17 @@ struct checkout_opts { int writeout_stage; int overwrite_ignore; - /* not set by parse_options */ - int branch_exists; - const char *new_branch; const char *new_branch_force; const char *new_orphan_branch; int new_branch_log; enum branch_track track; struct diff_options diff_options; + + int branch_exists; + const char *prefix; + const char **pathspec; + struct tree *source_tree; }; static int post_checkout_hook(struct commit *old, struct commit *new, @@ -214,8 +217,7 @@ static int checkout_merged(int pos, struct checkout *state) return status; } -static int checkout_paths(struct tree *source_tree, const char **pathspec, - const char *prefix, const struct checkout_opts *opts) +static int checkout_paths(const struct checkout_opts *opts) { int pos; struct checkout state; @@ -230,34 +232,34 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 1); - if (read_cache_preload(pathspec) < 0) + if (read_cache_preload(opts->pathspec) < 0) return error(_("corrupt index file")); - if (source_tree) - read_tree_some(source_tree, pathspec); + if (opts->source_tree) + read_tree_some(opts->source_tree, opts->pathspec); - for (pos = 0; pathspec[pos]; pos++) + for (pos = 0; opts->pathspec[pos]; pos++) ; ps_matched = xcalloc(1, pos); for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (source_tree && !(ce->ce_flags & CE_UPDATE)) + if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) continue; - match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched); + match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched); } - if (report_path_error(ps_matched, pathspec, prefix)) + if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) return 1; /* "checkout -m path" to recreate conflicted state */ if (opts->merge) - unmerge_cache(pathspec); + unmerge_cache(opts->pathspec); /* Any unmerged paths? */ for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) { + if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) { if (!ce_stage(ce)) continue; if (opts->force) { @@ -282,9 +284,9 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, state.refresh_cache = 1; for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (source_tree && !(ce->ce_flags & CE_UPDATE)) + if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) continue; - if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) { + if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) { if (!ce_stage(ce)) { errs |= checkout_entry(ce, &state, NULL); continue; @@ -706,7 +708,8 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) free(refs.objects); } -static int switch_branches(const struct checkout_opts *opts, struct branch_info *new) +static int switch_branches(const struct checkout_opts *opts, + struct branch_info *new) { int ret = 0; struct branch_info old; @@ -760,8 +763,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb) return git_xmerge_config(var, value, NULL); } -static int interactive_checkout(const char *revision, const char **pathspec, - struct checkout_opts *opts) +static int interactive_checkout(const char *revision, const char **pathspec) { return run_add_interactive(revision, "--patch=checkout", pathspec); } @@ -931,11 +933,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; - unsigned char rev[20]; struct branch_info new; - struct tree *source_tree = NULL; char *conflict_style = NULL; - int patch_mode = 0; int dwim_new_local_branch = 1; struct option options[] = { OPT__QUIET(&opts.quiet, "suppress progress reporting"), @@ -957,7 +956,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, "update ignored files (default)"), OPT_STRING(0, "conflict", &conflict_style, "style", "conflict style (merge or diff3)"), - OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), + OPT_BOOLEAN('p', "patch", &opts.patch_mode, "select hunks interactively"), { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL, "second guess 'git checkout no-such-branch'", PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, @@ -967,6 +966,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); opts.overwrite_ignore = 1; + opts.prefix = prefix; gitmodules_config(); git_config(git_checkout_config, &opts); @@ -984,7 +984,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (opts.new_branch_force) opts.new_branch = opts.new_branch_force; - if (patch_mode && (opts.track > 0 || opts.new_branch + if (opts.patch_mode && (opts.track > 0 || opts.new_branch || opts.new_branch_log || opts.merge || opts.force || opts.force_detach)) die (_("--patch is incompatible with all other options")); @@ -1039,13 +1039,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) * remote branches, erroring out for invalid or ambiguous cases. */ if (argc) { + unsigned char rev[20]; int dwim_ok = - !patch_mode && + !opts.patch_mode && dwim_new_local_branch && opts.track == BRANCH_TRACK_UNSPECIFIED && !opts.new_branch; int n = parse_branchname_arg(argc, argv, dwim_ok, - &new, &source_tree, rev, &opts.new_branch); + &new, &opts.source_tree, + rev, &opts.new_branch); argv += n; argc -= n; } @@ -1054,13 +1056,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.track = git_branch_track; if (argc) { - const char **pathspec = get_pathspec(prefix, argv); + opts.pathspec = get_pathspec(prefix, argv); - if (!pathspec) + if (!opts.pathspec) die(_("invalid path specification")); - if (patch_mode) - return interactive_checkout(new.name, pathspec, &opts); + if (opts.patch_mode) + return interactive_checkout(new.name, opts.pathspec); /* Checkout paths */ if (opts.new_branch) { @@ -1077,11 +1079,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge) die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.")); - return checkout_paths(source_tree, pathspec, prefix, &opts); + return checkout_paths(&opts); } - if (patch_mode) - return interactive_checkout(new.name, NULL, &opts); + if (opts.patch_mode) + return interactive_checkout(new.name, NULL); if (opts.new_branch) { struct strbuf buf = STRBUF_INIT; -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 3/3] checkout: reorder option handling 2012-08-29 13:55 ` [PATCH v2 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy 2012-08-29 13:55 ` [PATCH v2 2/3] checkout: move more parameters to struct checkout_opts Nguyễn Thái Ngọc Duy @ 2012-08-29 13:55 ` Nguyễn Thái Ngọc Duy 2012-08-29 18:37 ` Junio C Hamano ` (2 more replies) 1 sibling, 3 replies; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-29 13:55 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy 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 determinining mode - Code of each mode is in cmd_checkout_entry() and cmd_switch(), where sanity checks are performed per mode 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 <pclouds@gmail.com> --- I'm not entirely sure about this chunk + if (opts->track != BRANCH_TRACK_UNSPECIFIED) { + if (opts->new_orphan_branch) + die(_("%s cannot be used with %s"), "--orphan", "-t"); + if (opts->force_detach) + die(_("%s cannot be used with %s"), "--detach", "-t"); + } else + opts->track = git_branch_track; If we don't want -t and --orphan/--detach together, then we probably should ignore branch.autosetupmerge when --orphan/--detach is specified. I did not unify new_branch, new_branch_force and new_orphan_branch. They touch other parts of the code and should probably be done separately. builtin/checkout.c | 169 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 68 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 78abaeb..8289bcd 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -930,6 +930,81 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) return status; } +static int cmd_checkout_entry(const struct checkout_opts *opts, + const struct branch_info *new) +{ + if (opts->track != BRANCH_TRACK_UNSPECIFIED) + die(_("%s cannot be used with updating paths"), "--track"); + + if (opts->new_branch_log) + die(_("%s cannot be used with updating paths"), "-l"); + + if (opts->force && opts->patch_mode) + die(_("%s cannot be used with updating paths"), "-f"); + + if (opts->force_detach) + die(_("%s cannot be used with updating paths"), "--detach"); + + if (opts->merge && opts->patch_mode) + die(_("%s cannot be used with %s"), "--merge", "--patch"); + + if (opts->force && opts->merge) + die(_("%s cannot be used with %s"), "-f", "-m"); + + if (opts->new_branch) + die(_("Cannot update paths and switch to branch '%s' at the same time."), + opts->new_branch); + + if (opts->patch_mode) + return interactive_checkout(new->name, opts->pathspec); + else + return checkout_paths(opts); +} + +static int cmd_switch(struct checkout_opts *opts, + struct branch_info *new) +{ + if (opts->pathspec) + die(_("paths cannot be used with switching branches")); + + if (opts->patch_mode) + die(_("%s cannot be used with switching branches"), + "--patch"); + + if (opts->writeout_stage) + die(_("%s cannot be used with switching branches"), + "--ours/--theirs"); + + if (opts->force && opts->merge) + die(_("%s cannot be used with %s"), "-f", "-m"); + + if (opts->force_detach && opts->new_branch) + die(_("%s cannot be used with %s"), + "--detach", "-b/-B/--orphan"); + + if (opts->track != BRANCH_TRACK_UNSPECIFIED) { + if (opts->new_orphan_branch) + die(_("%s cannot be used with %s"), "--orphan", "-t"); + if (opts->force_detach) + die(_("%s cannot be used with %s"), "--detach", "-t"); + } else + opts->track = git_branch_track; + + if (new->name && !new->commit) + die(_("Cannot switch branch to a non-commit '%s'."), + new->name); + + if (!new->commit && opts->new_branch) { + unsigned char rev[20]; + int flag; + + if (!read_ref_full("HEAD", rev, 0, &flag) && + (flag & REF_ISSYMREF) && is_null_sha1(rev)) + return switch_unborn_to_new_branch(opts); + } + return switch_branches(opts, new); +} + int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; @@ -976,26 +1051,22 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - /* we can assume from now on new_branch = !new_branch_force */ - if (opts.new_branch && opts.new_branch_force) - die(_("-B cannot be used with -b")); + if (conflict_style) { + opts.merge = 1; /* implied */ + git_xmerge_config("merge.conflictstyle", conflict_style, NULL); + } + + if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1) + die(_("-b, -B and --orphan are mutually exclusive")); - /* copy -B over to -b, so that we can just check the latter */ if (opts.new_branch_force) opts.new_branch = opts.new_branch_force; - if (opts.patch_mode && (opts.track > 0 || opts.new_branch - || opts.new_branch_log || opts.merge || opts.force - || opts.force_detach)) - die (_("--patch is incompatible with all other options")); - - if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch)) - die(_("--detach cannot be used with -b/-B/--orphan")); - if (opts.force_detach && 0 < opts.track) - die(_("--detach cannot be used with -t")); + if (opts.new_orphan_branch) + opts.new_branch = opts.new_orphan_branch; /* --track without -b should DWIM */ - if (0 < opts.track && !opts.new_branch) { + if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) { const char *argv0 = argv[0]; if (!argc || !strcmp(argv0, "--")) die (_("--track needs a branch name")); @@ -1009,22 +1080,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.new_branch = argv0 + 1; } - if (opts.new_orphan_branch) { - if (opts.new_branch) - die(_("--orphan and -b|-B are mutually exclusive")); - if (opts.track > 0) - die(_("--orphan cannot be used with -t")); - opts.new_branch = opts.new_orphan_branch; - } - - if (conflict_style) { - opts.merge = 1; /* implied */ - git_xmerge_config("merge.conflictstyle", conflict_style, NULL); - } - - if (opts.force && opts.merge) - die(_("git checkout: -f and -m are incompatible")); - /* * Extract branch name from command line arguments, so * all that is left is pathspecs. @@ -1052,62 +1107,40 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc -= n; } - if (opts.track == BRANCH_TRACK_UNSPECIFIED) - opts.track = git_branch_track; - if (argc) { opts.pathspec = get_pathspec(prefix, argv); if (!opts.pathspec) die(_("invalid path specification")); - if (opts.patch_mode) - return interactive_checkout(new.name, opts.pathspec); - - /* Checkout paths */ - if (opts.new_branch) { - if (argc == 1) { - 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]); - } else { - die(_("git checkout: updating paths is incompatible with switching branches.")); - } - } + /* Try to give more helpful suggestion, new_branch && + argc > 1 will be caught later */ + if (opts.new_branch && argc == 1) + die(_("Cannot update paths and switch to branch '%s' at the same time.\n" + "Did you intend to checkout '%s' which can not be resolved as commit?"), + opts.new_branch, argv[0]); if (opts.force_detach) die(_("git checkout: --detach does not take a path argument")); if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge) - die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.")); - - return checkout_paths(&opts); + die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n" + "checking out of the index.")); } - if (opts.patch_mode) - return interactive_checkout(new.name, NULL); - if (opts.new_branch) { struct strbuf buf = STRBUF_INIT; - opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, - !!opts.new_branch_force, - !!opts.new_branch_force); + opts.branch_exists = + validate_new_branchname(opts.new_branch, &buf, + !!opts.new_branch_force, + !!opts.new_branch_force); strbuf_release(&buf); } - if (new.name && !new.commit) { - die(_("Cannot switch branch to a non-commit.")); - } - if (opts.writeout_stage) - die(_("--ours/--theirs is incompatible with switching branches.")); - - if (!new.commit && opts.new_branch) { - unsigned char rev[20]; - int flag; - - if (!read_ref_full("HEAD", rev, 0, &flag) && - (flag & REF_ISSYMREF) && is_null_sha1(rev)) - return switch_unborn_to_new_branch(&opts); - } - return switch_branches(&opts, &new); + if (opts.patch_mode || opts.pathspec) + return cmd_checkout_entry(&opts, &new); + else + return cmd_switch(&opts, &new); } -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v2 3/3] checkout: reorder option handling 2012-08-29 13:55 ` [PATCH v2 3/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy @ 2012-08-29 18:37 ` Junio C Hamano 2012-08-29 18:55 ` Junio C Hamano 2012-08-30 12:45 ` [PATCH v3 " Nguyễn Thái Ngọc Duy 2 siblings, 0 replies; 16+ messages in thread From: Junio C Hamano @ 2012-08-29 18:37 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > I'm not entirely sure about this chunk > > + if (opts->track != BRANCH_TRACK_UNSPECIFIED) { > + if (opts->new_orphan_branch) > + die(_("%s cannot be used with %s"), "--orphan", "-t"); > + if (opts->force_detach) > + die(_("%s cannot be used with %s"), "--detach", "-t"); > + } else > + opts->track = git_branch_track; > > If we don't want -t and --orphan/--detach together, then we probably should ignore > branch.autosetupmerge when --orphan/--detach is specified. Yeah, I agree. > I did not unify new_branch, new_branch_force and new_orphan_branch. > They touch other parts of the code and should probably be done > separately. That sounds like a very sensible decision. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 3/3] checkout: reorder option handling 2012-08-29 13:55 ` [PATCH v2 3/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy 2012-08-29 18:37 ` Junio C Hamano @ 2012-08-29 18:55 ` Junio C Hamano 2012-08-30 12:45 ` [PATCH v3 " Nguyễn Thái Ngọc Duy 2 siblings, 0 replies; 16+ messages in thread From: Junio C Hamano @ 2012-08-29 18:55 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > + if (opts.patch_mode || opts.pathspec) > + return cmd_checkout_entry(&opts, &new); > + else > + return cmd_switch(&opts, &new); > } Yeah, patch_mode is really part of checking out paths, so separating the actions into two makes sense. Even though I am not sure about "cmd_" prefix (as these are not commands), I'll rename the former "cmd_checkout_paths" to be consistent with the underlying checkout_paths() function, and the. latter "cmd_checkout_branch" for consistency with the former (you either check out one-or-more paths, or check out a branch). ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v3 3/3] checkout: reorder option handling 2012-08-29 13:55 ` [PATCH v2 3/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy 2012-08-29 18:37 ` Junio C Hamano 2012-08-29 18:55 ` Junio C Hamano @ 2012-08-30 12:45 ` Nguyễn Thái Ngọc Duy 2012-08-30 16:10 ` Junio C Hamano 2012-09-07 20:19 ` Junio C Hamano 2 siblings, 2 replies; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-30 12:45 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy 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 <pclouds@gmail.com> --- Changes since v2 (the first two patches are not resent): - leave track mode unspecified if --detach/--orphan is specified - merge cmd_checkout_entry() into checkout_paths() - rename cmd_switch() to checkout_branch() builtin/checkout.c | 177 ++++++++++++++++++++++++++++++----------------------- 1 tập tin đã bị thay đổi, 102 được thêm vào(+), 75 bị xóa(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 78abaeb..b0c5133 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -217,7 +217,8 @@ static int checkout_merged(int pos, struct checkout *state) return status; } -static int checkout_paths(const struct checkout_opts *opts) +static int checkout_paths(const struct checkout_opts *opts, + const char *revision) { int pos; struct checkout state; @@ -229,7 +230,35 @@ static int checkout_paths(const struct checkout_opts *opts) int stage = opts->writeout_stage; int merge = opts->merge; int newfd; - struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); + struct lock_file *lock_file; + + if (opts->track != BRANCH_TRACK_UNSPECIFIED) + die(_("%s cannot be used with updating paths"), "--track"); + + if (opts->new_branch_log) + die(_("%s cannot be used with updating paths"), "-l"); + + if (opts->force && opts->patch_mode) + die(_("%s cannot be used with updating paths"), "-f"); + + if (opts->force_detach) + die(_("%s cannot be used with updating paths"), "--detach"); + + if (opts->merge && opts->patch_mode) + die(_("%s cannot be used with %s"), "--merge", "--patch"); + + if (opts->force && opts->merge) + die(_("%s cannot be used with %s"), "-f", "-m"); + + if (opts->new_branch) + die(_("Cannot update paths and switch to branch '%s' at the same time."), + opts->new_branch); + + if (opts->patch_mode) + return run_add_interactive(revision, "--patch=checkout", + opts->pathspec); + + lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 1); if (read_cache_preload(opts->pathspec) < 0) @@ -763,11 +792,6 @@ static int git_checkout_config(const char *var, const char *value, void *cb) return git_xmerge_config(var, value, NULL); } -static int interactive_checkout(const char *revision, const char **pathspec) -{ - return run_add_interactive(revision, "--patch=checkout", pathspec); -} - struct tracking_name_data { const char *name; char *remote; @@ -930,6 +954,51 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) return status; } +static int checkout_branch(struct checkout_opts *opts, + struct branch_info *new) +{ + if (opts->pathspec) + die(_("paths cannot be used with switching branches")); + + if (opts->patch_mode) + die(_("%s cannot be used with switching branches"), + "--patch"); + + if (opts->writeout_stage) + die(_("%s cannot be used with switching branches"), + "--ours/--theirs"); + + if (opts->force && opts->merge) + die(_("%s cannot be used with %s"), "-f", "-m"); + + if (opts->force_detach && opts->new_branch) + die(_("%s cannot be used with %s"), + "--detach", "-b/-B/--orphan"); + + if (opts->new_orphan_branch) { + if (opts->track != BRANCH_TRACK_UNSPECIFIED) + die(_("%s cannot be used with %s"), "--orphan", "-t"); + } else if (opts->force_detach) { + if (opts->track != BRANCH_TRACK_UNSPECIFIED) + die(_("%s cannot be used with %s"), "--detach", "-t"); + } else if (opts->track == BRANCH_TRACK_UNSPECIFIED) + opts->track = git_branch_track; + + if (new->name && !new->commit) + die(_("Cannot switch branch to a non-commit '%s'."), + new->name); + + if (!new->commit && opts->new_branch) { + unsigned char rev[20]; + int flag; + + if (!read_ref_full("HEAD", rev, 0, &flag) && + (flag & REF_ISSYMREF) && is_null_sha1(rev)) + return switch_unborn_to_new_branch(opts); + } + return switch_branches(opts, new); +} + int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; @@ -976,26 +1045,22 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - /* we can assume from now on new_branch = !new_branch_force */ - if (opts.new_branch && opts.new_branch_force) - die(_("-B cannot be used with -b")); + if (conflict_style) { + opts.merge = 1; /* implied */ + git_xmerge_config("merge.conflictstyle", conflict_style, NULL); + } + + if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1) + die(_("-b, -B and --orphan are mutually exclusive")); - /* copy -B over to -b, so that we can just check the latter */ if (opts.new_branch_force) opts.new_branch = opts.new_branch_force; - if (opts.patch_mode && (opts.track > 0 || opts.new_branch - || opts.new_branch_log || opts.merge || opts.force - || opts.force_detach)) - die (_("--patch is incompatible with all other options")); - - if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch)) - die(_("--detach cannot be used with -b/-B/--orphan")); - if (opts.force_detach && 0 < opts.track) - die(_("--detach cannot be used with -t")); + if (opts.new_orphan_branch) + opts.new_branch = opts.new_orphan_branch; /* --track without -b should DWIM */ - if (0 < opts.track && !opts.new_branch) { + if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) { const char *argv0 = argv[0]; if (!argc || !strcmp(argv0, "--")) die (_("--track needs a branch name")); @@ -1009,22 +1074,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.new_branch = argv0 + 1; } - if (opts.new_orphan_branch) { - if (opts.new_branch) - die(_("--orphan and -b|-B are mutually exclusive")); - if (opts.track > 0) - die(_("--orphan cannot be used with -t")); - opts.new_branch = opts.new_orphan_branch; - } - - if (conflict_style) { - opts.merge = 1; /* implied */ - git_xmerge_config("merge.conflictstyle", conflict_style, NULL); - } - - if (opts.force && opts.merge) - die(_("git checkout: -f and -m are incompatible")); - /* * Extract branch name from command line arguments, so * all that is left is pathspecs. @@ -1052,62 +1101,40 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc -= n; } - if (opts.track == BRANCH_TRACK_UNSPECIFIED) - opts.track = git_branch_track; - if (argc) { opts.pathspec = get_pathspec(prefix, argv); if (!opts.pathspec) die(_("invalid path specification")); - if (opts.patch_mode) - return interactive_checkout(new.name, opts.pathspec); - - /* Checkout paths */ - if (opts.new_branch) { - if (argc == 1) { - 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]); - } else { - die(_("git checkout: updating paths is incompatible with switching branches.")); - } - } + /* Try to give more helpful suggestion, new_branch && + argc > 1 will be caught later */ + if (opts.new_branch && argc == 1) + die(_("Cannot update paths and switch to branch '%s' at the same time.\n" + "Did you intend to checkout '%s' which can not be resolved as commit?"), + opts.new_branch, argv[0]); if (opts.force_detach) die(_("git checkout: --detach does not take a path argument")); if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge) - die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.")); - - return checkout_paths(&opts); + die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n" + "checking out of the index.")); } - if (opts.patch_mode) - return interactive_checkout(new.name, NULL); - if (opts.new_branch) { struct strbuf buf = STRBUF_INIT; - opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, - !!opts.new_branch_force, - !!opts.new_branch_force); + opts.branch_exists = + validate_new_branchname(opts.new_branch, &buf, + !!opts.new_branch_force, + !!opts.new_branch_force); strbuf_release(&buf); } - if (new.name && !new.commit) { - die(_("Cannot switch branch to a non-commit.")); - } - if (opts.writeout_stage) - die(_("--ours/--theirs is incompatible with switching branches.")); - - if (!new.commit && opts.new_branch) { - unsigned char rev[20]; - int flag; - - if (!read_ref_full("HEAD", rev, 0, &flag) && - (flag & REF_ISSYMREF) && is_null_sha1(rev)) - return switch_unborn_to_new_branch(&opts); - } - return switch_branches(&opts, &new); + if (opts.patch_mode || opts.pathspec) + return checkout_paths(&opts, new.name); + else + return checkout_branch(&opts, &new); } -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v3 3/3] checkout: reorder option handling 2012-08-30 12:45 ` [PATCH v3 " Nguyễn Thái Ngọc Duy @ 2012-08-30 16:10 ` Junio C Hamano 2012-09-07 20:19 ` Junio C Hamano 1 sibling, 0 replies; 16+ messages in thread From: Junio C Hamano @ 2012-08-30 16:10 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > Changes since v2 (the first two patches are not resent): > ... > - merge cmd_checkout_entry() into checkout_paths() I didn't think of this when I reviewed the previous one; I think it makes sense. Thanks. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v3 3/3] checkout: reorder option handling 2012-08-30 12:45 ` [PATCH v3 " Nguyễn Thái Ngọc Duy 2012-08-30 16:10 ` Junio C Hamano @ 2012-09-07 20:19 ` Junio C Hamano 1 sibling, 0 replies; 16+ messages in thread From: Junio C Hamano @ 2012-09-07 20:19 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > + if (opts->track != BRANCH_TRACK_UNSPECIFIED) > + die(_("%s cannot be used with updating paths"), "--track"); I think most of the places we try to enclose these literals inside quotes, so I'd squash in a patch to make this: die(_("'%s' cannot be used with updating paths"), "--track"); and update others to match. > + if (new->name && !new->commit) > + die(_("Cannot switch branch to a non-commit '%s'."), > + new->name); This is one of the only few places that end a sentence with a full-stop. > + /* Try to give more helpful suggestion, new_branch && > + argc > 1 will be caught later */ Style; > + if (opts.new_branch && argc == 1) > + die(_("Cannot update paths and switch to branch '%s' at the same time.\n" > + "Did you intend to checkout '%s' which can not be resolved as commit?"), > + opts.new_branch, argv[0]); > > if (opts.force_detach) > die(_("git checkout: --detach does not take a path argument")); I tried this codepath and the message left me feeling uneasy. I'd do: die(_("git checkout: --detach does not take a path argument '%s'"), argv[0]); Other than these, I didn't find anything obviously wrong with my final review before merging to 'next', so I'll merge it soonish. Thanks. ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 3/3] checkout: move branch guessing code out as a separate function 2012-08-28 13:49 ` [PATCH 0/3] Refactor checkout option handling Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 2/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy @ 2012-08-28 13:49 ` Nguyễn Thái Ngọc Duy 2 siblings, 0 replies; 16+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-08-28 13:49 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy This makes cmd_checkout() shorter, therefore easier to get the big picture. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- In general I like it, except that parse_branchname_arg() pulls so many options from cmd_checkout(). I would rather have update_new_branch() that only takes "struct checkout_opts *" and returns a new branch. So I'm not sure if we should do this. If we do, perhaps we can merge it into the previous patch. builtin/checkout.c | 184 ++++++++++++++++++++++++++++------------------------- 1 file changed, 96 insertions(+), 88 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 3f1a436..e9d503d 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -914,6 +914,99 @@ static int parse_branchname_arg(int argc, const char **argv, return argcount; } +static const char *update_new_branch(const struct checkout_opts *opts, + int argc, const char **argv, + const char *prefix, const char ***pathspec, + int dwim_ok, + struct branch_info *new, + struct tree **source_tree, + unsigned char *rev) +{ + const char *branch = opts->new_branch; + + if (opts->new_branch_force) { + /* we can assume from now on new_branch = !new_branch_force */ + if (branch) + die(_("New branch '%s' is already specified by other options"), + branch); + + /* copy -B over to -b, so that we can just check the latter */ + if (opts->new_branch_force) + branch = opts->new_branch_force; + } + + if (opts->new_orphan_branch) { + if (opts->track > 0) + die(_("--orphan cannot be used with -t")); + if (branch) + die(_("New branch '%s' is already specified by other options"), + branch); + branch = opts->new_orphan_branch; + } + + /* --track without -b should DWIM */ + if (0 < opts->track && !branch) { + const char *argv0 = argv[0]; + if (!argc || !strcmp(argv0, "--")) + die (_("--track needs a branch name")); + if (!prefixcmp(argv0, "refs/")) + argv0 += 5; + if (!prefixcmp(argv0, "remotes/")) + argv0 += 8; + argv0 = strchr(argv0, '/'); + if (!argv0 || !argv0[1]) + die (_("Missing branch name; try -b")); + branch = argv0 + 1; + } + + /* + * Extract branch name from command line arguments, so + * all that is left is pathspecs. + * + * Handle + * + * 1) git checkout <tree> -- [<paths>] + * 2) git checkout -- [<paths>] + * 3) git checkout <something> [<paths>] + * + * including "last branch" syntax and DWIM-ery for names of + * remote branches, erroring out for invalid or ambiguous cases. + */ + if (argc) { + dwim_ok = dwim_ok && + opts->track == BRANCH_TRACK_UNSPECIFIED && + !branch; + int n = parse_branchname_arg(argc, argv, dwim_ok, + new, source_tree, rev, &branch); + argv += n; + argc -= n; + } + + /* After DWIM, these must be pathspec */ + if (argc) { + *pathspec = get_pathspec(prefix, argv); + + if (!*pathspec) + die(_("invalid path specification")); + + /* Try to give more helpful suggestion, new_branch && + argc > 1 will be caught later */ + if (branch && argc == 1) + die(_("Cannot update paths and switch to branch '%s' at the same time.\n" + "Did you intend to checkout '%s' which can not be resolved as commit?"), + branch, argv[0]); + + if (opts->force_detach) + die(_("git checkout: --detach does not take a path argument")); + + 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.")); + } + + return branch; +} + static int switch_unborn_to_new_branch(const struct checkout_opts *opts) { int status; @@ -989,94 +1082,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) git_xmerge_config("merge.conflictstyle", conflict_style, NULL); } - /* - * opts.new_branch calculation, it could be from -b or -B or - * --track or --orphan or other command line arguments. - * - * The following code until new branch validation should not - * update any fields in opts but opts.new_branch. - */ - if (opts.new_branch_force) { - /* we can assume from now on new_branch = !new_branch_force */ - if (opts.new_branch) - die(_("New branch '%s' is already specified by other options"), - opts.new_branch); - - /* copy -B over to -b, so that we can just check the latter */ - if (opts.new_branch_force) - opts.new_branch = opts.new_branch_force; - } - - if (opts.new_orphan_branch) { - if (opts.track > 0) - die(_("--orphan cannot be used with -t")); - if (opts.new_branch) - die(_("New branch '%s' is already specified by other options"), - opts.new_branch); - opts.new_branch = opts.new_orphan_branch; - } - - /* --track without -b should DWIM */ - if (0 < opts.track && !opts.new_branch) { - const char *argv0 = argv[0]; - if (!argc || !strcmp(argv0, "--")) - die (_("--track needs a branch name")); - if (!prefixcmp(argv0, "refs/")) - argv0 += 5; - if (!prefixcmp(argv0, "remotes/")) - argv0 += 8; - argv0 = strchr(argv0, '/'); - if (!argv0 || !argv0[1]) - die (_("Missing branch name; try -b")); - opts.new_branch = argv0 + 1; - } - - /* - * Extract branch name from command line arguments, so - * all that is left is pathspecs. - * - * Handle - * - * 1) git checkout <tree> -- [<paths>] - * 2) git checkout -- [<paths>] - * 3) git checkout <something> [<paths>] - * - * including "last branch" syntax and DWIM-ery for names of - * remote branches, erroring out for invalid or ambiguous cases. - */ - if (argc) { - int dwim_ok = - !patch_mode && - dwim_new_local_branch && - opts.track == BRANCH_TRACK_UNSPECIFIED && - !opts.new_branch; - int n = parse_branchname_arg(argc, argv, dwim_ok, - &new, &source_tree, rev, &opts.new_branch); - argv += n; - argc -= n; - } - - /* After DWIM, these must be pathspec */ - if (argc) { - pathspec = get_pathspec(prefix, argv); - - if (!pathspec) - die(_("invalid path specification")); - - /* Try to give more helpful suggestion, new_branch && - argc > 1 will be caught later */ - if (opts.new_branch && argc == 1) - die(_("Cannot update paths and switch to branch '%s' at the same time.\n" - "Did you intend to checkout '%s' which can not be resolved as commit?"), - opts.new_branch, argv[0]); - - if (opts.force_detach) - die(_("git checkout: --detach does not take a path argument")); - - 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.")); - } + opts.new_branch = update_new_branch(&opts, argc, argv, prefix, &pathspec, + !patch_mode && dwim_new_local_branch, + &new, &source_tree, rev); /* new branch calculation done, validate it */ if (opts.new_branch) { -- 1.7.12.rc2.18.g61b472e ^ permalink raw reply related [flat|nested] 16+ messages in thread
end of thread, other threads:[~2012-09-07 20:19 UTC | newest] Thread overview: 16+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-08-28 2:29 [PATCH] checkout: verify new branch name's validity early Nguyễn Thái Ngọc Duy 2012-08-28 3:55 ` Junio C Hamano 2012-08-28 13:49 ` [PATCH 0/3] Refactor checkout option handling Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy 2012-08-28 13:49 ` [PATCH 2/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy 2012-08-28 20:45 ` Junio C Hamano 2012-08-29 12:16 ` Nguyen Thai Ngoc Duy 2012-08-29 13:55 ` [PATCH v2 1/3] checkout: pass "struct checkout_opts *" as const pointer Nguyễn Thái Ngọc Duy 2012-08-29 13:55 ` [PATCH v2 2/3] checkout: move more parameters to struct checkout_opts Nguyễn Thái Ngọc Duy 2012-08-29 13:55 ` [PATCH v2 3/3] checkout: reorder option handling Nguyễn Thái Ngọc Duy 2012-08-29 18:37 ` Junio C Hamano 2012-08-29 18:55 ` Junio C Hamano 2012-08-30 12:45 ` [PATCH v3 " Nguyễn Thái Ngọc Duy 2012-08-30 16:10 ` Junio C Hamano 2012-09-07 20:19 ` Junio C Hamano 2012-08-28 13:49 ` [PATCH 3/3] checkout: move branch guessing code out as a separate function Nguyễn Thái Ngọc Duy
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).