From: Jonathan Nieder <jrnieder@gmail.com>
To: Jeff King <peff@peff.net>
Cc: Junio C Hamano <gitster@pobox.com>,
Sverre Rabbelier <srabbelier@gmail.com>,
Martin von Zweigbergk <martin.von.zweigbergk@gmail.com>,
git@vger.kernel.org
Subject: [PATCH 1/3] checkout: split off a function to peel away branchname arg
Date: Tue, 8 Feb 2011 04:29:09 -0600 [thread overview]
Message-ID: <20110208102909.GB29660@elie> (raw)
In-Reply-To: <20110208102605.GA29660@elie>
The code to parse and consume the tree name and "--" in commands such
as "git checkout @{-1} -- '*.c'" is intimidatingly long. Split it out
into a separate function and make it easier to skip on first reading
by making the data it uses and produces more explicit.
No functional change intended.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Just some code movement.
builtin/checkout.c | 229 ++++++++++++++++++++++++++++++----------------------
1 files changed, 131 insertions(+), 98 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 953abdd..0e7a6a3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -675,11 +675,123 @@ static const char *unique_tracking_name(const char *name)
return NULL;
}
+static int parse_branchname_arg(int argc, const char **argv,
+ int dwim_new_local_branch_ok,
+ struct branch_info *new,
+ struct tree **source_tree,
+ unsigned char rev[20],
+ const char **new_branch)
+{
+ int argcount = 0;
+ unsigned char branch_rev[20];
+ const char *arg;
+ int has_dash_dash;
+
+ /*
+ * case 1: git checkout <ref> -- [<paths>]
+ *
+ * <ref> must be a valid tree, everything after the '--' must be
+ * a path.
+ *
+ * case 2: git checkout -- [<paths>]
+ *
+ * everything after the '--' must be paths.
+ *
+ * case 3: git checkout <something> [<paths>]
+ *
+ * With no paths, if <something> is a commit, that is to
+ * switch to the branch or detach HEAD at it. As a special case,
+ * if <something> is A...B (missing A or B means HEAD but you can
+ * omit at most one side), and if there is a unique merge base
+ * between A and B, A...B names that merge base.
+ *
+ * With no paths, if <something> is _not_ a commit, no -t nor -b
+ * was given, and there is a tracking branch whose name is
+ * <something> in one and only one remote, then this is a short-hand
+ * to fork local <something> from that remote tracking branch.
+ *
+ * Otherwise <something> shall not be ambiguous.
+ * - If it's *only* a reference, treat it like case (1).
+ * - If it's only a path, treat it like case (2).
+ * - else: fail.
+ *
+ */
+ if (!argc)
+ return 0;
+
+ if (!strcmp(argv[0], "--")) /* case (2) */
+ return 1;
+
+ arg = argv[0];
+ has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
+
+ if (!strcmp(arg, "-"))
+ arg = "@{-1}";
+
+ if (get_sha1_mb(arg, rev)) {
+ if (has_dash_dash) /* case (1) */
+ die("invalid reference: %s", arg);
+ if (dwim_new_local_branch_ok &&
+ !check_filename(NULL, arg) &&
+ argc == 1) {
+ const char *remote = unique_tracking_name(arg);
+ if (!remote || get_sha1(remote, rev))
+ return argcount;
+ *new_branch = arg;
+ arg = remote;
+ /* DWIMmed to create local branch */
+ } else {
+ return argcount;
+ }
+ }
+
+ /* we can't end up being in (2) anymore, eat the argument */
+ argcount++;
+ argv++;
+ argc--;
+
+ new->name = arg;
+ setup_branch_path(new);
+
+ if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
+ resolve_ref(new->path, branch_rev, 1, NULL))
+ hashcpy(rev, branch_rev);
+ else
+ new->path = NULL; /* not an existing branch */
+
+ new->commit = lookup_commit_reference_gently(rev, 1);
+ if (!new->commit) {
+ /* not a commit */
+ *source_tree = parse_tree_indirect(rev);
+ } else {
+ parse_commit(new->commit);
+ *source_tree = new->commit->tree;
+ }
+
+ if (!*source_tree) /* case (1): want a tree */
+ die("reference is not a tree: %s", arg);
+ if (!has_dash_dash) {/* case (3 -> 1) */
+ /*
+ * Do not complain the most common case
+ * git checkout branch
+ * even if there happen to be a file called 'branch';
+ * it would be extremely annoying.
+ */
+ if (argc)
+ verify_non_filename(NULL, arg);
+ } else {
+ argcount++;
+ argv++;
+ argc--;
+ }
+
+ return argcount;
+}
+
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
struct checkout_opts opts;
- unsigned char rev[20], branch_rev[20];
- const char *arg;
+ unsigned char rev[20];
struct branch_info new;
struct tree *source_tree = NULL;
char *conflict_style = NULL;
@@ -709,7 +821,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
OPT_END(),
};
- int has_dash_dash;
memset(&opts, 0, sizeof(opts));
memset(&new, 0, sizeof(new));
@@ -766,108 +877,30 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
die("git checkout: -f and -m are incompatible");
/*
- * case 1: git checkout <ref> -- [<paths>]
- *
- * <ref> must be a valid tree, everything after the '--' must be
- * a path.
- *
- * case 2: git checkout -- [<paths>]
- *
- * everything after the '--' must be paths.
- *
- * case 3: git checkout <something> [<paths>]
- *
- * With no paths, if <something> is a commit, that is to
- * switch to the branch or detach HEAD at it. As a special case,
- * if <something> is A...B (missing A or B means HEAD but you can
- * omit at most one side), and if there is a unique merge base
- * between A and B, A...B names that merge base.
- *
- * With no paths, if <something> is _not_ a commit, no -t nor -b
- * was given, and there is a tracking branch whose name is
- * <something> in one and only one remote, then this is a short-hand
- * to fork local <something> from that remote tracking branch.
- *
- * Otherwise <something> shall not be ambiguous.
- * - If it's *only* a reference, treat it like case (1).
- * - If it's only a path, treat it like case (2).
- * - else: fail.
- *
+ * 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) {
- if (!strcmp(argv[0], "--")) { /* case (2) */
- argv++;
- argc--;
- goto no_reference;
- }
-
- arg = argv[0];
- has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
-
- if (!strcmp(arg, "-"))
- arg = "@{-1}";
-
- if (get_sha1_mb(arg, rev)) {
- if (has_dash_dash) /* case (1) */
- die("invalid reference: %s", arg);
- if (!patch_mode &&
- dwim_new_local_branch &&
- opts.track == BRANCH_TRACK_UNSPECIFIED &&
- !opts.new_branch &&
- !check_filename(NULL, arg) &&
- argc == 1) {
- const char *remote = unique_tracking_name(arg);
- if (!remote || get_sha1(remote, rev))
- goto no_reference;
- opts.new_branch = arg;
- arg = remote;
- /* DWIMmed to create local branch */
- }
- else
- goto no_reference;
- }
-
- /* we can't end up being in (2) anymore, eat the argument */
- argv++;
- argc--;
-
- new.name = arg;
- setup_branch_path(&new);
-
- if (check_ref_format(new.path) == CHECK_REF_FORMAT_OK &&
- resolve_ref(new.path, branch_rev, 1, NULL))
- hashcpy(rev, branch_rev);
- else
- new.path = NULL; /* not an existing branch */
-
- if (!(new.commit = lookup_commit_reference_gently(rev, 1))) {
- /* not a commit */
- source_tree = parse_tree_indirect(rev);
- } else {
- parse_commit(new.commit);
- source_tree = new.commit->tree;
- }
-
- if (!source_tree) /* case (1): want a tree */
- die("reference is not a tree: %s", arg);
- if (!has_dash_dash) {/* case (3 -> 1) */
- /*
- * Do not complain the most common case
- * git checkout branch
- * even if there happen to be a file called 'branch';
- * it would be extremely annoying.
- */
- if (argc)
- verify_non_filename(NULL, arg);
- }
- else {
- argv++;
- 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;
}
-no_reference:
-
if (opts.track == BRANCH_TRACK_UNSPECIFIED)
opts.track = git_branch_track;
--
1.7.4
next prev parent reply other threads:[~2011-02-08 10:29 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-07 11:01 [1.8.0] git checkout refs/heads/foo checks out branch foo Martin von Zweigbergk
2011-02-07 20:53 ` Junio C Hamano
2011-02-07 20:59 ` Jeff King
2011-02-07 21:36 ` Sverre Rabbelier
2011-02-07 22:00 ` Jonathan Nieder
2011-02-07 23:37 ` Junio C Hamano
2011-02-07 23:45 ` Jeff King
2011-02-08 0:01 ` Jonathan Nieder
2011-02-08 0:28 ` Jeff King
2011-02-08 0:31 ` Martin von Zweigbergk
2011-02-08 0:52 ` [PATCH/WIP] checkout: introduce --detach synonym for "git checkout foo^{commit}" Jonathan Nieder
2011-02-08 0:55 ` Jonathan Nieder
2011-02-08 10:26 ` [PATCH v2 0/3] " Jonathan Nieder
2011-02-08 10:29 ` Jonathan Nieder [this message]
2011-02-08 10:32 ` [PATCH 2/3] " Jonathan Nieder
2011-02-08 10:34 ` [PATCH 3/3] checkout: rearrange update_refs_for_switch for clarity Jonathan Nieder
2011-02-07 21:11 ` [1.8.0] git checkout refs/heads/foo checks out branch foo Heiko Voigt
2011-02-08 0:22 ` Martin von Zweigbergk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20110208102909.GB29660@elie \
--to=jrnieder@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=martin.von.zweigbergk@gmail.com \
--cc=peff@peff.net \
--cc=srabbelier@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).