Merge branch 'dt/cat-file-batch-ambiguous'
authorJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:21 +0000 (22:05 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:21 +0000 (22:05 -0800)
"git cat-file --batch" reported a dangling symbolic link by
mistake, when it wanted to report that a given name is ambiguous.

* dt/cat-file-batch-ambiguous:
t1512: test ambiguous cat-file --batch and --batch-output
Do not print 'dangling' for cat-file in case of ambiguity

1  2 
Documentation/git-cat-file.txt
cache.h
tree-walk.c
tree-walk.h
index 9a2e9cdafba59cf731f8e7c2bc564d7942977996,e27a9409854ab9585b110c16155d4708b24a651e..8eca671b8278cfe02692605b80a9121bc5717567
@@@ -23,8 -23,8 +23,8 @@@ In the second form, a list of objects (
  stdin, and the SHA-1, type, and size of each object is printed on stdout. The
  output format can be overridden using the optional `<format>` argument. If
  either `--textconv` or `--filters` was specified, the input is expected to
 -list the object names followed by the path name, separated by a single white
 -space, so that the appropriate drivers can be determined.
 +list the object names followed by the path name, separated by a single
 +whitespace, so that the appropriate drivers can be determined.
  
  OPTIONS
  -------
@@@ -79,7 -79,7 +79,7 @@@
        Print object information and contents for each object provided
        on stdin.  May not be combined with any other options or arguments
        except `--textconv` or `--filters`, in which case the input lines
 -      also need to specify the path, separated by white space.  See the
 +      also need to specify the path, separated by whitespace.  See the
        section `BATCH OUTPUT` below for details.
  
  --batch-check::
@@@ -87,7 -87,7 +87,7 @@@
        Print object information for each object provided on stdin.  May
        not be combined with any other options or arguments except
        `--textconv` or `--filters`, in which case the input lines also
 -      need to specify the path, separated by white space.  See the
 +      need to specify the path, separated by whitespace.  See the
        section `BATCH OUTPUT` below for details.
  
  --batch-all-objects::
@@@ -252,6 -252,12 +252,12 @@@ the repository, then `cat-file` will ig
  <object> SP missing LF
  ------------
  
+ If a name is specified that might refer to more than one object (an ambiguous short sha), then `cat-file` will ignore any custom format and print:
+ ------------
+ <object> SP ambiguous 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:
diff --combined cache.h
index 038e3764a9430e1f90b6b0ebbe0269197eb3e25b,70652e99dca31b5984176b13ca43eeda4d9a5d8b..156a839484e3b646d0e713ecbb41559640e22c35
+++ b/cache.h
@@@ -45,20 -45,10 +45,20 @@@ unsigned long git_deflate_bound(git_zst
  /* The length in bytes and in hex digits of an object name (SHA-1 value). */
  #define GIT_SHA1_RAWSZ 20
  #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 +/* The block size of SHA-1. */
 +#define GIT_SHA1_BLKSZ 64
 +
 +/* The length in bytes and in hex digits of an object name (SHA-256 value). */
 +#define GIT_SHA256_RAWSZ 32
 +#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
 +/* The block size of SHA-256. */
 +#define GIT_SHA256_BLKSZ 64
  
  /* The length in byte and in hex digits of the largest possible hash value. */
 -#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
 -#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
 +#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
 +#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
 +/* The largest possible block size for any supported hash. */
 +#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
  
  struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@@ -755,7 -745,6 +755,7 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  #define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
 +#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  
@@@ -1039,12 -1028,16 +1039,12 @@@ extern const struct object_id null_oid
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
        /*
 -       * This is a temporary optimization hack. By asserting the size here,
 -       * we let the compiler know that it's always going to be 20, which lets
 -       * it turn this fixed-size memcmp into a few inline instructions.
 -       *
 -       * This will need to be extended or ripped out when we learn about
 -       * hashes of different sizes.
 +       * Teach the compiler that there are only two possibilities of hash size
 +       * here, so that it can optimize for this case as much as possible.
         */
 -      if (the_hash_algo->rawsz != 20)
 -              BUG("hash size not yet supported by hashcmp");
 -      return memcmp(sha1, sha2, the_hash_algo->rawsz);
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
  
  static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      return !hashcmp(sha1, sha2);
 +      /*
 +       * We write this here instead of deferring to hashcmp so that the
 +       * compiler can properly inline it and avoid calling memcmp.
 +       */
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
@@@ -1085,7 -1072,7 +1085,7 @@@ static inline void hashcpy(unsigned cha
  
  static inline void oidcpy(struct object_id *dst, const struct object_id *src)
  {
 -      hashcpy(dst->hash, src->hash);
 +      memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
  }
  
  static inline struct object_id *oiddup(const struct object_id *src)
@@@ -1345,6 -1332,24 +1345,24 @@@ struct object_context 
        GET_OID_TREE | GET_OID_TREEISH | \
        GET_OID_BLOB)
  
+ enum get_oid_result {
+       FOUND = 0,
+       MISSING_OBJECT = -1, /* The requested object is missing */
+       SHORT_NAME_AMBIGUOUS = -2,
+       /* The following only apply when symlinks are followed */
+       DANGLING_SYMLINK = -4, /*
+                               * The initial symlink is there, but
+                               * (transitively) points to a missing
+                               * in-tree file
+                               */
+       SYMLINK_LOOP = -5,
+       NOT_DIR = -6, /*
+                      * Somewhere along the symlink chain, a path is
+                      * requested which contains a file as a
+                      * non-final element.
+                      */
+ };
  extern int get_oid(const char *str, struct object_id *oid);
  extern int get_oid_commit(const char *str, struct object_id *oid);
  extern int get_oid_committish(const char *str, struct object_id *oid);
@@@ -1352,7 -1357,7 +1370,7 @@@ extern int get_oid_tree(const char *str
  extern int get_oid_treeish(const char *str, struct object_id *oid);
  extern int get_oid_blob(const char *str, struct object_id *oid);
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
- extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
+ extern enum get_oid_result get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
  
  
  typedef int each_abbrev_fn(const struct object_id *oid, void *);
@@@ -1378,9 -1383,9 +1396,9 @@@ extern int get_oid_hex(const char *hex
  extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  
  /*
 - * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
 + * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
 - * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
 + * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
   * convenience.
   *
   * The non-`_r` variant returns a static buffer, but uses a ring of 4
   *
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
 -extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 -extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 -extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
 -extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
 +char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
 +char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +char *oid_to_hex_r(char *out, const struct object_id *oid);
 +char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);     /* static buffer result! */
 +char *sha1_to_hex(const unsigned char *sha1);                                         /* same static buffer */
 +char *hash_to_hex(const unsigned char *hash);                                         /* same static buffer */
 +char *oid_to_hex(const struct object_id *oid);                                                /* same static buffer */
  
  /*
   * Parse a 40-character hexadecimal object ID starting from hex, updating the
diff --combined tree-walk.c
index 277e3b3243011725cb24578e676309ed4444e485,fb7959b21d1730bca287accb71b21086346ab76b..1e4bbc8a0e48c6afe61dc8aab76488356d7e1cbc
@@@ -48,8 -48,7 +48,8 @@@ static int decode_tree_entry(struct tre
        /* Initialize the descriptor entry */
        desc->entry.path = path;
        desc->entry.mode = canon_mode(mode);
 -      desc->entry.oid  = (const struct object_id *)(path + len);
 +      desc->entry.pathlen = len - 1;
 +      hashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);
  
        return 0;
  }
@@@ -108,7 -107,7 +108,7 @@@ static void entry_extract(struct tree_d
  static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
  {
        const void *buf = desc->buffer;
 -      const unsigned char *end = desc->entry.oid->hash + the_hash_algo->rawsz;
 +      const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
        unsigned long size = desc->size;
        unsigned long len = end - (const unsigned char *)buf;
  
@@@ -176,11 -175,9 +176,11 @@@ void setup_traverse_info(struct travers
                pathlen--;
        info->pathlen = pathlen ? pathlen + 1 : 0;
        info->name.path = base;
 -      info->name.oid = (void *)(base + pathlen + 1);
 -      if (pathlen)
 +      info->name.pathlen = pathlen;
 +      if (pathlen) {
 +              hashcpy(info->name.oid.hash, (const unsigned char *)base + pathlen + 1);
                info->prev = &dummy;
 +      }
  }
  
  char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
@@@ -505,10 -502,10 +505,10 @@@ static int find_tree_entry(struct tree_
        int namelen = strlen(name);
        while (t->size) {
                const char *entry;
 -              const struct object_id *oid;
 +              struct object_id oid;
                int entrylen, cmp;
  
 -              oid = tree_entry_extract(t, &entry, mode);
 +              oidcpy(&oid, tree_entry_extract(t, &entry, mode));
                entrylen = tree_entry_len(&t->entry);
                update_tree_entry(t);
                if (entrylen > namelen)
                if (cmp < 0)
                        break;
                if (entrylen == namelen) {
 -                      oidcpy(result, oid);
 +                      oidcpy(result, &oid);
                        return 0;
                }
                if (name[entrylen] != '/')
                if (!S_ISDIR(*mode))
                        break;
                if (++entrylen == namelen) {
 -                      oidcpy(result, oid);
 +                      oidcpy(result, &oid);
                        return 0;
                }
 -              return get_tree_entry(oid, name + entrylen, result, mode);
 +              return get_tree_entry(&oid, name + entrylen, result, mode);
        }
        return -1;
  }
@@@ -582,10 -579,10 +582,10 @@@ int get_tree_entry(const struct object_
   * with the sha1 of the found object, and *mode will hold the mode of
   * the object.
   *
-  * See the code for enum follow_symlink_result for a description of
+  * See the code for enum get_oid_result for a description of
   * the return values.
   */
- enum follow_symlinks_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)
+ enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)
  {
        int retval = MISSING_OBJECT;
        struct dir_state *parents = NULL;
diff --combined tree-walk.h
index a4ad28ea5e62926b3450a4c7b093c9aa811107cd,de6b95179dbb1d760f3b439096daf290792f2fa4..82251718666eb5dceb33d6140e86de5a627baab1
@@@ -1,12 -1,12 +1,12 @@@
  #ifndef TREE_WALK_H
  #define TREE_WALK_H
  
 -struct index_state;
 -struct strbuf;
 +#include "cache.h"
  
  struct name_entry {
 -      const struct object_id *oid;
 +      struct object_id oid;
        const char *path;
 +      int pathlen;
        unsigned int mode;
  };
  
@@@ -20,12 -20,12 +20,12 @@@ static inline const struct object_id *t
  {
        *pathp = desc->entry.path;
        *modep = desc->entry.mode;
 -      return desc->entry.oid;
 +      return &desc->entry.oid;
  }
  
  static inline int tree_entry_len(const struct name_entry *ne)
  {
 -      return (const char *)ne->oid - ne->path - 1;
 +      return ne->pathlen;
  }
  
  /*
@@@ -51,23 -51,7 +51,7 @@@ 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(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);
  
- enum follow_symlinks_result {
-       FOUND = 0, /* This includes out-of-tree links */
-       MISSING_OBJECT = -1, /* The initial symlink is missing */
-       DANGLING_SYMLINK = -2, /*
-                               * The initial symlink is there, but
-                               * (transitively) points to a missing
-                               * in-tree file
-                               */
-       SYMLINK_LOOP = -3,
-       NOT_DIR = -4, /*
-                      * Somewhere along the symlink chain, a path is
-                      * requested which contains a file as a
-                      * non-final element.
-                      */
- };
- enum follow_symlinks_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);
+ enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);
  
  struct traverse_info {
        const char *traverse_path;