* [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks
@ 2015-05-08 18:13 dturner
2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
` (3 more replies)
0 siblings, 4 replies; 20+ messages in thread
From: dturner @ 2015-05-08 18:13 UTC (permalink / raw)
To: git; +Cc: David Turner
From: David Turner <dturner@twitter.com>
Add a new function, get_tree_entry_follow_symlinks, to tree-walk.[ch].
The function is not yet used. It will be used to implement git
cat-file --batch --follow-symlinks.
The function locates an object by path, following symlinks in the
repository. If the symlinks lead outside the repository, the function
reports this to the caller.
Signed-off-by: David Turner <dturner@twitter.com>
---
tree-walk.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tree-walk.h | 2 +
2 files changed, 224 insertions(+)
diff --git a/tree-walk.c b/tree-walk.c
index 5dd9a71..6fb4b7d 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -415,6 +415,228 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
return error;
}
+static int find_tree_entry_nonrecursive(struct tree_desc *t, char *name, unsigned char *result, unsigned *mode) {
+ int namelen = strlen(name);
+
+ while (t->size) {
+ const char *entry;
+ const unsigned char *sha1;
+ int entrylen, cmp;
+
+ sha1 = tree_entry_extract(t, &entry, mode);
+ entrylen = tree_entry_len(&t->entry);
+ update_tree_entry(t);
+ if (entrylen > namelen)
+ continue;
+ cmp = memcmp(name, entry, entrylen);
+ if (cmp > 0)
+ continue;
+ if (cmp < 0)
+ break;
+ if (entrylen == namelen) {
+ hashcpy(result, sha1);
+ return 0;
+ }
+ if (name[entrylen] != '/')
+ continue;
+ if (!S_ISDIR(*mode))
+ break;
+ hashcpy(result, sha1);
+ return 0;
+ }
+ return -1;
+}
+
+struct dir_state {
+ void *tree;
+ unsigned long size;
+ unsigned char sha1[20];
+};
+
+#define GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS 40
+
+/**
+ * Find a tree entry by following symlinks in tree_sha (which is
+ * assumed to be the root of the repository). In the event that a
+ * symlink points outside the repository (e.g. a link to /foo or a
+ * root-level link to ../foo), the portion of the link which is
+ * outside the repository will be copied into result_path (which is
+ * assumed to hold at least PATH_MAX bytes), and *mode will be set to
+ * 0. Otherwise, result will be filled in with the sha1 of the found
+ * object, and *mode will hold the mode of the object.
+ */
+int get_tree_enty_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, unsigned char *result_path, unsigned *mode)
+{
+ int retval = -1;
+ void *tree;
+ struct dir_state *parents = NULL;
+ size_t parents_cap = 0;
+ ssize_t parents_len = 0;
+ unsigned long size;
+ unsigned char root[20];
+ unsigned char current_tree_sha1[20];
+ struct strbuf namebuf = STRBUF_INIT;
+ enum object_type type;
+ int already_have_tree = 0;
+ struct tree_desc t = {0};
+ int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
+ int i;
+
+ strbuf_addstr(&namebuf, name);
+ hashcpy(current_tree_sha1, tree_sha1);
+
+ while (1) {
+ char *first_slash;
+ char *remainder = NULL;
+ int find_result;
+
+ if (!t.buffer) {
+ tree = read_object_with_reference(current_tree_sha1,
+ tree_type, &size,
+ root);
+ if (!tree)
+ goto done;
+
+ ALLOC_GROW(parents, parents_len + 1, parents_cap);
+ parents[parents_len].tree = tree;
+ parents[parents_len].size = size;
+ hashcpy(parents[parents_len].sha1, root);
+
+ parents_len++;
+
+ if (namebuf.buf[0] == '\0') {
+ hashcpy(result, root);
+ retval = 0;
+ goto done;
+ }
+
+ if (!size)
+ goto done;
+
+ /* descend */
+ init_tree_desc(&t, tree, size);
+ }
+
+ /* Handle symlinks to e.g. a//b by removing leading slashes */
+ while (namebuf.buf[0] == '/') {
+ strbuf_remove(&namebuf, 0, 1);
+ }
+
+ /* Split namebuf into a first component and a
+ * remainder */
+ if ((first_slash = strchr(namebuf.buf, '/'))) {
+ *first_slash = 0;
+ remainder = first_slash + 1;
+ }
+
+ if (!strcmp(namebuf.buf, "..")) {
+ struct dir_state *parent;
+ /* We could end up with .. in the namebuf if
+ * it appears in a symlink. */
+
+ if (parents_len == 1) {
+ if (remainder)
+ *first_slash = '/';
+ if (strlcpy(result_path, namebuf.buf,
+ PATH_MAX) < PATH_MAX) {
+ *mode = 0;
+ retval = 0;
+ }
+ goto done;
+ }
+ parent = &parents[parents_len - 1];
+ free(parent->tree);
+ parents_len--;
+ parent = &parents[parents_len - 1];
+ init_tree_desc(&t, parent->tree, parent->size);
+ strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
+ continue;
+ }
+
+ /* We could end up here via a symlink to dir/.. */
+ if (namebuf.buf[0] == '\0') {
+ hashcpy(result, parents[parents_len - 1].sha1);
+ retval = 0;
+ goto done;
+ }
+
+ /* Look up the first (or only) path component
+ * in the tree. */
+ find_result = find_tree_entry_nonrecursive(&t, namebuf.buf,
+ current_tree_sha1,
+ mode);
+ if (find_result) {
+ retval = find_result;
+ goto done;
+ }
+
+ if (S_ISDIR(*mode)) {
+ if (!remainder) {
+ hashcpy(result, current_tree_sha1);
+ retval = 0;
+ goto done;
+ }
+ /* Descend the tree */
+ t.buffer = NULL;
+ strbuf_remove(&namebuf, 0,
+ 1 + first_slash - namebuf.buf);
+ } else if (S_ISREG(*mode)) {
+ if (!remainder) {
+ hashcpy(result, current_tree_sha1);
+ retval = 0;
+ }
+ goto done;
+ } else if (S_ISLNK(*mode)) {
+ /* Follow a symlink */
+ size_t link_len, len;
+ char *contents, *contents_start;
+ struct dir_state *parent;
+
+ if (follows_remaining-- == 0)
+ /* Too many symlinks followed */
+ goto done;
+
+ contents = read_sha1_file(current_tree_sha1, &type,
+ &link_len);
+
+ if (!contents)
+ goto done;
+
+ if (contents[0] == '/') {
+ if (strlcpy(result_path,
+ contents, PATH_MAX) < PATH_MAX) {
+ *mode = 0;
+ retval = 0;
+ }
+ goto done;
+ }
+
+ if (remainder)
+ len = first_slash - namebuf.buf;
+ else
+ len = namebuf.len;
+
+ contents_start = contents;
+
+ parent = &parents[parents_len - 1];
+ init_tree_desc(&t, parent->tree, parent->size);
+ strbuf_splice(&namebuf, 0, len,
+ contents_start, link_len);
+ if (remainder)
+ namebuf.buf[link_len] = '/';
+ free(contents);
+ }
+ }
+done:
+ for (i = 0; i < parents_len; ++i) {
+ free(parents[i].tree);
+ }
+ free(parents);
+
+ strbuf_release(&namebuf);
+ return retval;
+}
+
static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
{
int namelen = strlen(name);
diff --git a/tree-walk.h b/tree-walk.h
index ae7fb3a..002e5a9 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -40,6 +40,8 @@ struct traverse_info;
typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
+int get_tree_enty_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, unsigned char *result_path, unsigned *mode);
+
struct traverse_info {
struct traverse_info *prev;
struct name_entry name;
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 18:13 [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks dturner
@ 2015-05-08 18:13 ` dturner
2015-05-08 19:45 ` Eric Sunshine
2015-05-08 21:31 ` Junio C Hamano
2015-05-08 18:13 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch dturner
` (2 subsequent siblings)
3 siblings, 2 replies; 20+ messages in thread
From: dturner @ 2015-05-08 18:13 UTC (permalink / raw)
To: git; +Cc: David Turner
From: David Turner <dturner@twitter.com>
Wire up get_sha1_with_context to call get_tree_entry_follow_symlinks
when GET_SHA1_FOLLOW_SYMLINKS is passed in flags. G_S_FOLLOW_SYMLINKS
is incompatible with G_S_ONLY_TO_DIE because the diagnosis that
ONLY_TO_DIE triggers does not consider symlinks.
Signed-off-by: David Turner <dturner@twitter.com>
---
cache.h | 15 ++++++++-------
sha1_name.c | 8 ++++++++
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/cache.h b/cache.h
index 3d3244b..16743de 100644
--- a/cache.h
+++ b/cache.h
@@ -924,13 +924,14 @@ struct object_context {
unsigned mode;
};
-#define GET_SHA1_QUIETLY 01
-#define GET_SHA1_COMMIT 02
-#define GET_SHA1_COMMITTISH 04
-#define GET_SHA1_TREE 010
-#define GET_SHA1_TREEISH 020
-#define GET_SHA1_BLOB 040
-#define GET_SHA1_ONLY_TO_DIE 04000
+#define GET_SHA1_QUIETLY 01
+#define GET_SHA1_COMMIT 02
+#define GET_SHA1_COMMITTISH 04
+#define GET_SHA1_TREE 010
+#define GET_SHA1_TREEISH 020
+#define GET_SHA1_BLOB 040
+#define GET_SHA1_FOLLOW_SYMLINKS 0100
+#define GET_SHA1_ONLY_TO_DIE 04000
extern int get_sha1(const char *str, unsigned char *sha1);
extern int get_sha1_commit(const char *str, unsigned char *sha1);
diff --git a/sha1_name.c b/sha1_name.c
index 6d10f05..23863f7 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -1434,6 +1434,12 @@ static int get_sha1_with_context_1(const char *name,
new_filename = resolve_relative_path(filename);
if (new_filename)
filename = new_filename;
+ if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
+ ret = get_tree_enty_follow_symlinks(tree_sha1,
+ filename, sha1, oc->path, &oc->mode);
+ free(new_filename);
+ return ret;
+ }
ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
if (ret && only_to_die) {
diagnose_invalid_sha1_path(prefix, filename,
@@ -1469,5 +1475,7 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
{
+ if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
+ die(_("internal error: bad flags for get_sha1_with_context"));
return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
}
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/3] cat-file: add --follow-symlinks to --batch
2015-05-08 18:13 [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks dturner
2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
@ 2015-05-08 18:13 ` dturner
2015-05-08 19:51 ` Eric Sunshine
2015-05-08 19:26 ` [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks Junio C Hamano
2015-05-08 19:43 ` Eric Sunshine
3 siblings, 1 reply; 20+ messages in thread
From: dturner @ 2015-05-08 18:13 UTC (permalink / raw)
To: git; +Cc: David Turner
From: David Turner <dturner@twitter.com>
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@twitter.com>
---
Documentation/git-cat-file.txt | 28 ++++++-
builtin/cat-file.c | 23 +++++-
t/t1006-cat-file.sh | 184 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 230 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index f6a16f4..18b67a3 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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
-----------
@@ -69,6 +69,19 @@ OPTIONS
not be combined with any other options or arguments. See the
section `BATCH OUTPUT` below for details.
+--follow-symlinks::
+ Follow symlinks inside the repository. Instead of providing
+ output about the link itself, provide output about the linked-to
+ object. This option requires --batch or --batch-check. In the
+ event of a symlink loop (or more than 40 symlinks in a symlink
+ resolution chain), the file will be treated as missing. If a
+ symlink points outside the repository (e.g. a link to /foo or a
+ root-level link to ../foo), the portion of the link which is
+ outside the repository will be printed. Follow-symlinks will
+ be silently turned off if <object> specifies an object in the
+ index rather than one in the object database.
+
+
OUTPUT
------
If '-t' is specified, one of the <type>.
@@ -148,6 +161,19 @@ 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 repository root. For instance, if dir/link points to ../../foo,
+then <symlink> will be ../foo. <size> is the size of the symlink in
+bytes.
+
CAVEATS
-------
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..277af32 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -224,6 +224,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;
};
@@ -232,16 +233,24 @@ 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;
if (!obj_name)
return 1;
- if (get_sha1(obj_name, data->sha1)) {
+ if (get_sha1_with_context(obj_name, flags, data->sha1, &ctx)) {
printf("%s missing\n", obj_name);
fflush(stdout);
return 0;
}
+ if (ctx.mode == 0) {
+ printf("symlink %"PRIuMAX"\n%s\n", (uintmax_t)strlen(ctx.path),
+ ctx.path);
+ return 0;
+ }
+
if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
printf("%s missing\n", obj_name);
fflush(stdout);
@@ -342,9 +351,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;
@@ -369,6 +377,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
OPT_SET_INT(0, "textconv", &opt,
N_("for blob objects, run textconv on object's content"), 'c'),
+ OPT_SET_INT(0, "follow-symlinks", &batch.follow_symlinks,
+ N_("Follow in-repo symlinks; report out-of-repo symlinks (requires --batch or --batch-check)"),
+ 1),
{ OPTION_CALLBACK, 0, "batch", &batch, "format",
N_("show info and content of objects fed from the standard input"),
PARSE_OPT_OPTARG, batch_option_callback },
@@ -402,6 +413,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);
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index ab36b1e..a9ef3a6 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -189,6 +189,13 @@ do
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
@@ -296,4 +303,181 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" > morx &&
+ ln -s morx same-dir-link &&
+ ln -s ../fleem out-of-repo-link &&
+ ln -s .. out-of-repo-link-dir &&
+ ln -s same-dir-link link-to-link &&
+ ln -s nope broken-same-dir-link &&
+ mkdir dir &&
+ ln -s ../morx dir/parent-dir-link &&
+ ln -s .. dir/link-dir &&
+ ln -s ../../escape dir/out-of-repo-link &&
+ ln -s ../.. dir/out-of-repo-link-dir &&
+ ln -s nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ ln -s ../../morx dir/subdir/grandparent-dir-link &&
+ ln -s ../../../great-escape dir/subdir/out-of-repo-link &&
+ ln -s ../../.. dir/subdir/out-of-repo-link-dir &&
+ ln -s ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ ln -s ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ ln -s dir dirlink &&
+ ln -s dir/subdir subdirlink &&
+ ln -s subdir/ind2 dir/link-to-child &&
+ ln -s dir/link-to-child link-to-down-link &&
+ ln -s dir/.. up-down &&
+ ln -s dir/../ up-down-trailing &&
+ ln -s dir/../morx up-down-file &&
+ ln -s dir/../../morx up-up-down-file &&
+ ln -s subdirlink/../../morx up-two-down-file &&
+ ln -s loop1 loop2 &&
+ ln -s loop2 loop1 &&
+ git add . &&
+ git commit -am "test"
+'
+
+echo $hello_sha1 blob $hello_size > found
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing > expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo HEAD:broken-same-dir-link missing > expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/parent-dir-link/nope missing > expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo HEAD:dir/link-dir/nope missing > expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/broken-link-in-dir missing > expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo HEAD:dir/subdir/grandparent-dir-link/nope missing > expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo HEAD:dirlink/morx missing > expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size > expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo HEAD:subdirlink/morx missing > expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir -> subdir links' '
+ echo HEAD:dir/link-to-child/morx missing > expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 > expect &&
+ echo ../fleem >> expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo symlink 2 > expect &&
+ echo .. >> expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 > expect &&
+ echo ../escape >> expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo symlink 2 > expect &&
+ echo .. >> expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 > expect &&
+ echo ../great-escape >> expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo symlink 2 > expect &&
+ echo .. >> expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo symlink 3 > expect &&
+ echo ../ >> expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check > expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual &&
+ echo symlink 7 > expect &&
+ echo ../morx >> expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo HEAD:loop1 missing > expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks > actual &&
+ test_cmp expect actual
+'
test_done
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks
2015-05-08 18:13 [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks dturner
2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
2015-05-08 18:13 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch dturner
@ 2015-05-08 19:26 ` Junio C Hamano
2015-05-08 20:02 ` David Turner
2015-05-08 19:43 ` Eric Sunshine
3 siblings, 1 reply; 20+ messages in thread
From: Junio C Hamano @ 2015-05-08 19:26 UTC (permalink / raw)
To: dturner; +Cc: git, David Turner
dturner@twopensource.com writes:
> From: David Turner <dturner@twitter.com>
>
> Add a new function, get_tree_entry_follow_symlinks, to tree-walk.[ch].
> The function is not yet used. It will be used to implement git
> cat-file --batch --follow-symlinks.
>
> The function locates an object by path, following symlinks in the
> repository. If the symlinks lead outside the repository, the function
> reports this to the caller.
>
> Signed-off-by: David Turner <dturner@twitter.com>
> ---
> tree-walk.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> tree-walk.h | 2 +
> 2 files changed, 224 insertions(+)
>
> diff --git a/tree-walk.c b/tree-walk.c
> index 5dd9a71..6fb4b7d 100644
> --- a/tree-walk.c
> +++ b/tree-walk.c
> @@ -415,6 +415,228 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
> return error;
> }
>
> +static int find_tree_entry_nonrecursive(struct tree_desc *t, char *name, unsigned char *result, unsigned *mode) {
> + int namelen = strlen(name);
> +
> + while (t->size) {
Why do you need an almost duplicate of existing find_tree_entry()
here? The argument "name" above is not const, so isn't that just
the matter of the caller to temporarily replace '/' in name[] before
calling find_tree_entry() if all you wanted to avoid was to prevent
it from falling into get_tree_entry() codepath? Or are there more
to this function?
> +#define GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS 40
Is 40 just a randomly chosen number?
I do not think 40 is particularly unreasonable, but so is 5 (which I
think is also reasonable and is already used as MAXDEPTH in refs.c
to follow symrefs), and am curious where that number came from.
> +/**
> + * Find a tree entry by following symlinks in tree_sha (which is
> + * assumed to be the root of the repository). In the event that a
> + * symlink points outside the repository (e.g. a link to /foo or a
> + * root-level link to ../foo), the portion of the link which is
> + * outside the repository will be copied into result_path (which is
> + * assumed to hold at least PATH_MAX bytes), and *mode will be set to
> + * 0.
As the API to this new function is not constrained by existing
callers, you might want to consider using strbuf for result_path,
which would make it easier for both the callers and this function.
> +int get_tree_enty_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, unsigned char *result_path, unsigned *mode)
> +{
> + int retval = -1;
> + void *tree;
> + struct dir_state *parents = NULL;
> + size_t parents_cap = 0;
> + ssize_t parents_len = 0;
It is customary to name variables to control an ALLOC_GROW()-managed
array 'foo' as foo_nr and foo_alloc. Deviating from the convention
makes the patch harder to read by people who are familiar with it
without any benefit, and those who are familiar with the existing
code are the people you want your patch reviewed by.
I am context-switching now; will review the remainder some other
time.
Thanks.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks
2015-05-08 18:13 [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks dturner
` (2 preceding siblings ...)
2015-05-08 19:26 ` [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks Junio C Hamano
@ 2015-05-08 19:43 ` Eric Sunshine
3 siblings, 0 replies; 20+ messages in thread
From: Eric Sunshine @ 2015-05-08 19:43 UTC (permalink / raw)
To: David Turner; +Cc: Git List, David Turner
On Fri, May 8, 2015 at 2:13 PM, <dturner@twopensource.com> wrote:
> tree-walk: learn get_tree_enty_follow_symlinks
s/enty/entry/ here and in several places in the patch itself.
> Add a new function, get_tree_entry_follow_symlinks, to tree-walk.[ch].
> The function is not yet used. It will be used to implement git
> cat-file --batch --follow-symlinks.
>
> The function locates an object by path, following symlinks in the
> repository. If the symlinks lead outside the repository, the function
> reports this to the caller.
>
> Signed-off-by: David Turner <dturner@twitter.com>
> ---
> diff --git a/tree-walk.c b/tree-walk.c
> index 5dd9a71..6fb4b7d 100644
> --- a/tree-walk.c
> +++ b/tree-walk.c
> @@ -415,6 +415,228 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
> static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
> {
> diff --git a/tree-walk.h b/tree-walk.h
> index ae7fb3a..002e5a9 100644
> --- a/tree-walk.h
> +++ b/tree-walk.h
> @@ -40,6 +40,8 @@ struct traverse_info;
> typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
> int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
>
> +int get_tree_enty_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, unsigned char *result_path, unsigned *mode);
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
@ 2015-05-08 19:45 ` Eric Sunshine
2015-05-08 20:17 ` Junio C Hamano
2015-05-08 21:31 ` Junio C Hamano
1 sibling, 1 reply; 20+ messages in thread
From: Eric Sunshine @ 2015-05-08 19:45 UTC (permalink / raw)
To: David Turner; +Cc: Git List, David Turner
On Fri, May 8, 2015 at 2:13 PM, <dturner@twopensource.com> wrote:
> Wire up get_sha1_with_context to call get_tree_entry_follow_symlinks
> when GET_SHA1_FOLLOW_SYMLINKS is passed in flags. G_S_FOLLOW_SYMLINKS
> is incompatible with G_S_ONLY_TO_DIE because the diagnosis that
> ONLY_TO_DIE triggers does not consider symlinks.
>
> Signed-off-by: David Turner <dturner@twitter.com>
> ---
> diff --git a/sha1_name.c b/sha1_name.c
> index 6d10f05..23863f7 100644
> --- a/sha1_name.c
> +++ b/sha1_name.c
> @@ -1434,6 +1434,12 @@ static int get_sha1_with_context_1(const char *name,
> new_filename = resolve_relative_path(filename);
> if (new_filename)
> filename = new_filename;
> + if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
> + ret = get_tree_enty_follow_symlinks(tree_sha1,
> + filename, sha1, oc->path, &oc->mode);
> + free(new_filename);
> + return ret;
> + }
> ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
> if (ret && only_to_die) {
> diagnose_invalid_sha1_path(prefix, filename,
> @@ -1469,5 +1475,7 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
>
> int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
> {
> + if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
> + die(_("internal error: bad flags for get_sha1_with_context"));
There may not be much value in marking an "internal error" string for
translation.
> return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
> }
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] cat-file: add --follow-symlinks to --batch
2015-05-08 18:13 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch dturner
@ 2015-05-08 19:51 ` Eric Sunshine
0 siblings, 0 replies; 20+ messages in thread
From: Eric Sunshine @ 2015-05-08 19:51 UTC (permalink / raw)
To: David Turner; +Cc: Git List, David Turner
On Fri, May 8, 2015 at 2:13 PM, <dturner@twopensource.com> wrote:
> 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@twitter.com>
> ---
> diff --git a/builtin/cat-file.c b/builtin/cat-file.c
> index df99df4..277af32 100644
> --- a/builtin/cat-file.c
> +++ b/builtin/cat-file.c
> @@ -369,6 +377,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
> OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
> OPT_SET_INT(0, "textconv", &opt,
> N_("for blob objects, run textconv on object's content"), 'c'),
> + OPT_SET_INT(0, "follow-symlinks", &batch.follow_symlinks,
> + N_("Follow in-repo symlinks; report out-of-repo symlinks (requires --batch or --batch-check)"),
> + 1),
Capitalization is not used for descriptions of the other options.
> { OPTION_CALLBACK, 0, "batch", &batch, "format",
> N_("show info and content of objects fed from the standard input"),
> PARSE_OPT_OPTARG, batch_option_callback },
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks
2015-05-08 19:26 ` [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks Junio C Hamano
@ 2015-05-08 20:02 ` David Turner
0 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-05-08 20:02 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, David Turner
On Fri, 2015-05-08 at 12:26 -0700, Junio C Hamano wrote:
> > +static int find_tree_entry_nonrecursive(struct tree_desc *t, char *name, unsigned char *result, unsigned *mode) {
> > + int namelen = strlen(name);
> > +
> > + while (t->size) {
>
> Why do you need an almost duplicate of existing find_tree_entry()
> here? The argument "name" above is not const, so isn't that just
> the matter of the caller to temporarily replace '/' in name[] before
> calling find_tree_entry() if all you wanted to avoid was to prevent
> it from falling into get_tree_entry() codepath? Or are there more
> to this function?
Oh, right, actually, we already replaced the '/'! So I can just use the
existing one. That was silly.
> > +#define GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS 40
>
> Is 40 just a randomly chosen number?
>
> I do not think 40 is particularly unreasonable, but so is 5 (which I
> think is also reasonable and is already used as MAXDEPTH in refs.c
> to follow symrefs), and am curious where that number came from.
http://lxr.linux.no/linux+v3.6.4/fs/namei.c#L783
I'll add a comment to that effect.
> > +/**
> > + * Find a tree entry by following symlinks in tree_sha (which is
> > + * assumed to be the root of the repository). In the event that a
> > + * symlink points outside the repository (e.g. a link to /foo or a
> > + * root-level link to ../foo), the portion of the link which is
> > + * outside the repository will be copied into result_path (which is
> > + * assumed to hold at least PATH_MAX bytes), and *mode will be set to
> > + * 0.
>
> As the API to this new function is not constrained by existing
> callers, you might want to consider using strbuf for result_path,
> which would make it easier for both the callers and this function.
The API is sort-of constrained, because the caller has a fixed-size
buffer to fill in (see the 2nd patch in this series).
> It is customary to name variables to control an ALLOC_GROW()-managed
> array 'foo' as foo_nr and foo_alloc. Deviating from the convention
> makes the patch harder to read by people who are familiar with it
> without any benefit, and those who are familiar with the existing
> code are the people you want your patch reviewed by.
Will fix.
> I am context-switching now; will review the remainder some other
> time.
Thanks for the review.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 19:45 ` Eric Sunshine
@ 2015-05-08 20:17 ` Junio C Hamano
2015-05-08 20:25 ` Junio C Hamano
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Junio C Hamano @ 2015-05-08 20:17 UTC (permalink / raw)
To: Eric Sunshine; +Cc: David Turner, Git List, David Turner
On Fri, May 8, 2015 at 12:45 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>> + if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
>> + die(_("internal error: bad flags for get_sha1_with_context"));
>
> There may not be much value in marking an "internal error" string for
> translation.
The whole point of this kind of messages is so that the end users,
when they see it trigger, can notify us and then to allow us to
identify which die() triggered. It is not just "may not be much
value in", but is actively unproductive to make it translatable,
even if we ignore the cost to i18n/l10n teams to translate such
messages.
By the way, we would want to standardise the string before the
colon, so that we can tell users "If you see an error message that
begins with 'internal error:', please report that to us---it is a
programming error". I think the majority of existing code uses the
string "BUG:" for that, and I do not mind using 'internal error:'
for that purpose instead, but the important thing is to use just one
single string thoughout the codebase, so that the "please report
when you see this" message we give the users can be simple.
Thanks.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 20:17 ` Junio C Hamano
@ 2015-05-08 20:25 ` Junio C Hamano
2015-05-08 20:39 ` Jeff King
2015-05-08 20:27 ` David Turner
2015-05-08 20:38 ` Philip Oakley
2 siblings, 1 reply; 20+ messages in thread
From: Junio C Hamano @ 2015-05-08 20:25 UTC (permalink / raw)
To: Eric Sunshine; +Cc: David Turner, Git List, David Turner
Perhaps something like the attached patch is a good idea (I used
"BUG:" for now), I wonder?
Documentation/CodingGuidelines | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index c6e536f..020370e 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -232,7 +232,12 @@ For C programs:
to be translated, that follows immediately after it */
_("Here is a translatable string explained by the above.");
+ - Speaking of translations, do not mark the message to die() that
+ should never trigger unless there is an programming error. E.g.
+
+ die("BUG: frotz() called with NULL pointer to xyzzy parameter");
+
- - Double negation is often harder to understand than no negation
+ - Double negation is not easier to understand than no negation
at all.
- There are two schools of thought when it comes to comparison,
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 20:17 ` Junio C Hamano
2015-05-08 20:25 ` Junio C Hamano
@ 2015-05-08 20:27 ` David Turner
2015-05-08 20:38 ` Philip Oakley
2 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-05-08 20:27 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Eric Sunshine, Git List
On Fri, 2015-05-08 at 13:17 -0700, Junio C Hamano wrote:
> On Fri, May 8, 2015 at 12:45 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> >> + if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
> >> + die(_("internal error: bad flags for get_sha1_with_context"));
> >
> > There may not be much value in marking an "internal error" string for
> > translation.
>
> The whole point of this kind of messages is so that the end users,
> when they see it trigger, can notify us and then to allow us to
> identify which die() triggered. It is not just "may not be much
> value in", but is actively unproductive to make it translatable,
> even if we ignore the cost to i18n/l10n teams to translate such
> messages.
>
> By the way, we would want to standardise the string before the
> colon, so that we can tell users "If you see an error message that
> begins with 'internal error:', please report that to us---it is a
> programming error". I think the majority of existing code uses the
> string "BUG:" for that, and I do not mind using 'internal error:'
> for that purpose instead, but the important thing is to use just one
> single string thoughout the codebase, so that the "please report
> when you see this" message we give the users can be simple.
It appears that "BUG:" is preferred to "internal error:" by a margin of
2:1. So I'll change it. Fixing the remaining ~40 instances of
"internal error" would be a great project for someone new to git
development.
>From an i18n perspective, I think the correct thing to do would be to
i18n the "BUG" portion and nothing else. That way, users can understand
what has happened, but if they report it upstream, git developers know
where in the code to look. But this would probably be complicated to
implement, so maybe not worthwhile.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 20:17 ` Junio C Hamano
2015-05-08 20:25 ` Junio C Hamano
2015-05-08 20:27 ` David Turner
@ 2015-05-08 20:38 ` Philip Oakley
2 siblings, 0 replies; 20+ messages in thread
From: Philip Oakley @ 2015-05-08 20:38 UTC (permalink / raw)
To: Junio C Hamano, Eric Sunshine; +Cc: David Turner, Git List, David Turner
From: "Junio C Hamano" <gitster@pobox.com>
> On Fri, May 8, 2015 at 12:45 PM, Eric Sunshine
> <sunshine@sunshineco.com> wrote:
>>> + if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags &
>>> GET_SHA1_ONLY_TO_DIE)
>>> + die(_("internal error: bad flags for
>>> get_sha1_with_context"));
>>
>> There may not be much value in marking an "internal error" string for
>> translation.
>
> The whole point of this kind of messages is so that the end users,
> when they see it trigger, can notify us and then to allow us to
> identify which die() triggered. It is not just "may not be much
> value in", but is actively unproductive to make it translatable,
> even if we ignore the cost to i18n/l10n teams to translate such
> messages.
>
> By the way, we would want to standardise the string before the
> colon, so that we can tell users "If you see an error message that
> begins with 'internal error:', please report that to us---it is a
> programming error". I think the majority of existing code uses the
> string "BUG:" for that, and I do not mind using 'internal error:'
> for that purpose instead, but the important thing is to use just one
> single string thoughout the codebase, so that the "please report
> when you see this" message we give the users can be simple.
>
I would see "BUG:" as being internationally recognised, while "internal
error:" would need to be translated, and perhaps be written "internal
error, report this to git@vger.kernel.org:" to ensure that the reader
knows immediately what to do.
--
Philip
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 20:25 ` Junio C Hamano
@ 2015-05-08 20:39 ` Jeff King
2015-05-08 21:22 ` Junio C Hamano
0 siblings, 1 reply; 20+ messages in thread
From: Jeff King @ 2015-05-08 20:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Eric Sunshine, David Turner, Git List, David Turner
On Fri, May 08, 2015 at 01:25:45PM -0700, Junio C Hamano wrote:
> Perhaps something like the attached patch is a good idea (I used
> "BUG:" for now), I wonder?
>
> Documentation/CodingGuidelines | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
Yeah, this looks good (and we definitely prefer BUG:, though I am
responsible for over 1/3 of the existing incidences).
I have been tempted to provide a macro or function for it, and have it
actually call abort() to trigger a coredump. I.e., basically assert(),
except unconditional but you get to write a more useful message.
So all of the sites would just become:
BUG("frotz() called with NULL pointer to xyzzy parameter");
or similar.
-Peff
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 20:39 ` Jeff King
@ 2015-05-08 21:22 ` Junio C Hamano
0 siblings, 0 replies; 20+ messages in thread
From: Junio C Hamano @ 2015-05-08 21:22 UTC (permalink / raw)
To: Jeff King; +Cc: Eric Sunshine, David Turner, Git List, David Turner
Jeff King <peff@peff.net> writes:
> On Fri, May 08, 2015 at 01:25:45PM -0700, Junio C Hamano wrote:
>
>> Perhaps something like the attached patch is a good idea (I used
>> "BUG:" for now), I wonder?
>>
>> Documentation/CodingGuidelines | 7 ++++++-
>> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> Yeah, this looks good (and we definitely prefer BUG:, though I am
> responsible for over 1/3 of the existing incidences).
>
> I have been tempted to provide a macro or function for it, and have it
> actually call abort() to trigger a coredump. I.e., basically assert(),
> except unconditional but you get to write a more useful message.
>
> So all of the sites would just become:
>
> BUG("frotz() called with NULL pointer to xyzzy parameter");
>
> or similar.
Yeah, that sounds more sensible (especially the abort() part).
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks
2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
2015-05-08 19:45 ` Eric Sunshine
@ 2015-05-08 21:31 ` Junio C Hamano
1 sibling, 0 replies; 20+ messages in thread
From: Junio C Hamano @ 2015-05-08 21:31 UTC (permalink / raw)
To: dturner; +Cc: git, David Turner
dturner@twopensource.com writes:
> diff --git a/sha1_name.c b/sha1_name.c
> index 6d10f05..23863f7 100644
> --- a/sha1_name.c
> +++ b/sha1_name.c
> @@ -1434,6 +1434,12 @@ static int get_sha1_with_context_1(const char *name,
> new_filename = resolve_relative_path(filename);
> if (new_filename)
> filename = new_filename;
> + if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
> + ret = get_tree_enty_follow_symlinks(tree_sha1,
> + filename, sha1, oc->path, &oc->mode);
> + free(new_filename);
> + return ret;
> + }
> ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
> if (ret && only_to_die) {
> diagnose_invalid_sha1_path(prefix, filename,
In general, I'd prefer to avoid this kind of "I only need this part
of the existing codeflow and I'll ignore rest by returning early".
You cannot anticipate any future needs of callers (e.g. only-to-die
flag might want to be passed when doing follow-symlinks in the
future) and clean-ups that may be added to the later part of the
function that you are skipping with this early return.
In other words,
if (new_filename)
filename = new_filename;
- ret = get_tree_entry(...);
+ if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
+ ret = ... whatever new thing you want to do ...
+ } else {
+ ret = get_tree_entry(... just reindented ... );
+ }
if (ret && ...)
is what we would prefer to see here.
> @@ -1469,5 +1475,7 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
>
> int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
> {
> + if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
> + die(_("internal error: bad flags for get_sha1_with_context"));
> return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
> }
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 3/3] cat-file: add --follow-symlinks to --batch
2015-05-13 18:21 [PATCH v8 0/3] git cat-file --follow-symlinks David Turner
@ 2015-05-13 18:21 ` David Turner
0 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-05-13 18:21 UTC (permalink / raw)
To: git; +Cc: David Turner
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@twitter.com>
---
| 102 +++++++++++++++++++-
| 46 ++++++++-
| 205 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 347 insertions(+), 6 deletions(-)
--git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index f6a16f4..3763ea2 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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
-----------
@@ -69,6 +69,65 @@ 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.
+[normal]
+ 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.
+[normal]
+ This option cannot be used unless `--batch` or `--batch-check`
+ is used, because it would be impossible to distinguish between
+ the output for an out-of-repo symlink, and the contents of a
+ blob.
+[normal]
+ 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
+--
+[normal]
+ For a regular file `f`, `echo HEAD:f | git cat-file --batch` would
+ print
++
+--
+ ce013625030ba8dba906f756967f9e9ca394464a blob 6
+--
+[normal]
+ 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`.
+[normal]
+ Without `--follow-symlinks`, these would print data about the
+ symlink itself. In the case of `HEAD:link`, you would see
++
+--
+ 4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
+--
+[normal]
+ 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>.
@@ -148,6 +207,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
-------
--git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..6ed4807 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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)
{
@@ -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;
};
@@ -232,12 +234,40 @@ 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", strlen(obj_name),
+ obj_name);
+ break;
+ case SYMLINK_LOOP:
+ printf("loop %"PRIuMAX"\n%s\n", strlen(obj_name),
+ obj_name);
+ break;
+ case NOT_DIR:
+ printf("notdir %"PRIuMAX"\n%s\n", strlen(obj_name),
+ obj_name);
+ 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;
}
@@ -342,9 +372,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;
@@ -369,6 +398,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
OPT_SET_INT(0, "textconv", &opt,
N_("for blob objects, run textconv on object's content"), 'c'),
+ OPT_SET_INT(0, "follow-symlinks", &batch.follow_symlinks,
+ N_("follow in-repo symlinks; report out-of-repo symlinks (requires --batch or --batch-check)"),
+ 1),
{ OPTION_CALLBACK, 0, "batch", &batch, "format",
N_("show info and content of objects fed from the standard input"),
PARSE_OPT_OPTARG, batch_option_callback },
@@ -402,6 +434,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);
--git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index ab36b1e..a494013 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -189,6 +189,13 @@ do
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
@@ -296,4 +303,202 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" >morx &&
+ test_ln_s_add morx same-dir-link &&
+ test_ln_s_add dir link-to-dir &&
+ test_ln_s_add ../fleem out-of-repo-link &&
+ test_ln_s_add .. out-of-repo-link-dir &&
+ test_ln_s_add same-dir-link link-to-link &&
+ test_ln_s_add nope broken-same-dir-link &&
+ mkdir dir &&
+ test_ln_s_add ../morx dir/parent-dir-link &&
+ test_ln_s_add .. dir/link-dir &&
+ test_ln_s_add ../../escape dir/out-of-repo-link &&
+ test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+ test_ln_s_add nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+ test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+ test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+ test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ test_ln_s_add dir dirlink &&
+ test_ln_s_add dir/subdir subdirlink &&
+ test_ln_s_add subdir/ind2 dir/link-to-child &&
+ test_ln_s_add dir/link-to-child link-to-down-link &&
+ test_ln_s_add dir/.. up-down &&
+ test_ln_s_add dir/../ up-down-trailing &&
+ test_ln_s_add dir/../morx up-down-file &&
+ test_ln_s_add dir/../../morx up-up-down-file &&
+ test_ln_s_add subdirlink/../../morx up-two-down-file &&
+ test_ln_s_add loop1 loop2 &&
+ test_ln_s_add loop2 loop1 &&
+ git add morx dir/subdir/ind2 dir/ind1 &&
+ git commit -am "test" &&
+ echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing >expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+ echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo dangling 25 >expect &&
+ echo HEAD:broken-same-dir-link >>expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo notdir 29 >expect &&
+ echo HEAD:dir/parent-dir-link/nope >>expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo dangling 22 >expect &&
+ echo HEAD:dir/link-dir/nope >>expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo dangling 27 >expect &&
+ echo HEAD:dir/broken-link-in-dir >>expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo notdir 41 >expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo dangling 17 >expect &&
+ echo HEAD:dirlink/morx >>expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size >expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo dangling 20 >expect &&
+ echo HEAD:subdirlink/morx >>expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+ echo notdir 27 >expect &&
+ echo HEAD:dir/link-to-child/morx >>expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 >expect &&
+ echo ../fleem >>expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 >expect &&
+ echo ../escape >>expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 >expect &&
+ echo ../great-escape >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 3 >expect &&
+ echo ../ >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check >expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo symlink 7 >expect &&
+ echo ../morx >>expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo loop 10 >expect &&
+ echo HEAD:loop1 >>expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+ echo HEAD:morx | git cat-file --batch >expect &&
+ echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+ test_cmp expect actual
+'
test_done
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/3] cat-file: add --follow-symlinks to --batch
2015-05-13 18:23 [PATCH v9 0/3] David Turner
@ 2015-05-13 18:23 ` David Turner
0 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-05-13 18:23 UTC (permalink / raw)
To: git; +Cc: David Turner
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@twitter.com>
---
| 102 +++++++++++++++++++-
| 50 +++++++++-
| 205 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 351 insertions(+), 6 deletions(-)
--git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index f6a16f4..3763ea2 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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
-----------
@@ -69,6 +69,65 @@ 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.
+[normal]
+ 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.
+[normal]
+ This option cannot be used unless `--batch` or `--batch-check`
+ is used, because it would be impossible to distinguish between
+ the output for an out-of-repo symlink, and the contents of a
+ blob.
+[normal]
+ 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
+--
+[normal]
+ For a regular file `f`, `echo HEAD:f | git cat-file --batch` would
+ print
++
+--
+ ce013625030ba8dba906f756967f9e9ca394464a blob 6
+--
+[normal]
+ 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`.
+[normal]
+ Without `--follow-symlinks`, these would print data about the
+ symlink itself. In the case of `HEAD:link`, you would see
++
+--
+ 4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
+--
+[normal]
+ 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>.
@@ -148,6 +207,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
-------
--git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..095554b 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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)
{
@@ -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;
};
@@ -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;
}
@@ -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;
@@ -369,6 +402,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
OPT_SET_INT(0, "textconv", &opt,
N_("for blob objects, run textconv on object's content"), 'c'),
+ OPT_SET_INT(0, "follow-symlinks", &batch.follow_symlinks,
+ N_("follow in-repo symlinks; report out-of-repo symlinks (requires --batch or --batch-check)"),
+ 1),
{ OPTION_CALLBACK, 0, "batch", &batch, "format",
N_("show info and content of objects fed from the standard input"),
PARSE_OPT_OPTARG, batch_option_callback },
@@ -402,6 +438,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);
--git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index ab36b1e..a494013 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -189,6 +189,13 @@ do
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
@@ -296,4 +303,202 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" >morx &&
+ test_ln_s_add morx same-dir-link &&
+ test_ln_s_add dir link-to-dir &&
+ test_ln_s_add ../fleem out-of-repo-link &&
+ test_ln_s_add .. out-of-repo-link-dir &&
+ test_ln_s_add same-dir-link link-to-link &&
+ test_ln_s_add nope broken-same-dir-link &&
+ mkdir dir &&
+ test_ln_s_add ../morx dir/parent-dir-link &&
+ test_ln_s_add .. dir/link-dir &&
+ test_ln_s_add ../../escape dir/out-of-repo-link &&
+ test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+ test_ln_s_add nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+ test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+ test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+ test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ test_ln_s_add dir dirlink &&
+ test_ln_s_add dir/subdir subdirlink &&
+ test_ln_s_add subdir/ind2 dir/link-to-child &&
+ test_ln_s_add dir/link-to-child link-to-down-link &&
+ test_ln_s_add dir/.. up-down &&
+ test_ln_s_add dir/../ up-down-trailing &&
+ test_ln_s_add dir/../morx up-down-file &&
+ test_ln_s_add dir/../../morx up-up-down-file &&
+ test_ln_s_add subdirlink/../../morx up-two-down-file &&
+ test_ln_s_add loop1 loop2 &&
+ test_ln_s_add loop2 loop1 &&
+ git add morx dir/subdir/ind2 dir/ind1 &&
+ git commit -am "test" &&
+ echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing >expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+ echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo dangling 25 >expect &&
+ echo HEAD:broken-same-dir-link >>expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo notdir 29 >expect &&
+ echo HEAD:dir/parent-dir-link/nope >>expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo dangling 22 >expect &&
+ echo HEAD:dir/link-dir/nope >>expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo dangling 27 >expect &&
+ echo HEAD:dir/broken-link-in-dir >>expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo notdir 41 >expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo dangling 17 >expect &&
+ echo HEAD:dirlink/morx >>expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size >expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo dangling 20 >expect &&
+ echo HEAD:subdirlink/morx >>expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+ echo notdir 27 >expect &&
+ echo HEAD:dir/link-to-child/morx >>expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 >expect &&
+ echo ../fleem >>expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 >expect &&
+ echo ../escape >>expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 >expect &&
+ echo ../great-escape >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 3 >expect &&
+ echo ../ >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check >expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo symlink 7 >expect &&
+ echo ../morx >>expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo loop 10 >expect &&
+ echo HEAD:loop1 >>expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+ echo HEAD:morx | git cat-file --batch >expect &&
+ echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+ test_cmp expect actual
+'
test_done
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/3] cat-file: add --follow-symlinks to --batch
2015-05-14 20:17 [PATCH v10 0/3] David Turner
@ 2015-05-14 20:17 ` David Turner
0 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-05-14 20:17 UTC (permalink / raw)
To: git; +Cc: David Turner
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@twitter.com>
---
| 100 +++++++++++++++++++-
| 50 +++++++++-
| 205 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 349 insertions(+), 6 deletions(-)
--git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index f6a16f4..050448c 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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
-----------
@@ -69,6 +69,63 @@ 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.
+[normal]
+ 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.
+[normal]
+ This option cannot (currently) be used unless `--batch` or
+ `--batch-check` is used.
+[normal]
+ 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
+--
+[normal]
+ For a regular file `f`, `echo HEAD:f | git cat-file --batch` would
+ print
++
+--
+ ce013625030ba8dba906f756967f9e9ca394464a blob 6
+--
+[normal]
+ 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`.
+[normal]
+ Without `--follow-symlinks`, these would print data about the
+ symlink itself. In the case of `HEAD:link`, you would see
++
+--
+ 4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
+--
+[normal]
+ 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>.
@@ -148,6 +205,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
-------
--git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..095554b 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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)
{
@@ -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;
};
@@ -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;
}
@@ -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;
@@ -369,6 +402,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
OPT_SET_INT(0, "textconv", &opt,
N_("for blob objects, run textconv on object's content"), 'c'),
+ OPT_SET_INT(0, "follow-symlinks", &batch.follow_symlinks,
+ N_("follow in-repo symlinks; report out-of-repo symlinks (requires --batch or --batch-check)"),
+ 1),
{ OPTION_CALLBACK, 0, "batch", &batch, "format",
N_("show info and content of objects fed from the standard input"),
PARSE_OPT_OPTARG, batch_option_callback },
@@ -402,6 +438,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);
--git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index ab36b1e..a494013 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -189,6 +189,13 @@ do
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
@@ -296,4 +303,202 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" >morx &&
+ test_ln_s_add morx same-dir-link &&
+ test_ln_s_add dir link-to-dir &&
+ test_ln_s_add ../fleem out-of-repo-link &&
+ test_ln_s_add .. out-of-repo-link-dir &&
+ test_ln_s_add same-dir-link link-to-link &&
+ test_ln_s_add nope broken-same-dir-link &&
+ mkdir dir &&
+ test_ln_s_add ../morx dir/parent-dir-link &&
+ test_ln_s_add .. dir/link-dir &&
+ test_ln_s_add ../../escape dir/out-of-repo-link &&
+ test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+ test_ln_s_add nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+ test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+ test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+ test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ test_ln_s_add dir dirlink &&
+ test_ln_s_add dir/subdir subdirlink &&
+ test_ln_s_add subdir/ind2 dir/link-to-child &&
+ test_ln_s_add dir/link-to-child link-to-down-link &&
+ test_ln_s_add dir/.. up-down &&
+ test_ln_s_add dir/../ up-down-trailing &&
+ test_ln_s_add dir/../morx up-down-file &&
+ test_ln_s_add dir/../../morx up-up-down-file &&
+ test_ln_s_add subdirlink/../../morx up-two-down-file &&
+ test_ln_s_add loop1 loop2 &&
+ test_ln_s_add loop2 loop1 &&
+ git add morx dir/subdir/ind2 dir/ind1 &&
+ git commit -am "test" &&
+ echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing >expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+ echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo dangling 25 >expect &&
+ echo HEAD:broken-same-dir-link >>expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo notdir 29 >expect &&
+ echo HEAD:dir/parent-dir-link/nope >>expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo dangling 22 >expect &&
+ echo HEAD:dir/link-dir/nope >>expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo dangling 27 >expect &&
+ echo HEAD:dir/broken-link-in-dir >>expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo notdir 41 >expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo dangling 17 >expect &&
+ echo HEAD:dirlink/morx >>expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size >expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo dangling 20 >expect &&
+ echo HEAD:subdirlink/morx >>expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+ echo notdir 27 >expect &&
+ echo HEAD:dir/link-to-child/morx >>expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 >expect &&
+ echo ../fleem >>expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 >expect &&
+ echo ../escape >>expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 >expect &&
+ echo ../great-escape >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 3 >expect &&
+ echo ../ >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check >expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo symlink 7 >expect &&
+ echo ../morx >>expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo loop 10 >expect &&
+ echo HEAD:loop1 >>expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+ echo HEAD:morx | git cat-file --batch >expect &&
+ echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+ test_cmp expect actual
+'
test_done
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/3] cat-file: add --follow-symlinks to --batch
2015-05-14 20:38 [PATCH v11 0/3] cat-file --follow-symlinks David Turner
@ 2015-05-14 20:38 ` David Turner
0 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-05-14 20:38 UTC (permalink / raw)
To: git; +Cc: David Turner
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@twitter.com>
---
| 100 +++++++++++++++++++-
| 51 ++++++++--
| 205 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 349 insertions(+), 7 deletions(-)
--git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index f6a16f4..050448c 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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
-----------
@@ -69,6 +69,63 @@ 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.
+[normal]
+ 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.
+[normal]
+ This option cannot (currently) be used unless `--batch` or
+ `--batch-check` is used.
+[normal]
+ 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
+--
+[normal]
+ For a regular file `f`, `echo HEAD:f | git cat-file --batch` would
+ print
++
+--
+ ce013625030ba8dba906f756967f9e9ca394464a blob 6
+--
+[normal]
+ 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`.
+[normal]
+ Without `--follow-symlinks`, these would print data about the
+ symlink itself. In the case of `HEAD:link`, you would see
++
+--
+ 4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
+--
+[normal]
+ 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>.
@@ -148,6 +205,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
-------
--git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..43338bb 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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)
{
@@ -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;
};
@@ -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;
}
@@ -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
};
@@ -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;
@@ -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()
};
@@ -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);
--git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index ab36b1e..a494013 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -189,6 +189,13 @@ do
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
@@ -296,4 +303,202 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" >morx &&
+ test_ln_s_add morx same-dir-link &&
+ test_ln_s_add dir link-to-dir &&
+ test_ln_s_add ../fleem out-of-repo-link &&
+ test_ln_s_add .. out-of-repo-link-dir &&
+ test_ln_s_add same-dir-link link-to-link &&
+ test_ln_s_add nope broken-same-dir-link &&
+ mkdir dir &&
+ test_ln_s_add ../morx dir/parent-dir-link &&
+ test_ln_s_add .. dir/link-dir &&
+ test_ln_s_add ../../escape dir/out-of-repo-link &&
+ test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+ test_ln_s_add nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+ test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+ test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+ test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ test_ln_s_add dir dirlink &&
+ test_ln_s_add dir/subdir subdirlink &&
+ test_ln_s_add subdir/ind2 dir/link-to-child &&
+ test_ln_s_add dir/link-to-child link-to-down-link &&
+ test_ln_s_add dir/.. up-down &&
+ test_ln_s_add dir/../ up-down-trailing &&
+ test_ln_s_add dir/../morx up-down-file &&
+ test_ln_s_add dir/../../morx up-up-down-file &&
+ test_ln_s_add subdirlink/../../morx up-two-down-file &&
+ test_ln_s_add loop1 loop2 &&
+ test_ln_s_add loop2 loop1 &&
+ git add morx dir/subdir/ind2 dir/ind1 &&
+ git commit -am "test" &&
+ echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing >expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+ echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo dangling 25 >expect &&
+ echo HEAD:broken-same-dir-link >>expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo notdir 29 >expect &&
+ echo HEAD:dir/parent-dir-link/nope >>expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo dangling 22 >expect &&
+ echo HEAD:dir/link-dir/nope >>expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo dangling 27 >expect &&
+ echo HEAD:dir/broken-link-in-dir >>expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo notdir 41 >expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo dangling 17 >expect &&
+ echo HEAD:dirlink/morx >>expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size >expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo dangling 20 >expect &&
+ echo HEAD:subdirlink/morx >>expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+ echo notdir 27 >expect &&
+ echo HEAD:dir/link-to-child/morx >>expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 >expect &&
+ echo ../fleem >>expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 >expect &&
+ echo ../escape >>expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 >expect &&
+ echo ../great-escape >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 3 >expect &&
+ echo ../ >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check >expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo symlink 7 >expect &&
+ echo ../morx >>expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo loop 10 >expect &&
+ echo HEAD:loop1 >>expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+ echo HEAD:morx | git cat-file --batch >expect &&
+ echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+ test_cmp expect actual
+'
test_done
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/3] cat-file: add --follow-symlinks to --batch
2015-05-14 21:58 [PATCH v12 0/3] git cat-file --follow-symlinks David Turner
@ 2015-05-14 21:58 ` David Turner
0 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-05-14 21:58 UTC (permalink / raw)
To: git; +Cc: David Turner, David Turner
From: David Turner <dturner@twitter.com>
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>
---
Documentation/git-cat-file.txt | 99 +++++++++++++++++++-
builtin/cat-file.c | 51 ++++++++--
t/t1006-cat-file.sh | 205 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 348 insertions(+), 7 deletions(-)
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index f6a16f4..3568aff 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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
-----------
@@ -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>.
@@ -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
-------
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..43338bb 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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)
{
@@ -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;
};
@@ -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;
}
@@ -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
};
@@ -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;
@@ -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()
};
@@ -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);
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index ab36b1e..a494013 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -189,6 +189,13 @@ do
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
@@ -296,4 +303,202 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" >morx &&
+ test_ln_s_add morx same-dir-link &&
+ test_ln_s_add dir link-to-dir &&
+ test_ln_s_add ../fleem out-of-repo-link &&
+ test_ln_s_add .. out-of-repo-link-dir &&
+ test_ln_s_add same-dir-link link-to-link &&
+ test_ln_s_add nope broken-same-dir-link &&
+ mkdir dir &&
+ test_ln_s_add ../morx dir/parent-dir-link &&
+ test_ln_s_add .. dir/link-dir &&
+ test_ln_s_add ../../escape dir/out-of-repo-link &&
+ test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+ test_ln_s_add nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+ test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+ test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+ test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ test_ln_s_add dir dirlink &&
+ test_ln_s_add dir/subdir subdirlink &&
+ test_ln_s_add subdir/ind2 dir/link-to-child &&
+ test_ln_s_add dir/link-to-child link-to-down-link &&
+ test_ln_s_add dir/.. up-down &&
+ test_ln_s_add dir/../ up-down-trailing &&
+ test_ln_s_add dir/../morx up-down-file &&
+ test_ln_s_add dir/../../morx up-up-down-file &&
+ test_ln_s_add subdirlink/../../morx up-two-down-file &&
+ test_ln_s_add loop1 loop2 &&
+ test_ln_s_add loop2 loop1 &&
+ git add morx dir/subdir/ind2 dir/ind1 &&
+ git commit -am "test" &&
+ echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing >expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+ echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo dangling 25 >expect &&
+ echo HEAD:broken-same-dir-link >>expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo notdir 29 >expect &&
+ echo HEAD:dir/parent-dir-link/nope >>expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo dangling 22 >expect &&
+ echo HEAD:dir/link-dir/nope >>expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo dangling 27 >expect &&
+ echo HEAD:dir/broken-link-in-dir >>expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo notdir 41 >expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo dangling 17 >expect &&
+ echo HEAD:dirlink/morx >>expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size >expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo dangling 20 >expect &&
+ echo HEAD:subdirlink/morx >>expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+ echo notdir 27 >expect &&
+ echo HEAD:dir/link-to-child/morx >>expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 >expect &&
+ echo ../fleem >>expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 >expect &&
+ echo ../escape >>expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 >expect &&
+ echo ../great-escape >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 3 >expect &&
+ echo ../ >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check >expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo symlink 7 >expect &&
+ echo ../morx >>expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo loop 10 >expect &&
+ echo HEAD:loop1 >>expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+ echo HEAD:morx | git cat-file --batch >expect &&
+ echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+ test_cmp expect actual
+'
test_done
--
2.0.4.315.gad8727a-twtrsrc
^ permalink raw reply related [flat|nested] 20+ messages in thread
end of thread, other threads:[~2015-05-14 21:58 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-05-08 18:13 [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks dturner
2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
2015-05-08 19:45 ` Eric Sunshine
2015-05-08 20:17 ` Junio C Hamano
2015-05-08 20:25 ` Junio C Hamano
2015-05-08 20:39 ` Jeff King
2015-05-08 21:22 ` Junio C Hamano
2015-05-08 20:27 ` David Turner
2015-05-08 20:38 ` Philip Oakley
2015-05-08 21:31 ` Junio C Hamano
2015-05-08 18:13 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch dturner
2015-05-08 19:51 ` Eric Sunshine
2015-05-08 19:26 ` [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks Junio C Hamano
2015-05-08 20:02 ` David Turner
2015-05-08 19:43 ` Eric Sunshine
-- strict thread matches above, loose matches on Subject: below --
2015-05-13 18:21 [PATCH v8 0/3] git cat-file --follow-symlinks David Turner
2015-05-13 18:21 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch David Turner
2015-05-13 18:23 [PATCH v9 0/3] David Turner
2015-05-13 18:23 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch David Turner
2015-05-14 20:17 [PATCH v10 0/3] David Turner
2015-05-14 20:17 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch David Turner
2015-05-14 20:38 [PATCH v11 0/3] cat-file --follow-symlinks David Turner
2015-05-14 20:38 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch David Turner
2015-05-14 21:58 [PATCH v12 0/3] git cat-file --follow-symlinks David Turner
2015-05-14 21:58 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch David Turner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).