Skip to content

Commit 122d534

Browse files
dturner-twgitster
authored andcommitted
cat-file: add --follow-symlinks to --batch
This wires the in-repo-symlink following code through to the cat-file builtin. In the event of an out-of-repo link, cat-file will print the link in a new format. Signed-off-by: David Turner <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c4ec967 commit 122d534

File tree

3 files changed

+348
-7
lines changed

3 files changed

+348
-7
lines changed

Documentation/git-cat-file.txt

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git cat-file' (-t | -s | -e | -p | <type> | --textconv ) <object>
13-
'git cat-file' (--batch | --batch-check) < <list-of-objects>
13+
'git cat-file' (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>
1414

1515
DESCRIPTION
1616
-----------
@@ -69,6 +69,62 @@ OPTIONS
6969
not be combined with any other options or arguments. See the
7070
section `BATCH OUTPUT` below for details.
7171

72+
--follow-symlinks::
73+
With --batch or --batch-check, follow symlinks inside the
74+
repository when requesting objects with extended SHA-1
75+
expressions of the form tree-ish:path-in-tree. Instead of
76+
providing output about the link itself, provide output about
77+
the linked-to object. If a symlink points outside the
78+
tree-ish (e.g. a link to /foo or a root-level link to ../foo),
79+
the portion of the link which is outside the tree will be
80+
printed.
81+
+
82+
This option does not (currently) work correctly when an object in the
83+
index is specified (e.g. `:link` instead of `HEAD:link`) rather than
84+
one in the tree.
85+
+
86+
This option cannot (currently) be used unless `--batch` or
87+
`--batch-check` is used.
88+
+
89+
For example, consider a git repository containing:
90+
+
91+
--
92+
f: a file containing "hello\n"
93+
link: a symlink to f
94+
dir/link: a symlink to ../f
95+
plink: a symlink to ../f
96+
alink: a symlink to /etc/passwd
97+
--
98+
+
99+
For a regular file `f`, `echo HEAD:f | git cat-file --batch` would print
100+
+
101+
--
102+
ce013625030ba8dba906f756967f9e9ca394464a blob 6
103+
--
104+
+
105+
And `echo HEAD:link | git cat-file --batch --follow-symlinks` would
106+
print the same thing, as would `HEAD:dir/link`, as they both point at
107+
`HEAD:f`.
108+
+
109+
Without `--follow-symlinks`, these would print data about the symlink
110+
itself. In the case of `HEAD:link`, you would see
111+
+
112+
--
113+
4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
114+
--
115+
+
116+
Both `plink` and `alink` point outside the tree, so they would
117+
respectively print:
118+
+
119+
--
120+
symlink 4
121+
../f
122+
123+
symlink 11
124+
/etc/passwd
125+
--
126+
127+
72128
OUTPUT
73129
------
74130
If '-t' is specified, one of the <type>.
@@ -148,6 +204,47 @@ the repository, then `cat-file` will ignore any custom format and print:
148204
<object> SP missing LF
149205
------------
150206

207+
If --follow-symlinks is used, and a symlink in the repository points
208+
outside the repository, then `cat-file` will ignore any custom format
209+
and print:
210+
211+
------------
212+
symlink SP <size> LF
213+
<symlink> LF
214+
------------
215+
216+
The symlink will either be absolute (beginning with a /), or relative
217+
to the tree root. For instance, if dir/link points to ../../foo, then
218+
<symlink> will be ../foo. <size> is the size of the symlink in bytes.
219+
220+
If --follow-symlinks is used, the following error messages will be
221+
displayed:
222+
223+
------------
224+
<object> SP missing LF
225+
------------
226+
is printed when the initial symlink requested does not exist.
227+
228+
------------
229+
dangling SP <size> LF
230+
<object> LF
231+
------------
232+
is printed when the initial symlink exists, but something that
233+
it (transitive-of) points to does not.
234+
235+
------------
236+
loop SP <size> LF
237+
<object> LF
238+
------------
239+
is printed for symlink loops (or any symlinks that
240+
require more than 40 link resolutions to resolve).
241+
242+
------------
243+
notdir SP <size> LF
244+
<object> LF
245+
------------
246+
is printed when, during symlink resolution, a file is used as a
247+
directory name.
151248

152249
CAVEATS
153250
-------

builtin/cat-file.c

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "parse-options.h"
99
#include "userdiff.h"
1010
#include "streaming.h"
11+
#include "tree-walk.h"
1112

1213
static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
1314
{
@@ -224,6 +225,7 @@ static void print_object_or_die(int fd, struct expand_data *data)
224225

225226
struct batch_options {
226227
int enabled;
228+
int follow_symlinks;
227229
int print_contents;
228230
const char *format;
229231
};
@@ -232,12 +234,44 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
232234
struct expand_data *data)
233235
{
234236
struct strbuf buf = STRBUF_INIT;
237+
struct object_context ctx;
238+
int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0;
239+
enum follow_symlinks_result result;
235240

236241
if (!obj_name)
237242
return 1;
238243

239-
if (get_sha1(obj_name, data->sha1)) {
240-
printf("%s missing\n", obj_name);
244+
result = get_sha1_with_context(obj_name, flags, data->sha1, &ctx);
245+
if (result != FOUND) {
246+
switch (result) {
247+
case MISSING_OBJECT:
248+
printf("%s missing\n", obj_name);
249+
break;
250+
case DANGLING_SYMLINK:
251+
printf("dangling %"PRIuMAX"\n%s\n",
252+
(uintmax_t)strlen(obj_name), obj_name);
253+
break;
254+
case SYMLINK_LOOP:
255+
printf("loop %"PRIuMAX"\n%s\n",
256+
(uintmax_t)strlen(obj_name), obj_name);
257+
break;
258+
case NOT_DIR:
259+
printf("notdir %"PRIuMAX"\n%s\n",
260+
(uintmax_t)strlen(obj_name), obj_name);
261+
break;
262+
default:
263+
die("BUG: unknown get_sha1_with_context result %d\n",
264+
result);
265+
break;
266+
}
267+
fflush(stdout);
268+
return 0;
269+
}
270+
271+
if (ctx.mode == 0) {
272+
printf("symlink %"PRIuMAX"\n%s\n",
273+
(uintmax_t)ctx.symlink_path.len,
274+
ctx.symlink_path.buf);
241275
fflush(stdout);
242276
return 0;
243277
}
@@ -324,7 +358,7 @@ static int batch_objects(struct batch_options *opt)
324358

325359
static const char * const cat_file_usage[] = {
326360
N_("git cat-file (-t | -s | -e | -p | <type> | --textconv) <object>"),
327-
N_("git cat-file (--batch | --batch-check) < <list-of-objects>"),
361+
N_("git cat-file (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>"),
328362
NULL
329363
};
330364

@@ -342,9 +376,8 @@ static int batch_option_callback(const struct option *opt,
342376
{
343377
struct batch_options *bo = opt->value;
344378

345-
if (unset) {
346-
memset(bo, 0, sizeof(*bo));
347-
return 0;
379+
if (bo->enabled) {
380+
return 1;
348381
}
349382

350383
bo->enabled = 1;
@@ -375,6 +408,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
375408
{ OPTION_CALLBACK, 0, "batch-check", &batch, "format",
376409
N_("show info about objects fed from the standard input"),
377410
PARSE_OPT_OPTARG, batch_option_callback },
411+
OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
412+
N_("follow in-tree symlinks (used with --batch or --batch-check)")),
378413
OPT_END()
379414
};
380415

@@ -402,6 +437,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
402437
usage_with_options(cat_file_usage, options);
403438
}
404439

440+
if (batch.follow_symlinks && !batch.enabled) {
441+
usage_with_options(cat_file_usage, options);
442+
}
443+
405444
if (batch.enabled)
406445
return batch_objects(&batch);
407446

0 commit comments

Comments
 (0)