Merge branch 'bw/shortref'

* bw/shortref:
  for-each-ref: `:short` format for `refname`
This commit is contained in:
Junio C Hamano 2008-09-18 20:18:44 -07:00
commit 8435bdfdc3
3 changed files with 173 additions and 7 deletions

View File

@ -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`).

View File

@ -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;
}
}
}

View File

@ -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 &&