Skip to content

Commit a08112d

Browse files
committed
cat-file --textconv/--filters: allow specifying the path separately
There are circumstances when it is relatively easy to figure out the object name for a given path, but not the name of the containing tree. For example, when looking at a diff generated by Git, the object names are recorded, but not the revision. As a matter of fact, the revisions from which the diff was generated may not even exist locally. In such a case, the user would have to generate a fake revision just to be able to use --textconv or --filters. Let's simplify this dramatically, because we do not really need that revision at all: all we care about is that we know the path. In the scenario described above, we do know the path, and we just want to specify it separately from the object name. Example usage: git cat-file --textconv --path=main.c 0f1937f Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 6e6c769 commit a08112d

File tree

3 files changed

+47
-6
lines changed

3 files changed

+47
-6
lines changed

Documentation/git-cat-file.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-cat-file - Provide content or type and size information for repository objec
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --filters ) <object>
12+
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --filters ) [--path=<path>] <object>
1313
'git cat-file' (--batch | --batch-check) [--follow-symlinks]
1414

1515
DESCRIPTION
@@ -64,6 +64,11 @@ OPTIONS
6464
end-of-line conversion, etc). In this case, <object> has to be of
6565
the form <tree-ish>:<path>, or :<path>.
6666

67+
--path=<path>::
68+
For use with --textconv or --filters, to allow specifying an object
69+
name and a path separately, e.g. when it is difficult to figure out
70+
the revision from which the blob came.
71+
6772
--batch::
6873
--batch=<format>::
6974
Print object information and contents for each object provided

builtin/cat-file.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ struct batch_options {
2020
const char *format;
2121
};
2222

23+
static const char *force_path;
24+
2325
static int filter_object(const char *path, unsigned mode,
2426
const unsigned char *sha1,
2527
char **buf, unsigned long *size)
@@ -58,13 +60,19 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
5860
struct object_info oi = {NULL};
5961
struct strbuf sb = STRBUF_INIT;
6062
unsigned flags = LOOKUP_REPLACE_OBJECT;
63+
const char *path = force_path;
6164

6265
if (unknown_type)
6366
flags |= LOOKUP_UNKNOWN_OBJECT;
6467

6568
if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
6669
die("Not a valid object name %s", obj_name);
6770

71+
if (!path)
72+
path = obj_context.path;
73+
if (obj_context.mode == S_IFINVALID)
74+
obj_context.mode = 0100644;
75+
6876
buf = NULL;
6977
switch (opt) {
7078
case 't':
@@ -89,21 +97,22 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
8997
return !has_sha1_file(sha1);
9098

9199
case 'w':
92-
if (!obj_context.path[0])
100+
if (!path[0])
93101
die("git cat-file --filters %s: <object> must be "
94102
"<sha1:path>", obj_name);
95103

96-
if (filter_object(obj_context.path, obj_context.mode,
104+
if (filter_object(path, obj_context.mode,
97105
sha1, &buf, &size))
98106
return -1;
99107
break;
100108

101109
case 'c':
102-
if (!obj_context.path[0])
110+
if (!path[0])
103111
die("git cat-file --textconv %s: <object> must be <sha1:path>",
104112
obj_name);
105113

106-
if (textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
114+
if (textconv_object(path, obj_context.mode,
115+
sha1, 1, &buf, &size))
107116
break;
108117

109118
case 'p':
@@ -477,7 +486,7 @@ static int batch_objects(struct batch_options *opt)
477486
}
478487

479488
static const char * const cat_file_usage[] = {
480-
N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) <object>"),
489+
N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"),
481490
N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
482491
NULL
483492
};
@@ -525,6 +534,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
525534
N_("for blob objects, run textconv on object's content"), 'c'),
526535
OPT_CMDMODE(0, "filters", &opt,
527536
N_("for blob objects, run filters on object's content"), 'w'),
537+
OPT_STRING(0, "path", &force_path, N_("blob"),
538+
N_("use a specific path for --textconv/--filters")),
528539
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
529540
N_("allow -s and -t to work with broken/corrupt objects")),
530541
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
@@ -567,6 +578,11 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
567578
usage_with_options(cat_file_usage, options);
568579
}
569580

581+
if (force_path && opt != 'c' && opt != 'w') {
582+
error("--path=<path> needs --textconv or --filters");
583+
usage_with_options(cat_file_usage, options);
584+
}
585+
570586
if (batch.buffer_output < 0)
571587
batch.buffer_output = batch.all_objects;
572588

t/t8010-cat-file-filters.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,24 @@ test_expect_success 'cat-file --filters converts to worktree version' '
3131
has_cr actual
3232
'
3333

34+
test_expect_success 'cat-file --filters --path=<path> works' '
35+
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
36+
git cat-file --filters --path=world.txt $sha1 >actual &&
37+
has_cr actual
38+
'
39+
40+
test_expect_success 'cat-file --textconv --path=<path> works' '
41+
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
42+
test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
43+
git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
44+
test uryyb = "$(cat rot13 | remove_cr)"
45+
'
46+
47+
test_expect_success '--path=<path> complains without --textconv/--filters' '
48+
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
49+
test_must_fail git cat-file --path=hello.txt blob $sha1 >actual 2>err &&
50+
test ! -s actual &&
51+
grep "path.*needs.*filters" err
52+
'
53+
3454
test_done

0 commit comments

Comments
 (0)