#include "blob.h"
#include "tree-walk.h"
#include "refs.h"
+#include "remote.h"
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
{
/*
* *string and *len will only be substituted, and *string returned (for
- * later free()ing) if the string passed in is of the form @{-<n>}.
+ * later free()ing) if the string passed in is a magic short-hand form
+ * to name a branch.
*/
static char *substitute_branch_name(const char **string, int *len)
{
*ref = xstrdup(r);
if (!warn_ambiguous_refs)
break;
- } else if ((flag & REF_ISSYMREF) &&
- (len != 4 || strcmp(str, "HEAD")))
+ } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD"))
warning("ignoring dangling symref %s.", fullref);
}
free(last_branch);
return logs_found;
}
+static inline int upstream_mark(const char *string, int len)
+{
+ const char *suffix[] = { "@{upstream}", "@{u}" };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+ int suffix_len = strlen(suffix[i]);
+ if (suffix_len <= len
+ && !memcmp(string, suffix[i], suffix_len))
+ return suffix_len;
+ }
+ return 0;
+}
+
static int get_sha1_1(const char *name, int len, unsigned char *sha1);
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
if (len && str[len-1] == '}') {
for (at = len-2; at >= 0; at--) {
if (str[at] == '@' && str[at+1] == '{') {
- reflog_len = (len-1) - (at+2);
- len = at;
+ if (!upstream_mark(str + at, len - at)) {
+ reflog_len = (len-1) - (at+2);
+ len = at;
+ }
break;
}
}
unsigned long co_time;
int co_tz, co_cnt;
+ /* a @{-N} placed anywhere except the start is an error */
+ if (str[at+2] == '-')
+ return -1;
+
/* Is it asking for N-th entry, or approxidate? */
for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
char ch = str[at+2+i];
} else if (0 <= nth)
at_time = 0;
else {
+ int errors = 0;
char *tmp = xstrndup(str + at + 2, reflog_len);
- at_time = approxidate(tmp);
+ at_time = approxidate_careful(tmp, &errors);
free(tmp);
+ if (errors)
+ return -1;
}
if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
&co_time, &co_tz, &co_cnt)) {
}
/*
- * This reads "@{-N}" syntax, finds the name of the Nth previous
- * branch we were on, and places the name of the branch in the given
- * buf and returns the number of characters parsed if successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
+ * Parse @{-N} syntax, return the number of characters parsed
+ * if successful; otherwise signal an error with negative value.
*/
-int interpret_branch_name(const char *name, struct strbuf *buf)
+static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
{
long nth;
int i, retval;
return st;
}
+/*
+ * 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
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ * places the name of the branch in the given buf and returns the
+ * number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ * <branch> is configured to merge with (missing <branch> defaults
+ * to the current branch), and places the name of the branch in the
+ * given buf and returns the number of characters parsed if
+ * successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * 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)
+{
+ char *cp;
+ struct branch *upstream;
+ int namelen = strlen(name);
+ int len = interpret_nth_prior_checkout(name, buf);
+ int tmp_len;
+
+ if (!len)
+ return len; /* syntax Ok, not enough switches */
+ if (0 < len && len == namelen)
+ return len; /* consumed all */
+ else if (0 < len) {
+ /* we have extra data, which might need further processing */
+ struct strbuf tmp = STRBUF_INIT;
+ int used = buf->len;
+ int ret;
+
+ strbuf_add(buf, name + len, namelen - len);
+ ret = interpret_branch_name(buf->buf, &tmp);
+ /* that data was not interpreted, remove our cruft */
+ if (ret < 0) {
+ strbuf_setlen(buf, used);
+ return len;
+ }
+ strbuf_reset(buf);
+ strbuf_addbuf(buf, &tmp);
+ strbuf_release(&tmp);
+ /* tweak for size of {-N} versus expanded ref name */
+ return ret - used + len;
+ }
+
+ 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);
+ if (!upstream
+ || !upstream->merge
+ || !upstream->merge[0]->dst)
+ return error("No upstream branch found for '%s'", cp);
+ free(cp);
+ cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
+ strbuf_reset(buf);
+ strbuf_addstr(buf, cp);
+ free(cp);
+ return len;
+}
+
/*
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
* notably "xyz^" for "parent of xyz"