Merge branch 'dt/cat-file-follow-symlinks'
authorJunio C Hamano <gitster@pobox.com>
Mon, 1 Jun 2015 19:45:16 +0000 (12:45 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 Jun 2015 19:45:16 +0000 (12:45 -0700)
"git cat-file --batch(-check)" learned the "--follow-symlinks"
option that follows an in-tree symbolic link when asked about an
object via extended SHA-1 syntax, e.g. HEAD:RelNotes that points at
Documentation/RelNotes/2.5.0.txt. With the new option, the command
behaves as if HEAD:Documentation/RelNotes/2.5.0.txt was given as
input instead.

* dt/cat-file-follow-symlinks:
cat-file: add --follow-symlinks to --batch
sha1_name: get_sha1_with_context learns to follow symlinks
tree-walk: learn get_tree_entry_follow_symlinks

1  2 
Documentation/git-cat-file.txt
builtin/cat-file.c
cache.h
sha1_name.c
t/t1006-cat-file.sh
index 499ae7b98a4d9bf48aa2c5a235608dfcffcc1c10,3568aff0de6bd2afdda7fe8b1ca1e4380e3e956c..319ab4cb086874da5e2f153d69d8f9af1f18461e
@@@ -9,8 -9,8 +9,8 @@@ git-cat-file - Provide content or type 
  SYNOPSIS
  --------
  [verse]
 -'git cat-file' (-t | -s | -e | -p | <type> | --textconv ) <object>
 +'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -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,9 -69,62 +69,65 @@@ OPTION
        not be combined with any other options or arguments.  See the
        section `BATCH OUTPUT` below for details.
  
 +--allow-unknown-type::
 +      Allow -s or -t to query broken/corrupt objects of unknown type.
 +
+ --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>.
index ecb488822f903c56b5d14bdec3130c71817e1b65,43338bb7de46e8e2d4babe9454f7fd32e8e54c37..049a95f1f113289a1a01f74a2485d1e9b282209f
@@@ -8,9 -8,9 +8,10 @@@
  #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)
 +static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
 +                      int unknown_type)
  {
        unsigned char sha1[20];
        enum object_type type;
@@@ -332,8 -357,8 +366,8 @@@ static int batch_objects(struct batch_o
  }
  
  static const char * const cat_file_usage[] = {
 -      N_("git cat-file (-t | -s | -e | -p | <type> | --textconv) <object>"),
 +      N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-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
  };
  
diff --cc cache.h
Simple merge
diff --cc sha1_name.c
Simple merge
index 4f225db9a72729e99f5eaab0b81ec579691b34c7,a494013ed32d82ffa0d02d04ecf8a0836c33044b..93a4794930bfebc99504a245392f32a3c92b3dde
@@@ -308,37 -303,202 +315,236 @@@ test_expect_success '%(deltabase) repor
        }
  '
  
 +bogus_type="bogus"
 +bogus_content="bogus"
 +bogus_size=$(strlen "$bogus_content")
 +bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
 +
 +test_expect_success "Type of broken object is correct" '
 +      echo $bogus_type >expect &&
 +      git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success "Size of broken object is correct" '
 +      echo $bogus_size >expect &&
 +      git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
 +      test_cmp expect actual
 +'
 +bogus_type="abcdefghijklmnopqrstuvwxyz1234679"
 +bogus_content="bogus"
 +bogus_size=$(strlen "$bogus_content")
 +bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
 +
 +test_expect_success "Type of broken object is correct when type is large" '
 +      echo $bogus_type >expect &&
 +      git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success "Size of large broken object is correct when type is large" '
 +      echo $bogus_size >expect &&
 +      git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
 +      test_cmp expect actual
 +'
 +
+ # 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