Merge branch 'nd/submodule-pathspec-ending-with-slash'
authorJunio C Hamano <gitster@pobox.com>
Thu, 27 Feb 2014 22:01:15 +0000 (14:01 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 27 Feb 2014 22:01:15 +0000 (14:01 -0800)
Allow "git cmd path/", when the 'path' is where a submodule is
bound to the top-level working tree, to match 'path', despite the
extra and unnecessary trailing slash.

* nd/submodule-pathspec-ending-with-slash:
clean: use cache_name_is_other()
clean: replace match_pathspec() with dir_path_match()
pathspec: pass directory indicator to match_pathspec_item()
match_pathspec: match pathspec "foo/" against directory "foo"
dir.c: prepare match_pathspec_item for taking more flags
pathspec: rename match_pathspec_depth() to match_pathspec()
pathspec: convert some match_pathspec_depth() to dir_path_match()
pathspec: convert some match_pathspec_depth() to ce_path_match()

1  2 
builtin/commit.c
builtin/rm.c
builtin/update-index.c
cache.h
read-cache.c
resolve-undo.c
t/t4010-diff-pathspec.sh
diff --combined builtin/commit.c
index 9c51b129e856b9c00a93097dcebd5e8b9c5fe128,26b2986abe679b594798748b4776917050b1bd4d..824be655857bf4fa5b1992054ef1f8948835df85
@@@ -234,7 -234,7 +234,7 @@@ static int list_paths(struct string_lis
  
                if (ce->ce_flags & CE_UPDATE)
                        continue;
-               if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
+               if (!ce_path_match(ce, pattern, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@@ -1406,10 -1406,6 +1406,10 @@@ static int git_commit_config(const cha
        }
        if (!strcmp(k, "commit.cleanup"))
                return git_config_string(&cleanup_arg, k, v);
 +      if (!strcmp(k, "commit.gpgsign")) {
 +              sign_commit = git_config_bool(k, v) ? "" : NULL;
 +              return 0;
 +      }
  
        status = git_gpg_config(k, v, NULL);
        if (status)
diff --combined builtin/rm.c
index 171f37c1cc5371c307ef16ce67fefc1c40fbc2c2,05642184c5d89addae1900660ad99e5f29c324f2..960634dd0c52f1da689e8a54980e5e81cf2d2f36
@@@ -308,10 -308,10 +308,10 @@@ int cmd_rm(int argc, const char **argv
  
        for (i = 0; i < active_nr; i++) {
                const struct cache_entry *ce = active_cache[i];
-               if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))
+               if (!ce_path_match(ce, &pathspec, seen))
                        continue;
                ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
 -              list.entry[list.nr].name = ce->name;
 +              list.entry[list.nr].name = xstrdup(ce->name);
                list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
                if (list.entry[list.nr++].is_submodule &&
                    !is_staging_gitmodules_ok())
diff --combined builtin/update-index.c
index 00313f373aadd989e0627b0b3068676c66c17a9b,aaa6f78f1629311257291ba1075ddd7c7dd1a3ed..d12ad95f3e210f780d95334d59a58e6b9d3b69ad
@@@ -12,6 -12,7 +12,7 @@@
  #include "resolve-undo.h"
  #include "parse-options.h"
  #include "pathspec.h"
+ #include "dir.h"
  
  /*
   * Default to not allowing changes to the list of files. The
@@@ -274,32 -275,36 +275,32 @@@ static void chmod_path(int flip, const 
        die("git update-index: cannot chmod %cx '%s'", flip, path);
  }
  
 -static void update_one(const char *path, const char *prefix, int prefix_length)
 +static void update_one(const char *path)
  {
 -      const char *p = prefix_path(prefix, prefix_length, path);
 -      if (!verify_path(p)) {
 +      if (!verify_path(path)) {
                fprintf(stderr, "Ignoring path %s\n", path);
 -              goto free_return;
 +              return;
        }
        if (mark_valid_only) {
 -              if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))
 +              if (mark_ce_flags(path, CE_VALID, mark_valid_only == MARK_FLAG))
                        die("Unable to mark file %s", path);
 -              goto free_return;
 +              return;
        }
        if (mark_skip_worktree_only) {
 -              if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
 +              if (mark_ce_flags(path, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
                        die("Unable to mark file %s", path);
 -              goto free_return;
 +              return;
        }
  
        if (force_remove) {
 -              if (remove_file_from_cache(p))
 +              if (remove_file_from_cache(path))
                        die("git update-index: unable to remove %s", path);
                report("remove '%s'", path);
 -              goto free_return;
 +              return;
        }
 -      if (process_path(p))
 +      if (process_path(path))
                die("Unable to process path %s", path);
        report("add '%s'", path);
 - free_return:
 -      if (p < path || p > path + strlen(path))
 -              free((char *)p);
  }
  
  static void read_index_info(int line_termination)
@@@ -559,9 -564,8 +560,9 @@@ static int do_reupdate(int ac, const ch
                const struct cache_entry *ce = active_cache[pos];
                struct cache_entry *old = NULL;
                int save_nr;
 +              char *path;
  
-               if (ce_stage(ce) || !ce_path_match(ce, &pathspec))
+               if (ce_stage(ce) || !ce_path_match(ce, &pathspec, NULL))
                        continue;
                if (has_head)
                        old = read_one_ent(NULL, head_sha1,
                 * or worse yet 'allow_replace', active_nr may decrease.
                 */
                save_nr = active_nr;
 -              update_one(ce->name + prefix_length, prefix, prefix_length);
 +              path = xstrdup(ce->name);
 +              update_one(path);
 +              free(path);
                if (save_nr != active_nr)
                        goto redo;
        }
@@@ -835,10 -837,11 +836,10 @@@ int cmd_update_index(int argc, const ch
  
                        setup_work_tree();
                        p = prefix_path(prefix, prefix_length, path);
 -                      update_one(p, NULL, 0);
 +                      update_one(p);
                        if (set_executable_bit)
                                chmod_path(set_executable_bit, p);
 -                      if (p < path || p > path + strlen(path))
 -                              free((char *)p);
 +                      free((char *)p);
                        ctx.argc--;
                        ctx.argv++;
                        break;
                                strbuf_swap(&buf, &nbuf);
                        }
                        p = prefix_path(prefix, prefix_length, buf.buf);
 -                      update_one(p, NULL, 0);
 +                      update_one(p);
                        if (set_executable_bit)
                                chmod_path(set_executable_bit, p);
 -                      if (p < buf.buf || p > buf.buf + buf.len)
 -                              free((char *)p);
 +                      free((char *)p);
                }
                strbuf_release(&nbuf);
                strbuf_release(&buf);
diff --combined cache.h
index 4007aa84508243e78c968871b55c31434f020c48,aa6dbf6956b5a06733b8a35f8d45c3463ceac776..5994f7a2034268ce4f69afc5e99aafe7586ccef7
+++ b/cache.h
@@@ -3,7 -3,7 +3,7 @@@
  
  #include "git-compat-util.h"
  #include "strbuf.h"
 -#include "hash.h"
 +#include "hashmap.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
@@@ -130,12 -130,12 +130,12 @@@ struct stat_data 
  };
  
  struct cache_entry {
 +      struct hashmap_entry ent;
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned char sha1[20];
 -      struct cache_entry *next;
        char name[FLEX_ARRAY]; /* more */
  };
  
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 -#define CE_UNHASHED          (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -194,18 -195,17 +194,18 @@@ struct pathspec
   * Copy the sha1 and stat state of a cache entry from one to
   * another. But we never change the name, or the hash state!
   */
 -#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
  static inline void copy_cache_entry(struct cache_entry *dst,
                                    const struct cache_entry *src)
  {
 -      unsigned int state = dst->ce_flags & CE_STATE_MASK;
 +      unsigned int state = dst->ce_flags & CE_HASHED;
  
        /* Don't copy hash chain and name */
 -      memcpy(dst, src, offsetof(struct cache_entry, next));
 +      memcpy(&dst->ce_stat_data, &src->ce_stat_data,
 +                      offsetof(struct cache_entry, name) -
 +                      offsetof(struct cache_entry, ce_stat_data));
  
        /* Restore the hash state */
 -      dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 +      dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
  }
  
  static inline unsigned create_ce_flags(unsigned stage)
@@@ -277,8 -277,8 +277,8 @@@ struct index_state 
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
 -      struct hash_table name_hash;
 -      struct hash_table dir_hash;
 +      struct hashmap name_hash;
 +      struct hashmap dir_hash;
  };
  
  extern struct index_state the_index;
@@@ -316,6 -316,7 +316,6 @@@ extern void free_name_hash(struct index
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
  #define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
 -#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
  #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
  #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
@@@ -466,6 -467,7 +466,6 @@@ extern int unmerged_index(const struct 
  extern int verify_path(const char *path);
  extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 -extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
@@@ -485,7 -487,7 +485,7 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IMPLICIT_DOT 32     /* internal to "git add -u/-A" */
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
 -extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 +extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern int index_name_is_other(const struct index_state *, const char *, int);
  extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
  #define CE_MATCH_RACY_IS_DIRTY                02
  /* do stat comparison even if CE_SKIP_WORKTREE is true */
  #define CE_MATCH_IGNORE_SKIP_WORKTREE 04
 +/* ignore non-existent files during stat update  */
 +#define CE_MATCH_IGNORE_MISSING               0x08
 +/* enable stat refresh */
 +#define CE_MATCH_REFRESH              0x10
  extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
- extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
diff --combined read-cache.c
index 71f64f3a6eee9ccb2abd9d78f42e03a810bb560b,23eb2513c139fa1f775040fe1a5ffd2558a3c418..29c1047eeb7ce69d6a196ec80c23f66472142273
@@@ -15,8 -15,7 +15,8 @@@
  #include "strbuf.h"
  #include "varint.h"
  
 -static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
 +                                             unsigned int options);
  
  /* Mask for the name length in ce_flags in the on-disk index */
  
@@@ -48,7 -47,6 +48,7 @@@ static void replace_index_entry(struct 
        struct cache_entry *old = istate->cache[nr];
  
        remove_name_hash(istate, old);
 +      free(old);
        set_index_entry(istate, nr, ce);
        istate->cache_changed = 1;
  }
@@@ -60,7 -58,7 +60,7 @@@ void rename_index_entry_at(struct index
  
        new = xmalloc(cache_entry_size(namelen));
        copy_cache_entry(new, old);
 -      new->ce_flags &= ~CE_STATE_MASK;
 +      new->ce_flags &= ~CE_HASHED;
        new->ce_namelen = namelen;
        memcpy(new->name, new_name, namelen + 1);
  
@@@ -480,7 -478,6 +480,7 @@@ int remove_index_entry_at(struct index_
  
        record_resolve_undo(istate, ce);
        remove_name_hash(istate, ce);
 +      free(ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
@@@ -502,10 -499,8 +502,10 @@@ void remove_marked_cache_entries(struc
        unsigned int i, j;
  
        for (i = j = 0; i < istate->cache_nr; i++) {
 -              if (ce_array[i]->ce_flags & CE_REMOVE)
 +              if (ce_array[i]->ce_flags & CE_REMOVE) {
                        remove_name_hash(istate, ce_array[i]);
 +                      free(ce_array[i]);
 +              }
                else
                        ce_array[j++] = ce_array[i];
        }
@@@ -701,7 -696,7 +701,7 @@@ int add_file_to_index(struct index_stat
  
  struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage,
 -              int refresh)
 +              unsigned int refresh_options)
  {
        int size, len;
        struct cache_entry *ce;
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
  
 -      if (refresh)
 -              return refresh_cache_entry(ce, 0);
 -
 -      return ce;
 +      return refresh_cache_entry(ce, refresh_options);
  }
  
  int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
        return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
  }
  
- int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec)
- {
-       return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL);
- }
  /*
   * We fundamentally don't like some paths: we don't want
   * dot or dot-dot anywhere, and for obvious reasons don't
@@@ -1031,12 -1024,10 +1026,12 @@@ static struct cache_entry *refresh_cach
        struct stat st;
        struct cache_entry *updated;
        int changed, size;
 +      int refresh = options & CE_MATCH_REFRESH;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
 +      int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
  
 -      if (ce_uptodate(ce))
 +      if (!refresh || ce_uptodate(ce))
                return ce;
  
        /*
        }
  
        if (lstat(ce->name, &st) < 0) {
 +              if (ignore_missing && errno == ENOENT)
 +                      return ce;
                if (err)
                        *err = errno;
                return NULL;
@@@ -1133,9 -1122,7 +1128,9 @@@ int refresh_index(struct index_state *i
        int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
        int first = 1;
        int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
 -      unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
 +      unsigned int options = (CE_MATCH_REFRESH |
 +                              (really ? CE_MATCH_IGNORE_VALID : 0) |
 +                              (not_new ? CE_MATCH_IGNORE_MISSING : 0));
        const char *modified_fmt;
        const char *deleted_fmt;
        const char *typechange_fmt;
                if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
                        continue;
  
-               if (pathspec &&
-                   !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen))
+               if (pathspec && !ce_path_match(ce, pathspec, seen))
                        filtered = 1;
  
                if (ce_stage(ce)) {
                if (!new) {
                        const char *fmt;
  
 -                      if (not_new && cache_errno == ENOENT)
 -                              continue;
                        if (really && cache_errno == EINVAL) {
                                /* If we are doing --really-refresh that
                                 * means the index is not valid anymore.
        return has_errors;
  }
  
 -static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
 +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
 +                                             unsigned int options)
  {
 -      return refresh_cache_ent(&the_index, ce, really, NULL, NULL);
 +      return refresh_cache_ent(&the_index, ce, options, NULL, NULL);
  }
  
  
@@@ -1901,7 -1888,7 +1895,7 @@@ int read_index_unmerged(struct index_st
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
 -                                   ce->name);
 +                                   new_ce->name);
                i = index_name_pos(istate, new_ce->name, len);
        }
        return unmerged;
diff --combined resolve-undo.c
index 49ebaaf8d8b269b374ad3ccbfc57acccd83fe006,67d1543141bf33b7a44e7f948e9750d35ebf420b..44c697c36d0b406330aeb084db1d6a4be715d6c6
@@@ -119,7 -119,6 +119,7 @@@ int unmerge_index_entry_at(struct index
        struct string_list_item *item;
        struct resolve_undo_info *ru;
        int i, err = 0, matched;
 +      char *name;
  
        if (!istate->resolve_undo)
                return pos;
        if (!ru)
                return pos;
        matched = ce->ce_flags & CE_MATCHED;
 +      name = xstrdup(ce->name);
        remove_index_entry_at(istate, pos);
        for (i = 0; i < 3; i++) {
                struct cache_entry *nce;
                if (!ru->mode[i])
                        continue;
                nce = make_cache_entry(ru->mode[i], ru->sha1[i],
 -                                     ce->name, i + 1, 0);
 +                                     name, i + 1, 0);
                if (matched)
                        nce->ce_flags |= CE_MATCHED;
                if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
                        err = 1;
 -                      error("cannot unmerge '%s'", ce->name);
 +                      error("cannot unmerge '%s'", name);
                }
        }
 +      free(name);
        if (err)
                return pos;
        free(ru);
@@@ -185,7 -182,7 +185,7 @@@ void unmerge_index(struct index_state *
  
        for (i = 0; i < istate->cache_nr; i++) {
                const struct cache_entry *ce = istate->cache[i];
-               if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+               if (!ce_path_match(ce, pathspec, NULL))
                        continue;
                i = unmerge_index_entry_at(istate, i);
        }
diff --combined t/t4010-diff-pathspec.sh
index 9f5659f7fe4df23d805e6c38264a1ebf38fc2538,d30ff34be7ebaf01ef22ce63cc2d1beeccf6a057..2bb973655bf043cc43292764ffd68becda25aa2e
@@@ -127,17 -127,10 +127,23 @@@ test_expect_success 'diff-tree ignores 
        test_cmp expect actual
  '
  
 +test_expect_success 'diff multiple wildcard pathspecs' '
 +      mkdir path2 &&
 +      echo rezrov >path2/file1 &&
 +      git update-index --add path2/file1 &&
 +      tree3=`git write-tree` &&
 +      git diff --name-only $tree $tree3 -- "path2*1" "path1*1" >actual &&
 +      cat <<-\EOF >expect &&
 +      path1/file1
 +      path2/file1
 +      EOF
 +      test_cmp expect actual
 +'
 +
+ test_expect_success 'diff-cache ignores trailing slash on submodule path' '
+       git diff --name-only HEAD^ submod >expect &&
+       git diff --name-only HEAD^ submod/ >actual &&
+       test_cmp expect actual
+ '
  test_done