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::
|
refname::
|
||||||
The name of the ref (the part after $GIT_DIR/).
|
The name of the ref (the part after $GIT_DIR/).
|
||||||
|
For a non-ambiguous short name of the ref append `:short`.
|
||||||
|
|
||||||
objecttype::
|
objecttype::
|
||||||
The type of the object (`blob`, `tree`, `commit`, `tag`).
|
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.
|
* Parse the object referred by ref, and grab needed value.
|
||||||
*/
|
*/
|
||||||
@ -570,15 +671,35 @@ static void populate_value(struct refinfo *ref)
|
|||||||
for (i = 0; i < used_atom_cnt; i++) {
|
for (i = 0; i < used_atom_cnt; i++) {
|
||||||
const char *name = used_atom[i];
|
const char *name = used_atom[i];
|
||||||
struct atom_value *v = &ref->value[i];
|
struct atom_value *v = &ref->value[i];
|
||||||
if (!strcmp(name, "refname"))
|
int deref = 0;
|
||||||
v->s = ref->refname;
|
if (*name == '*') {
|
||||||
else if (!strcmp(name, "*refname")) {
|
deref = 1;
|
||||||
int len = strlen(ref->refname);
|
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);
|
char *s = xmalloc(len + 4);
|
||||||
sprintf(s, "%s^{}", ref->refname);
|
sprintf(s, "%s^{}", refname);
|
||||||
v->s = s;
|
v->s = s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
grab_values(ref->value, 0, obj, buf, size);
|
grab_values(ref->value, 0, obj, buf, size);
|
||||||
if (!eaten)
|
if (!eaten)
|
||||||
|
@ -262,6 +262,50 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
|
|||||||
"
|
"
|
||||||
done
|
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' '
|
test_expect_success 'an unusual tag with an incomplete line' '
|
||||||
|
|
||||||
git tag -m "bogo" bogo &&
|
git tag -m "bogo" bogo &&
|
||||||
|
Loading…
Reference in New Issue
Block a user