Skip to content

Commit 7bcf341

Browse files
dschogitster
authored andcommitted
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 <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent b9e62f6 commit 7bcf341

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)
@@ -53,13 +55,19 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
5355
struct object_info oi = {NULL};
5456
struct strbuf sb = STRBUF_INIT;
5557
unsigned flags = LOOKUP_REPLACE_OBJECT;
58+
const char *path = force_path;
5659

5760
if (unknown_type)
5861
flags |= LOOKUP_UNKNOWN_OBJECT;
5962

6063
if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
6164
die("Not a valid object name %s", obj_name);
6265

66+
if (!path)
67+
path = obj_context.path;
68+
if (obj_context.mode == S_IFINVALID)
69+
obj_context.mode = 0100644;
70+
6371
buf = NULL;
6472
switch (opt) {
6573
case 't':
@@ -84,21 +92,22 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
8492
return !has_sha1_file(sha1);
8593

8694
case 'w':
87-
if (!obj_context.path[0])
95+
if (!path[0])
8896
die("git cat-file --filters %s: <object> must be "
8997
"<sha1:path>", obj_name);
9098

91-
if (filter_object(obj_context.path, obj_context.mode,
99+
if (filter_object(path, obj_context.mode,
92100
sha1, &buf, &size))
93101
return -1;
94102
break;
95103

96104
case 'c':
97-
if (!obj_context.path[0])
105+
if (!path[0])
98106
die("git cat-file --textconv %s: <object> must be <sha1:path>",
99107
obj_name);
100108

101-
if (textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
109+
if (textconv_object(path, obj_context.mode,
110+
sha1, 1, &buf, &size))
102111
break;
103112

104113
case 'p':
@@ -472,7 +481,7 @@ static int batch_objects(struct batch_options *opt)
472481
}
473482

474483
static const char * const cat_file_usage[] = {
475-
N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv|--filters) <object>"),
484+
N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv|--filters) [--path=<path>] <object>"),
476485
N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
477486
NULL
478487
};
@@ -520,6 +529,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
520529
N_("for blob objects, run textconv on object's content"), 'c'),
521530
OPT_CMDMODE(0, "filters", &opt,
522531
N_("for blob objects, run filters on object's content"), 'w'),
532+
OPT_STRING(0, "path", &force_path, N_("blob"),
533+
N_("use a specific path for --textconv/--filters")),
523534
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
524535
N_("allow -s and -t to work with broken/corrupt objects")),
525536
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
@@ -562,6 +573,11 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
562573
usage_with_options(cat_file_usage, options);
563574
}
564575

576+
if (force_path && opt != 'c' && opt != 'w') {
577+
error("--path=<path> needs --textconv or --filters");
578+
usage_with_options(cat_file_usage, options);
579+
}
580+
565581
if (batch.buffer_output < 0)
566582
batch.buffer_output = batch.all_objects;
567583

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)