diff options
Diffstat (limited to 'sha1_name.c')
-rw-r--r-- | sha1_name.c | 160 |
1 files changed, 110 insertions, 50 deletions
diff --git a/sha1_name.c b/sha1_name.c index 65ad066d9b..15854e35ec 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -343,7 +343,6 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1, return status; } - int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data) { char hex_pfx[40]; @@ -431,7 +430,7 @@ static inline int upstream_mark(const char *string, int len) } static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags); -static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf); +static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf); static int get_sha1_basic(const char *str, int len, unsigned char *sha1) { @@ -493,7 +492,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) struct strbuf buf = STRBUF_INIT; int detached; - if (interpret_nth_prior_checkout(str, &buf) > 0) { + if (interpret_nth_prior_checkout(str, len, &buf) > 0) { detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1)); strbuf_release(&buf); if (detached) @@ -677,11 +676,13 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) return -1; sp++; /* beginning of type name, or closing brace for empty */ - if (!strncmp(commit_type, sp, 6) && sp[6] == '}') + if (!prefixcmp(sp, "commit}")) expected_type = OBJ_COMMIT; - else if (!strncmp(tree_type, sp, 4) && sp[4] == '}') + else if (!prefixcmp(sp, "tag}")) + expected_type = OBJ_TAG; + else if (!prefixcmp(sp, "tree}")) expected_type = OBJ_TREE; - else if (!strncmp(blob_type, sp, 4) && sp[4] == '}') + else if (!prefixcmp(sp, "blob}")) expected_type = OBJ_BLOB; else if (!prefixcmp(sp, "object}")) expected_type = OBJ_ANY; @@ -930,7 +931,8 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, * Parse @{-N} syntax, return the number of characters parsed * if successful; otherwise signal an error with negative value. */ -static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf) +static int interpret_nth_prior_checkout(const char *name, int namelen, + struct strbuf *buf) { long nth; int retval; @@ -938,9 +940,11 @@ static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf) const char *brace; char *num_end; + if (namelen < 4) + return -1; if (name[0] != '@' || name[1] != '{' || name[2] != '-') return -1; - brace = strchr(name, '}'); + brace = memchr(name, '}', namelen); if (!brace) return -1; nth = strtol(name + 3, &num_end, 10); @@ -1004,6 +1008,28 @@ int get_sha1_mb(const char *name, unsigned char *sha1) return st; } +/* parse @something syntax, when 'something' is not {.*} */ +static int interpret_empty_at(const char *name, int namelen, int len, struct strbuf *buf) +{ + const char *next; + + if (len || name[1] == '{') + return -1; + + /* make sure it's a single @, or @@{.*}, not @foo */ + next = memchr(name + len + 1, '@', namelen - len - 1); + if (next && next[1] != '{') + return -1; + if (!next) + next = name + namelen; + if (next != name + 1) + return -1; + + strbuf_reset(buf); + strbuf_add(buf, "HEAD", 4); + return 1; +} + static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf) { /* we have extra data, which might need further processing */ @@ -1012,7 +1038,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu int ret; strbuf_add(buf, name + len, namelen - len); - ret = interpret_branch_name(buf->buf, &tmp); + ret = interpret_branch_name(buf->buf, buf->len, &tmp); /* that data was not interpreted, remove our cruft */ if (ret < 0) { strbuf_setlen(buf, used); @@ -1025,6 +1051,57 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu return ret - used + len; } +static void set_shortened_ref(struct strbuf *buf, const char *ref) +{ + char *s = shorten_unambiguous_ref(ref, 0); + strbuf_reset(buf); + strbuf_addstr(buf, s); + free(s); +} + +static const char *get_upstream_branch(const char *branch_buf, int len) +{ + char *branch = xstrndup(branch_buf, len); + struct branch *upstream = branch_get(*branch ? branch : NULL); + + /* + * Upstream can be NULL only if branch refers to HEAD and HEAD + * points to something different than a branch. + */ + if (!upstream) + die(_("HEAD does not point to a branch")); + if (!upstream->merge || !upstream->merge[0]->dst) { + if (!ref_exists(upstream->refname)) + die(_("No such branch: '%s'"), branch); + if (!upstream->merge) { + die(_("No upstream configured for branch '%s'"), + upstream->name); + } + die( + _("Upstream branch '%s' not stored as a remote-tracking branch"), + upstream->merge[0]->src); + } + free(branch); + + return upstream->merge[0]->dst; +} + +static int interpret_upstream_mark(const char *name, int namelen, + int at, struct strbuf *buf) +{ + int len; + + len = upstream_mark(name + at, namelen - at); + if (!len) + return -1; + + if (memchr(name, ':', at)) + return -1; + + set_shortened_ref(buf, get_upstream_branch(name, at)); + return len + at; +} + /* * This reads short-hand syntax that not only evaluates to a commit * object name, but also can act as if the end user spelled the name @@ -1046,13 +1123,14 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu * If the input was ok but there are not N branch switches in the * reflog, it returns 0. */ -int interpret_branch_name(const char *name, struct strbuf *buf) +int interpret_branch_name(const char *name, int namelen, struct strbuf *buf) { - char *cp; - struct branch *upstream; - int namelen = strlen(name); - int len = interpret_nth_prior_checkout(name, buf); - int tmp_len; + char *at; + const char *start; + int len = interpret_nth_prior_checkout(name, namelen, buf); + + if (!namelen) + namelen = strlen(name); if (!len) { return len; /* syntax Ok, not enough switches */ @@ -1063,44 +1141,26 @@ int interpret_branch_name(const char *name, struct strbuf *buf) return reinterpret(name, namelen, len, buf); } - cp = strchr(name, '@'); - if (!cp) - return -1; - tmp_len = upstream_mark(cp, namelen - (cp - name)); - if (!tmp_len) - return -1; - len = cp + tmp_len - name; - cp = xstrndup(name, cp - name); - upstream = branch_get(*cp ? cp : NULL); - /* - * Upstream can be NULL only if cp refers to HEAD and HEAD - * points to something different than a branch. - */ - if (!upstream) - die(_("HEAD does not point to a branch")); - if (!upstream->merge || !upstream->merge[0]->dst) { - if (!ref_exists(upstream->refname)) - die(_("No such branch: '%s'"), cp); - if (!upstream->merge) { - die(_("No upstream configured for branch '%s'"), - upstream->name); - } - die( - _("Upstream branch '%s' not stored as a remote-tracking branch"), - upstream->merge[0]->src); + for (start = name; + (at = memchr(start, '@', namelen - (start - name))); + start = at + 1) { + + len = interpret_empty_at(name, namelen, at - name, buf); + if (len > 0) + return reinterpret(name, namelen, len, buf); + + len = interpret_upstream_mark(name, namelen, at - name, buf); + if (len > 0) + return len; } - free(cp); - cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0); - strbuf_reset(buf); - strbuf_addstr(buf, cp); - free(cp); - return len; + + return -1; } int strbuf_branchname(struct strbuf *sb, const char *name) { int len = strlen(name); - int used = interpret_branch_name(name, sb); + int used = interpret_branch_name(name, len, sb); if (used == len) return 0; @@ -1130,13 +1190,13 @@ int get_sha1(const char *name, unsigned char *sha1) } /* - * Many callers know that the user meant to name a committish by + * Many callers know that the user meant to name a commit-ish by * syntactical positions where the object name appears. Calling this * function allows the machinery to disambiguate shorter-than-unique - * abbreviated object names between committish and others. + * abbreviated object names between commit-ish and others. * * Note that this does NOT error out when the named object is not a - * committish. It is merely to give a hint to the disambiguation + * commit-ish. It is merely to give a hint to the disambiguation * machinery. */ int get_sha1_committish(const char *name, unsigned char *sha1) |