Skip to content

Commit 556de1a

Browse files
committed
Merge branch 'sb/describe-blob'
"git describe" was taught to dig trees deeper to find a <commit-ish>:<path> that refers to a given blob object. * sb/describe-blob: builtin/describe.c: describe a blob builtin/describe.c: factor out describe_commit builtin/describe.c: print debug statements earlier builtin/describe.c: rename `oid` to avoid variable shadowing revision.h: introduce blob/tree walking in order of the commits list-objects.c: factor out traverse_trees_and_blobs t6120: fix typo in test name
2 parents 0433d53 + 644eb60 commit 556de1a

File tree

8 files changed

+277
-52
lines changed

8 files changed

+277
-52
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

Documentation/rev-list-options.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,11 @@ ifdef::git-rev-list[]
686686
all object IDs which I need to download if I have the commit
687687
object _bar_ but not _foo_''.
688688

689+
--in-commit-order::
690+
Print tree and blob ids in order of the commits. The tree
691+
and blob ids are printed after they are first referenced
692+
by a commit.
693+
689694
--objects-edge::
690695
Similar to `--objects`, but also print the IDs of excluded
691696
commits prefixed with a ``-'' character. This is used by

builtin/describe.c

Lines changed: 94 additions & 27 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"
@@ -12,6 +13,8 @@
1213
#include "hashmap.h"
1314
#include "argv-array.h"
1415
#include "run-command.h"
16+
#include "revision.h"
17+
#include "list-objects.h"
1518

1619
#define MAX_TAGS (FLAG_BITS - 1)
1720

@@ -256,7 +259,7 @@ static unsigned long finish_depth_computation(
256259
return seen_commits;
257260
}
258261

259-
static void display_name(struct commit_name *n)
262+
static void append_name(struct commit_name *n, struct strbuf *dst)
260263
{
261264
if (n->prio == 2 && !n->tag) {
262265
n->tag = lookup_tag(&n->oid);
@@ -272,19 +275,18 @@ static void display_name(struct commit_name *n)
272275
}
273276

274277
if (n->tag)
275-
printf("%s", n->tag->tag);
278+
strbuf_addstr(dst, n->tag->tag);
276279
else
277-
printf("%s", n->path);
280+
strbuf_addstr(dst, n->path);
278281
}
279282

280-
static void show_suffix(int depth, const struct object_id *oid)
283+
static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
281284
{
282-
printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
285+
strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
283286
}
284287

285-
static void describe(const char *arg, int last_one)
288+
static void describe_commit(struct object_id *oid, struct strbuf *dst)
286289
{
287-
struct object_id oid;
288290
struct commit *cmit, *gave_up_on = NULL;
289291
struct commit_list *list;
290292
struct commit_name *n;
@@ -293,30 +295,25 @@ static void describe(const char *arg, int last_one)
293295
unsigned long seen_commits = 0;
294296
unsigned int unannotated_cnt = 0;
295297

296-
if (get_oid(arg, &oid))
297-
die(_("Not a valid object name %s"), arg);
298-
cmit = lookup_commit_reference(&oid);
299-
if (!cmit)
300-
die(_("%s is not a valid '%s' object"), arg, commit_type);
298+
cmit = lookup_commit_reference(oid);
301299

302300
n = find_commit_name(&cmit->object.oid);
303301
if (n && (tags || all || n->prio == 2)) {
304302
/*
305303
* Exact match to an existing ref.
306304
*/
307-
display_name(n);
305+
append_name(n, dst);
308306
if (longformat)
309-
show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
307+
append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
310308
if (suffix)
311-
printf("%s", suffix);
312-
printf("\n");
309+
strbuf_addstr(dst, suffix);
313310
return;
314311
}
315312

316313
if (!max_candidates)
317314
die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
318315
if (debug)
319-
fprintf(stderr, _("searching to describe %s\n"), arg);
316+
fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
320317

321318
if (!have_util) {
322319
struct hashmap_iter iter;
@@ -381,22 +378,21 @@ static void describe(const char *arg, int last_one)
381378
}
382379

383380
if (!match_cnt) {
384-
struct object_id *oid = &cmit->object.oid;
381+
struct object_id *cmit_oid = &cmit->object.oid;
385382
if (always) {
386-
printf("%s", find_unique_abbrev(oid->hash, abbrev));
383+
strbuf_addstr(dst, find_unique_abbrev(cmit_oid->hash, abbrev));
387384
if (suffix)
388-
printf("%s", suffix);
389-
printf("\n");
385+
strbuf_addstr(dst, suffix);
390386
return;
391387
}
392388
if (unannotated_cnt)
393389
die(_("No annotated tags can describe '%s'.\n"
394390
"However, there were unannotated tags: try --tags."),
395-
oid_to_hex(oid));
391+
oid_to_hex(cmit_oid));
396392
else
397393
die(_("No tags can describe '%s'.\n"
398394
"Try --always, or create some tags."),
399-
oid_to_hex(oid));
395+
oid_to_hex(cmit_oid));
400396
}
401397

402398
QSORT(all_matches, match_cnt, compare_pt);
@@ -434,15 +430,86 @@ static void describe(const char *arg, int last_one)
434430
}
435431
}
436432

437-
display_name(all_matches[0].name);
433+
append_name(all_matches[0].name, dst);
438434
if (abbrev)
439-
show_suffix(all_matches[0].depth, &cmit->object.oid);
435+
append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
440436
if (suffix)
441-
printf("%s", suffix);
442-
printf("\n");
437+
strbuf_addstr(dst, suffix);
438+
}
439+
440+
struct process_commit_data {
441+
struct object_id current_commit;
442+
struct object_id looking_for;
443+
struct strbuf *dst;
444+
struct rev_info *revs;
445+
};
446+
447+
static void process_commit(struct commit *commit, void *data)
448+
{
449+
struct process_commit_data *pcd = data;
450+
pcd->current_commit = commit->object.oid;
451+
}
452+
453+
static void process_object(struct object *obj, const char *path, void *data)
454+
{
455+
struct process_commit_data *pcd = data;
456+
457+
if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
458+
reset_revision_walk();
459+
describe_commit(&pcd->current_commit, pcd->dst);
460+
strbuf_addf(pcd->dst, ":%s", path);
461+
free_commit_list(pcd->revs->commits);
462+
pcd->revs->commits = NULL;
463+
}
464+
}
465+
466+
static void describe_blob(struct object_id oid, struct strbuf *dst)
467+
{
468+
struct rev_info revs;
469+
struct argv_array args = ARGV_ARRAY_INIT;
470+
struct process_commit_data pcd = { null_oid, oid, dst, &revs};
471+
472+
argv_array_pushl(&args, "internal: The first arg is not parsed",
473+
"--objects", "--in-commit-order", "--reverse", "HEAD",
474+
NULL);
475+
476+
init_revisions(&revs, NULL);
477+
if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
478+
BUG("setup_revisions could not handle all args?");
479+
480+
if (prepare_revision_walk(&revs))
481+
die("revision walk setup failed");
482+
483+
traverse_commit_list(&revs, process_commit, process_object, &pcd);
484+
reset_revision_walk();
485+
}
486+
487+
static void describe(const char *arg, int last_one)
488+
{
489+
struct object_id oid;
490+
struct commit *cmit;
491+
struct strbuf sb = STRBUF_INIT;
492+
493+
if (debug)
494+
fprintf(stderr, _("describe %s\n"), arg);
495+
496+
if (get_oid(arg, &oid))
497+
die(_("Not a valid object name %s"), arg);
498+
cmit = lookup_commit_reference_gently(&oid, 1);
499+
500+
if (cmit)
501+
describe_commit(&oid, &sb);
502+
else if (lookup_blob(&oid))
503+
describe_blob(oid, &sb);
504+
else
505+
die(_("%s is neither a commit nor blob"), arg);
506+
507+
puts(sb.buf);
443508

444509
if (!last_one)
445510
clear_commit_marks(cmit, -1);
511+
512+
strbuf_release(&sb);
446513
}
447514

448515
int cmd_describe(int argc, const char **argv, const char *prefix)

list-objects.c

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -214,27 +214,17 @@ static void add_pending_tree(struct rev_info *revs, struct tree *tree)
214214
add_pending_object(revs, &tree->object, "");
215215
}
216216

217-
static void do_traverse(struct rev_info *revs,
218-
show_commit_fn show_commit,
219-
show_object_fn show_object,
220-
void *show_data,
221-
filter_object_fn filter_fn,
222-
void *filter_data)
217+
static void traverse_trees_and_blobs(struct rev_info *revs,
218+
struct strbuf *base,
219+
show_object_fn show_object,
220+
void *show_data,
221+
filter_object_fn filter_fn,
222+
void *filter_data)
223223
{
224224
int i;
225-
struct commit *commit;
226-
struct strbuf base;
227225

228-
strbuf_init(&base, PATH_MAX);
229-
while ((commit = get_revision(revs)) != NULL) {
230-
/*
231-
* an uninteresting boundary commit may not have its tree
232-
* parsed yet, but we are not going to show them anyway
233-
*/
234-
if (commit->tree)
235-
add_pending_tree(revs, commit->tree);
236-
show_commit(commit, show_data);
237-
}
226+
assert(base->len == 0);
227+
238228
for (i = 0; i < revs->pending.nr; i++) {
239229
struct object_array_entry *pending = revs->pending.objects + i;
240230
struct object *obj = pending->item;
@@ -251,21 +241,56 @@ static void do_traverse(struct rev_info *revs,
251241
path = "";
252242
if (obj->type == OBJ_TREE) {
253243
process_tree(revs, (struct tree *)obj, show_object,
254-
&base, path, show_data,
244+
base, path, show_data,
255245
filter_fn, filter_data);
256246
continue;
257247
}
258248
if (obj->type == OBJ_BLOB) {
259249
process_blob(revs, (struct blob *)obj, show_object,
260-
&base, path, show_data,
250+
base, path, show_data,
261251
filter_fn, filter_data);
262252
continue;
263253
}
264254
die("unknown pending object %s (%s)",
265255
oid_to_hex(&obj->oid), name);
266256
}
267257
object_array_clear(&revs->pending);
268-
strbuf_release(&base);
258+
}
259+
260+
static void do_traverse(struct rev_info *revs,
261+
show_commit_fn show_commit,
262+
show_object_fn show_object,
263+
void *show_data,
264+
filter_object_fn filter_fn,
265+
void *filter_data)
266+
{
267+
struct commit *commit;
268+
struct strbuf csp; /* callee's scratch pad */
269+
strbuf_init(&csp, PATH_MAX);
270+
271+
while ((commit = get_revision(revs)) != NULL) {
272+
/*
273+
* an uninteresting boundary commit may not have its tree
274+
* parsed yet, but we are not going to show them anyway
275+
*/
276+
if (commit->tree)
277+
add_pending_tree(revs, commit->tree);
278+
show_commit(commit, show_data);
279+
280+
if (revs->tree_blobs_in_commit_order)
281+
/*
282+
* NEEDSWORK: Adding the tree and then flushing it here
283+
* needs a reallocation for each commit. Can we pass the
284+
* tree directory without allocation churn?
285+
*/
286+
traverse_trees_and_blobs(revs, &csp,
287+
show_object, show_data,
288+
filter_fn, filter_data);
289+
}
290+
traverse_trees_and_blobs(revs, &csp,
291+
show_object, show_data,
292+
filter_fn, filter_data);
293+
strbuf_release(&csp);
269294
}
270295

271296
void traverse_commit_list(struct rev_info *revs,

revision.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,6 +1855,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
18551855
revs->dense = 0;
18561856
} else if (!strcmp(arg, "--show-all")) {
18571857
revs->show_all = 1;
1858+
} else if (!strcmp(arg, "--in-commit-order")) {
1859+
revs->tree_blobs_in_commit_order = 1;
18581860
} else if (!strcmp(arg, "--remove-empty")) {
18591861
revs->remove_empty_trees = 1;
18601862
} else if (!strcmp(arg, "--merges")) {

revision.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ struct rev_info {
121121
bisect:1,
122122
ancestry_path:1,
123123
first_parent_only:1,
124-
line_level_traverse:1;
124+
line_level_traverse:1,
125+
tree_blobs_in_commit_order:1;
125126

126127
/* Diff flags */
127128
unsigned int diff:1,

0 commit comments

Comments
 (0)