Skip to content

Commit

Permalink
cat-file: add --follow-symlinks to --batch
Browse files Browse the repository at this point in the history
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 <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
David Turner authored and Junio C Hamano committed May 20, 2015
1 parent c4ec967 commit 122d534
Show file tree
Hide file tree
Showing 3 changed files with 348 additions and 7 deletions.
99 changes: 98 additions & 1 deletion Documentation/git-cat-file.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git cat-file' (-t | -s | -e | -p | <type> | --textconv ) <object>
'git cat-file' (--batch | --batch-check) < <list-of-objects>
'git cat-file' (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>

DESCRIPTION
-----------
Expand Down Expand Up @@ -69,6 +69,62 @@ OPTIONS
not be combined with any other options or arguments. See the
section `BATCH OUTPUT` below for details.

--follow-symlinks::
With --batch or --batch-check, follow symlinks inside the
repository when requesting objects with extended SHA-1
expressions of the form tree-ish:path-in-tree. Instead of
providing output about the link itself, provide output about
the linked-to object. If a symlink points outside the
tree-ish (e.g. a link to /foo or a root-level link to ../foo),
the portion of the link which is outside the tree will be
printed.
+
This option does not (currently) work correctly when an object in the
index is specified (e.g. `:link` instead of `HEAD:link`) rather than
one in the tree.
+
This option cannot (currently) be used unless `--batch` or
`--batch-check` is used.
+
For example, consider a git repository containing:
+
--
f: a file containing "hello\n"
link: a symlink to f
dir/link: a symlink to ../f
plink: a symlink to ../f
alink: a symlink to /etc/passwd
--
+
For a regular file `f`, `echo HEAD:f | git cat-file --batch` would print
+
--
ce013625030ba8dba906f756967f9e9ca394464a blob 6
--
+
And `echo HEAD:link | git cat-file --batch --follow-symlinks` would
print the same thing, as would `HEAD:dir/link`, as they both point at
`HEAD:f`.
+
Without `--follow-symlinks`, these would print data about the symlink
itself. In the case of `HEAD:link`, you would see
+
--
4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
--
+
Both `plink` and `alink` point outside the tree, so they would
respectively print:
+
--
symlink 4
../f

symlink 11
/etc/passwd
--


OUTPUT
------
If '-t' is specified, one of the <type>.
Expand Down Expand Up @@ -148,6 +204,47 @@ the repository, then `cat-file` will ignore any custom format and print:
<object> SP missing LF
------------

If --follow-symlinks is used, and a symlink in the repository points
outside the repository, then `cat-file` will ignore any custom format
and print:

------------
symlink SP <size> LF
<symlink> LF
------------

The symlink will either be absolute (beginning with a /), or relative
to the tree root. For instance, if dir/link points to ../../foo, then
<symlink> will be ../foo. <size> is the size of the symlink in bytes.

If --follow-symlinks is used, the following error messages will be
displayed:

------------
<object> SP missing LF
------------
is printed when the initial symlink requested does not exist.

------------
dangling SP <size> LF
<object> LF
------------
is printed when the initial symlink exists, but something that
it (transitive-of) points to does not.

------------
loop SP <size> LF
<object> LF
------------
is printed for symlink loops (or any symlinks that
require more than 40 link resolutions to resolve).

------------
notdir SP <size> LF
<object> LF
------------
is printed when, during symlink resolution, a file is used as a
directory name.

CAVEATS
-------
Expand Down
51 changes: 45 additions & 6 deletions builtin/cat-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
#include "tree-walk.h"

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

struct batch_options {
int enabled;
int follow_symlinks;
int print_contents;
const char *format;
};
Expand All @@ -232,12 +234,44 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
struct expand_data *data)
{
struct strbuf buf = STRBUF_INIT;
struct object_context ctx;
int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0;
enum follow_symlinks_result result;

if (!obj_name)
return 1;

if (get_sha1(obj_name, data->sha1)) {
printf("%s missing\n", obj_name);
result = get_sha1_with_context(obj_name, flags, data->sha1, &ctx);
if (result != FOUND) {
switch (result) {
case MISSING_OBJECT:
printf("%s missing\n", obj_name);
break;
case DANGLING_SYMLINK:
printf("dangling %"PRIuMAX"\n%s\n",
(uintmax_t)strlen(obj_name), obj_name);
break;
case SYMLINK_LOOP:
printf("loop %"PRIuMAX"\n%s\n",
(uintmax_t)strlen(obj_name), obj_name);
break;
case NOT_DIR:
printf("notdir %"PRIuMAX"\n%s\n",
(uintmax_t)strlen(obj_name), obj_name);
break;
default:
die("BUG: unknown get_sha1_with_context result %d\n",
result);
break;
}
fflush(stdout);
return 0;
}

if (ctx.mode == 0) {
printf("symlink %"PRIuMAX"\n%s\n",
(uintmax_t)ctx.symlink_path.len,
ctx.symlink_path.buf);
fflush(stdout);
return 0;
}
Expand Down Expand Up @@ -324,7 +358,7 @@ static int batch_objects(struct batch_options *opt)

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

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

if (unset) {
memset(bo, 0, sizeof(*bo));
return 0;
if (bo->enabled) {
return 1;
}

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

Expand Down Expand Up @@ -402,6 +437,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
usage_with_options(cat_file_usage, options);
}

if (batch.follow_symlinks && !batch.enabled) {
usage_with_options(cat_file_usage, options);
}

if (batch.enabled)
return batch_objects(&batch);

Expand Down
Loading

0 comments on commit 122d534

Please sign in to comment.