Skip to content

Commit 9264b03

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]) "This is an interesting endeavor, because describing things is hard." -- me, upon writing this patch. 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 source code this is likely not the first commit that introduced the blob, but rather the latest commit that contained the blob. For example: git describe v0.99:Makefile v0.99-5-gab6625e06a:Makefile tells us the latest commit that contained the Makefile as it was in tag v0.99 is commit v0.99-5-gab6625e06a (and at the same path), as the next commit on top v0.99-6-gb1de9de2b9 ([PATCH] Bootstrap "make dist", 2005-07-11) touches the Makefile. Let's see how this description turns out, if it is useful in day-to-day use as I have the intuition that we'd rather want to see the *first* commit that this blob was introduced to the repository (which can be achieved easily by giving the `--reverse` flag in the describe_blob rev walk). [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 7ccf787 commit 9264b03

File tree

3 files changed

+87
-6
lines changed

3 files changed

+87
-6
lines changed

Documentation/git-describe.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ git-describe(1)
33

44
NAME
55
----
6-
git-describe - Describe a commit using the most recent tag reachable from it
6+
git-describe - Describe a commit or blob using the graph relations
77

88

99
SYNOPSIS
1010
--------
1111
[verse]
1212
'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]
1313
'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
14+
'git describe' [<options>] <blob>
1415

1516
DESCRIPTION
1617
-----------
@@ -24,6 +25,16 @@ By default (without --all or --tags) `git describe` only shows
2425
annotated tags. For more information about creating annotated tags
2526
see the -a and -s options to linkgit:git-tag[1].
2627

28+
If the given object refers to a blob, it will be described
29+
as `<commit-ish>:<path>`, such that the blob can be found
30+
at `<path>` in the `<commit-ish>`. Note, that the commit is likely
31+
not the commit that introduced the blob, but the one that was found
32+
first; to find the commit that introduced the blob, you need to find
33+
the commit that last touched the path, e.g.
34+
`git log <commit-description> -- <path>`.
35+
As blobs do not point at the commits they are contained in,
36+
describing blobs is slow as we have to walk the whole graph.
37+
2738
OPTIONS
2839
-------
2940
<commit-ish>...::

builtin/describe.c

Lines changed: 60 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,56 @@ 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+
pcd->revs->max_count = 0;
461+
}
462+
}
463+
464+
static void describe_blob(struct object_id oid, struct strbuf *dst)
465+
{
466+
struct rev_info revs;
467+
struct argv_array args = ARGV_ARRAY_INIT;
468+
struct process_commit_data pcd = { null_oid, oid, dst, &revs};
469+
470+
argv_array_pushl(&args, "internal: The first arg is not parsed",
471+
"--all", "--reflog", /* as many starting points as possible */
472+
/* NEEDSWORK: --all is incompatible with worktrees for now: */
473+
"--single-worktree",
474+
"--objects",
475+
"--in-commit-order",
476+
NULL);
477+
478+
init_revisions(&revs, NULL);
479+
if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
480+
BUG("setup_revisions could not handle all args?");
481+
482+
if (prepare_revision_walk(&revs))
483+
die("revision walk setup failed");
484+
485+
traverse_commit_list(&revs, process_commit, process_object, &pcd);
486+
reset_revision_walk();
487+
}
488+
437489
static void describe(const char *arg, int last_one)
438490
{
439491
struct object_id oid;
@@ -445,11 +497,14 @@ static void describe(const char *arg, int last_one)
445497

446498
if (get_oid(arg, &oid))
447499
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);
500+
cmit = lookup_commit_reference_gently(&oid, 1);
451501

452-
describe_commit(&oid, &sb);
502+
if (cmit)
503+
describe_commit(&oid, &sb);
504+
else if (lookup_blob(&oid))
505+
describe_blob(oid, &sb);
506+
else
507+
die(_("%s is neither a commit nor blob"), arg);
453508

454509
puts(sb.buf);
455510

t/t6120-describe.sh

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

313+
test_expect_success 'describe a blob at a tag' '
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 commit-ish' '
323+
git commit --allow-empty -m "empty commit" &&
324+
git describe HEAD:file >actual &&
325+
grep unique-file-1-g actual
326+
'
327+
313328
test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
314329
i=1 &&
315330
while test $i -lt 8000

0 commit comments

Comments
 (0)