Skip to content

Commit 644eb60

Browse files
stefanbellergitster
authored andcommitted
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to identify it further (ex.: Use verify-pack to find the largest blobs, but what are these? or [1]) When describing commits, we try to anchor them to tags or refs, as these are conceptually on a higher level than the commit. And if there is no ref or tag that matches exactly, we're out of luck. So we employ a heuristic to make up a name for the commit. These names are ambiguous, there might be different tags or refs to anchor to, and there might be different path in the DAG to travel to arrive at the commit precisely. When describing a blob, we want to describe the blob from a higher layer as well, which is a tuple of (commit, deep/path) as the tree objects involved are rather uninteresting. The same blob can be referenced by multiple commits, so how we decide which commit to use? This patch implements a rather naive approach on this: As there are no back pointers from blobs to commits in which the blob occurs, we'll start walking from any tips available, listing the blobs in-order of the commit and once we found the blob, we'll take the first commit that listed the blob. For example git describe --tags v0.99:Makefile conversion-901-g7672db20c2:Makefile tells us the Makefile as it was in v0.99 was introduced in commit 7672db2. The walking is performed in reverse order to show the introduction of a blob rather than its last occurrence. [1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob Signed-off-by: Stefan Beller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4dbc59a commit 644eb60

File tree

3 files changed

+107
-7
lines changed

3 files changed

+107
-7
lines changed

Documentation/git-describe.txt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ git-describe(1)
33

44
NAME
55
----
6-
git-describe - Describe a commit using the most recent tag reachable from it
7-
6+
git-describe - Give an object a human readable name based on an available ref
87

98
SYNOPSIS
109
--------
1110
[verse]
1211
'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]
1312
'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
13+
'git describe' <blob>
1414

1515
DESCRIPTION
1616
-----------
@@ -24,6 +24,12 @@ By default (without --all or --tags) `git describe` only shows
2424
annotated tags. For more information about creating annotated tags
2525
see the -a and -s options to linkgit:git-tag[1].
2626

27+
If the given object refers to a blob, it will be described
28+
as `<commit-ish>:<path>`, such that the blob can be found
29+
at `<path>` in the `<commit-ish>`, which itself describes the
30+
first commit in which this blob occurs in a reverse revision walk
31+
from HEAD.
32+
2733
OPTIONS
2834
-------
2935
<commit-ish>...::
@@ -186,6 +192,14 @@ selected and output. Here fewest commits different is defined as
186192
the number of commits which would be shown by `git log tag..input`
187193
will be the smallest number of commits possible.
188194

195+
BUGS
196+
----
197+
198+
Tree objects as well as tag objects not pointing at commits, cannot be described.
199+
When describing blobs, the lightweight tags pointing at blobs are ignored,
200+
but the blob is still described as <committ-ish>:<path> despite the lightweight
201+
tag being favorable.
202+
189203
GIT
190204
---
191205
Part of the linkgit:git[1] suite

builtin/describe.c

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "lockfile.h"
44
#include "commit.h"
55
#include "tag.h"
6+
#include "blob.h"
67
#include "refs.h"
78
#include "builtin.h"
89
#include "exec_cmd.h"
@@ -11,8 +12,9 @@
1112
#include "hashmap.h"
1213
#include "argv-array.h"
1314
#include "run-command.h"
15+
#include "revision.h"
16+
#include "list-objects.h"
1417

15-
#define SEEN (1u << 0)
1618
#define MAX_TAGS (FLAG_BITS - 1)
1719

1820
static const char * const describe_usage[] = {
@@ -434,6 +436,53 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
434436
strbuf_addstr(dst, suffix);
435437
}
436438

439+
struct process_commit_data {
440+
struct object_id current_commit;
441+
struct object_id looking_for;
442+
struct strbuf *dst;
443+
struct rev_info *revs;
444+
};
445+
446+
static void process_commit(struct commit *commit, void *data)
447+
{
448+
struct process_commit_data *pcd = data;
449+
pcd->current_commit = commit->object.oid;
450+
}
451+
452+
static void process_object(struct object *obj, const char *path, void *data)
453+
{
454+
struct process_commit_data *pcd = data;
455+
456+
if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
457+
reset_revision_walk();
458+
describe_commit(&pcd->current_commit, pcd->dst);
459+
strbuf_addf(pcd->dst, ":%s", path);
460+
free_commit_list(pcd->revs->commits);
461+
pcd->revs->commits = NULL;
462+
}
463+
}
464+
465+
static void describe_blob(struct object_id oid, struct strbuf *dst)
466+
{
467+
struct rev_info revs;
468+
struct argv_array args = ARGV_ARRAY_INIT;
469+
struct process_commit_data pcd = { null_oid, oid, dst, &revs};
470+
471+
argv_array_pushl(&args, "internal: The first arg is not parsed",
472+
"--objects", "--in-commit-order", "--reverse", "HEAD",
473+
NULL);
474+
475+
init_revisions(&revs, NULL);
476+
if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
477+
BUG("setup_revisions could not handle all args?");
478+
479+
if (prepare_revision_walk(&revs))
480+
die("revision walk setup failed");
481+
482+
traverse_commit_list(&revs, process_commit, process_object, &pcd);
483+
reset_revision_walk();
484+
}
485+
437486
static void describe(const char *arg, int last_one)
438487
{
439488
struct object_id oid;
@@ -445,11 +494,14 @@ static void describe(const char *arg, int last_one)
445494

446495
if (get_oid(arg, &oid))
447496
die(_("Not a valid object name %s"), arg);
448-
cmit = lookup_commit_reference(&oid);
449-
if (!cmit)
450-
die(_("%s is not a valid '%s' object"), arg, commit_type);
497+
cmit = lookup_commit_reference_gently(&oid, 1);
451498

452-
describe_commit(&oid, &sb);
499+
if (cmit)
500+
describe_commit(&oid, &sb);
501+
else if (lookup_blob(&oid))
502+
describe_blob(oid, &sb);
503+
else
504+
die(_("%s is neither a commit nor blob"), arg);
453505

454506
puts(sb.buf);
455507

t/t6120-describe.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,40 @@ test_expect_success 'describe ignoring a broken submodule' '
310310
grep broken out
311311
'
312312

313+
test_expect_success 'describe a blob at a directly tagged commit' '
314+
echo "make it a unique blob" >file &&
315+
git add file && git commit -m "content in file" &&
316+
git tag -a -m "latest annotated tag" unique-file &&
317+
git describe HEAD:file >actual &&
318+
echo "unique-file:file" >expect &&
319+
test_cmp expect actual
320+
'
321+
322+
test_expect_success 'describe a blob with its first introduction' '
323+
git commit --allow-empty -m "empty commit" &&
324+
git rm file &&
325+
git commit -m "delete blob" &&
326+
git revert HEAD &&
327+
git commit --allow-empty -m "empty commit" &&
328+
git describe HEAD:file >actual &&
329+
echo "unique-file:file" >expect &&
330+
test_cmp expect actual
331+
'
332+
333+
test_expect_success 'describe directly tagged blob' '
334+
git tag test-blob unique-file:file &&
335+
git describe test-blob >actual &&
336+
echo "unique-file:file" >expect &&
337+
# suboptimal: we rather want to see "test-blob"
338+
test_cmp expect actual
339+
'
340+
341+
test_expect_success 'describe tag object' '
342+
git tag test-blob-1 -a -m msg unique-file:file &&
343+
test_must_fail git describe test-blob-1 2>actual &&
344+
test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
345+
'
346+
313347
test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
314348
i=1 &&
315349
while test $i -lt 8000

0 commit comments

Comments
 (0)