Merge branch 'bw/shortref'
* bw/shortref: for-each-ref: `:short` format for `refname`
This commit is contained in:
commit
8435bdfdc3
@ -74,6 +74,7 @@ For all objects, the following names can be used:
|
||||
|
||||
refname::
|
||||
The name of the ref (the part after $GIT_DIR/).
|
||||
For a non-ambiguous short name of the ref append `:short`.
|
||||
|
||||
objecttype::
|
||||
The type of the object (`blob`, `tree`, `commit`, `tag`).
|
||||
|
@ -545,6 +545,107 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* generate a format suitable for scanf from a ref_rev_parse_rules
|
||||
* rule, that is replace the "%.*s" spec with a "%s" spec
|
||||
*/
|
||||
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
|
||||
{
|
||||
char *spec;
|
||||
|
||||
spec = strstr(rule, "%.*s");
|
||||
if (!spec || strstr(spec + 4, "%.*s"))
|
||||
die("invalid rule in ref_rev_parse_rules: %s", rule);
|
||||
|
||||
/* copy all until spec */
|
||||
strncpy(scanf_fmt, rule, spec - rule);
|
||||
scanf_fmt[spec - rule] = '\0';
|
||||
/* copy new spec */
|
||||
strcat(scanf_fmt, "%s");
|
||||
/* copy remaining rule */
|
||||
strcat(scanf_fmt, spec + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shorten the refname to an non-ambiguous form
|
||||
*/
|
||||
static char *get_short_ref(struct refinfo *ref)
|
||||
{
|
||||
int i;
|
||||
static char **scanf_fmts;
|
||||
static int nr_rules;
|
||||
char *short_name;
|
||||
|
||||
/* pre generate scanf formats from ref_rev_parse_rules[] */
|
||||
if (!nr_rules) {
|
||||
size_t total_len = 0;
|
||||
|
||||
/* the rule list is NULL terminated, count them first */
|
||||
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
|
||||
/* no +1 because strlen("%s") < strlen("%.*s") */
|
||||
total_len += strlen(ref_rev_parse_rules[nr_rules]);
|
||||
|
||||
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
|
||||
|
||||
total_len = 0;
|
||||
for (i = 0; i < nr_rules; i++) {
|
||||
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
|
||||
+ total_len;
|
||||
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
|
||||
total_len += strlen(ref_rev_parse_rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* bail out if there are no rules */
|
||||
if (!nr_rules)
|
||||
return ref->refname;
|
||||
|
||||
/* buffer for scanf result, at most ref->refname must fit */
|
||||
short_name = xstrdup(ref->refname);
|
||||
|
||||
/* skip first rule, it will always match */
|
||||
for (i = nr_rules - 1; i > 0 ; --i) {
|
||||
int j;
|
||||
int short_name_len;
|
||||
|
||||
if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
|
||||
continue;
|
||||
|
||||
short_name_len = strlen(short_name);
|
||||
|
||||
/*
|
||||
* check if the short name resolves to a valid ref,
|
||||
* but use only rules prior to the matched one
|
||||
*/
|
||||
for (j = 0; j < i; j++) {
|
||||
const char *rule = ref_rev_parse_rules[j];
|
||||
unsigned char short_objectname[20];
|
||||
|
||||
/*
|
||||
* the short name is ambiguous, if it resolves
|
||||
* (with this previous rule) to a valid ref
|
||||
* read_ref() returns 0 on success
|
||||
*/
|
||||
if (!read_ref(mkpath(rule, short_name_len, short_name),
|
||||
short_objectname))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* short name is non-ambiguous if all previous rules
|
||||
* haven't resolved to a valid ref
|
||||
*/
|
||||
if (j == i)
|
||||
return short_name;
|
||||
}
|
||||
|
||||
free(short_name);
|
||||
return ref->refname;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse the object referred by ref, and grab needed value.
|
||||
*/
|
||||
@ -570,13 +671,33 @@ static void populate_value(struct refinfo *ref)
|
||||
for (i = 0; i < used_atom_cnt; i++) {
|
||||
const char *name = used_atom[i];
|
||||
struct atom_value *v = &ref->value[i];
|
||||
if (!strcmp(name, "refname"))
|
||||
v->s = ref->refname;
|
||||
else if (!strcmp(name, "*refname")) {
|
||||
int len = strlen(ref->refname);
|
||||
char *s = xmalloc(len + 4);
|
||||
sprintf(s, "%s^{}", ref->refname);
|
||||
v->s = s;
|
||||
int deref = 0;
|
||||
if (*name == '*') {
|
||||
deref = 1;
|
||||
name++;
|
||||
}
|
||||
if (!prefixcmp(name, "refname")) {
|
||||
const char *formatp = strchr(name, ':');
|
||||
const char *refname = ref->refname;
|
||||
|
||||
/* look for "short" refname format */
|
||||
if (formatp) {
|
||||
formatp++;
|
||||
if (!strcmp(formatp, "short"))
|
||||
refname = get_short_ref(ref);
|
||||
else
|
||||
die("unknown refname format %s",
|
||||
formatp);
|
||||
}
|
||||
|
||||
if (!deref)
|
||||
v->s = refname;
|
||||
else {
|
||||
int len = strlen(refname);
|
||||
char *s = xmalloc(len + 4);
|
||||
sprintf(s, "%s^{}", refname);
|
||||
v->s = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,6 +262,50 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
|
||||
"
|
||||
done
|
||||
|
||||
cat >expected <<\EOF
|
||||
master
|
||||
testtag
|
||||
EOF
|
||||
|
||||
test_expect_success 'Check short refname format' '
|
||||
(git for-each-ref --format="%(refname:short)" refs/heads &&
|
||||
git for-each-ref --format="%(refname:short)" refs/tags) >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'Check for invalid refname format' '
|
||||
test_must_fail git for-each-ref --format="%(refname:INVALID)"
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
heads/master
|
||||
master
|
||||
EOF
|
||||
|
||||
test_expect_success 'Check ambiguous head and tag refs' '
|
||||
git checkout -b newtag &&
|
||||
echo "Using $datestamp" > one &&
|
||||
git add one &&
|
||||
git commit -m "Branch" &&
|
||||
setdate_and_increment &&
|
||||
git tag -m "Tagging at $datestamp" master &&
|
||||
git for-each-ref --format "%(refname:short)" refs/heads/master refs/tags/master >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
heads/ambiguous
|
||||
ambiguous
|
||||
EOF
|
||||
|
||||
test_expect_success 'Check ambiguous head and tag refs II' '
|
||||
git checkout master &&
|
||||
git tag ambiguous testtag^0 &&
|
||||
git branch ambiguous testtag^0 &&
|
||||
git for-each-ref --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'an unusual tag with an incomplete line' '
|
||||
|
||||
git tag -m "bogo" bogo &&
|
||||
|
Loading…
Reference in New Issue
Block a user