Skip to content

Commit 7c2b302

Browse files
peffgitster
authored andcommitted
make get_short_ref a public function
Often we want to shorten a full ref name to something "prettier" to show a user. For example, "refs/heads/master" is often shown simply as "master", or "refs/remotes/origin/master" is shown as "origin/master". Many places in the code use a very simple formula: skip common prefixes like refs/heads, refs/remotes, etc. This is codified in the prettify_ref function. for-each-ref has a more correct (but more expensive) approach: consider the ref lookup rules, and try shortening as much as possible while remaining unambiguous. This patch makes the latter strategy globally available as shorten_unambiguous_ref. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8cae19d commit 7c2b302

File tree

3 files changed

+101
-104
lines changed

3 files changed

+101
-104
lines changed

builtin-for-each-ref.c

Lines changed: 1 addition & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -545,109 +545,6 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
545545
}
546546
}
547547

548-
/*
549-
* generate a format suitable for scanf from a ref_rev_parse_rules
550-
* rule, that is replace the "%.*s" spec with a "%s" spec
551-
*/
552-
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
553-
{
554-
char *spec;
555-
556-
spec = strstr(rule, "%.*s");
557-
if (!spec || strstr(spec + 4, "%.*s"))
558-
die("invalid rule in ref_rev_parse_rules: %s", rule);
559-
560-
/* copy all until spec */
561-
strncpy(scanf_fmt, rule, spec - rule);
562-
scanf_fmt[spec - rule] = '\0';
563-
/* copy new spec */
564-
strcat(scanf_fmt, "%s");
565-
/* copy remaining rule */
566-
strcat(scanf_fmt, spec + 4);
567-
568-
return;
569-
}
570-
571-
/*
572-
* Shorten the refname to an non-ambiguous form
573-
*/
574-
static char *get_short_ref(const char *ref)
575-
{
576-
int i;
577-
static char **scanf_fmts;
578-
static int nr_rules;
579-
char *short_name;
580-
581-
/* pre generate scanf formats from ref_rev_parse_rules[] */
582-
if (!nr_rules) {
583-
size_t total_len = 0;
584-
585-
/* the rule list is NULL terminated, count them first */
586-
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
587-
/* no +1 because strlen("%s") < strlen("%.*s") */
588-
total_len += strlen(ref_rev_parse_rules[nr_rules]);
589-
590-
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
591-
592-
total_len = 0;
593-
for (i = 0; i < nr_rules; i++) {
594-
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
595-
+ total_len;
596-
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
597-
total_len += strlen(ref_rev_parse_rules[i]);
598-
}
599-
}
600-
601-
/* bail out if there are no rules */
602-
if (!nr_rules)
603-
return xstrdup(ref);
604-
605-
/* buffer for scanf result, at most ref must fit */
606-
short_name = xstrdup(ref);
607-
608-
/* skip first rule, it will always match */
609-
for (i = nr_rules - 1; i > 0 ; --i) {
610-
int j;
611-
int short_name_len;
612-
613-
if (1 != sscanf(ref, scanf_fmts[i], short_name))
614-
continue;
615-
616-
short_name_len = strlen(short_name);
617-
618-
/*
619-
* check if the short name resolves to a valid ref,
620-
* but use only rules prior to the matched one
621-
*/
622-
for (j = 0; j < i; j++) {
623-
const char *rule = ref_rev_parse_rules[j];
624-
unsigned char short_objectname[20];
625-
char refname[PATH_MAX];
626-
627-
/*
628-
* the short name is ambiguous, if it resolves
629-
* (with this previous rule) to a valid ref
630-
* read_ref() returns 0 on success
631-
*/
632-
mksnpath(refname, sizeof(refname),
633-
rule, short_name_len, short_name);
634-
if (!read_ref(refname, short_objectname))
635-
break;
636-
}
637-
638-
/*
639-
* short name is non-ambiguous if all previous rules
640-
* haven't resolved to a valid ref
641-
*/
642-
if (j == i)
643-
return short_name;
644-
}
645-
646-
free(short_name);
647-
return xstrdup(ref);
648-
}
649-
650-
651548
/*
652549
* Parse the object referred by ref, and grab needed value.
653550
*/
@@ -704,7 +601,7 @@ static void populate_value(struct refinfo *ref)
704601
if (formatp) {
705602
formatp++;
706603
if (!strcmp(formatp, "short"))
707-
refname = get_short_ref(refname);
604+
refname = shorten_unambiguous_ref(refname);
708605
else
709606
die("unknown %.*s format %s",
710607
(int)(formatp - name), name, formatp);

refs.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,3 +1652,102 @@ struct ref *find_ref_by_name(const struct ref *list, const char *name)
16521652
return (struct ref *)list;
16531653
return NULL;
16541654
}
1655+
1656+
/*
1657+
* generate a format suitable for scanf from a ref_rev_parse_rules
1658+
* rule, that is replace the "%.*s" spec with a "%s" spec
1659+
*/
1660+
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
1661+
{
1662+
char *spec;
1663+
1664+
spec = strstr(rule, "%.*s");
1665+
if (!spec || strstr(spec + 4, "%.*s"))
1666+
die("invalid rule in ref_rev_parse_rules: %s", rule);
1667+
1668+
/* copy all until spec */
1669+
strncpy(scanf_fmt, rule, spec - rule);
1670+
scanf_fmt[spec - rule] = '\0';
1671+
/* copy new spec */
1672+
strcat(scanf_fmt, "%s");
1673+
/* copy remaining rule */
1674+
strcat(scanf_fmt, spec + 4);
1675+
1676+
return;
1677+
}
1678+
1679+
char *shorten_unambiguous_ref(const char *ref)
1680+
{
1681+
int i;
1682+
static char **scanf_fmts;
1683+
static int nr_rules;
1684+
char *short_name;
1685+
1686+
/* pre generate scanf formats from ref_rev_parse_rules[] */
1687+
if (!nr_rules) {
1688+
size_t total_len = 0;
1689+
1690+
/* the rule list is NULL terminated, count them first */
1691+
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
1692+
/* no +1 because strlen("%s") < strlen("%.*s") */
1693+
total_len += strlen(ref_rev_parse_rules[nr_rules]);
1694+
1695+
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
1696+
1697+
total_len = 0;
1698+
for (i = 0; i < nr_rules; i++) {
1699+
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
1700+
+ total_len;
1701+
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
1702+
total_len += strlen(ref_rev_parse_rules[i]);
1703+
}
1704+
}
1705+
1706+
/* bail out if there are no rules */
1707+
if (!nr_rules)
1708+
return xstrdup(ref);
1709+
1710+
/* buffer for scanf result, at most ref must fit */
1711+
short_name = xstrdup(ref);
1712+
1713+
/* skip first rule, it will always match */
1714+
for (i = nr_rules - 1; i > 0 ; --i) {
1715+
int j;
1716+
int short_name_len;
1717+
1718+
if (1 != sscanf(ref, scanf_fmts[i], short_name))
1719+
continue;
1720+
1721+
short_name_len = strlen(short_name);
1722+
1723+
/*
1724+
* check if the short name resolves to a valid ref,
1725+
* but use only rules prior to the matched one
1726+
*/
1727+
for (j = 0; j < i; j++) {
1728+
const char *rule = ref_rev_parse_rules[j];
1729+
unsigned char short_objectname[20];
1730+
char refname[PATH_MAX];
1731+
1732+
/*
1733+
* the short name is ambiguous, if it resolves
1734+
* (with this previous rule) to a valid ref
1735+
* read_ref() returns 0 on success
1736+
*/
1737+
mksnpath(refname, sizeof(refname),
1738+
rule, short_name_len, short_name);
1739+
if (!read_ref(refname, short_objectname))
1740+
break;
1741+
}
1742+
1743+
/*
1744+
* short name is non-ambiguous if all previous rules
1745+
* haven't resolved to a valid ref
1746+
*/
1747+
if (j == i)
1748+
return short_name;
1749+
}
1750+
1751+
free(short_name);
1752+
return xstrdup(ref);
1753+
}

refs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ extern int for_each_reflog(each_ref_fn, void *);
8080
extern int check_ref_format(const char *target);
8181

8282
extern const char *prettify_ref(const struct ref *ref);
83+
extern char *shorten_unambiguous_ref(const char *ref);
8384

8485
/** rename ref, return 0 on success **/
8586
extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);

0 commit comments

Comments
 (0)