Code

Merge branch 'jc/gitignore-ends-with-slash'
authorJunio C Hamano <gitster@pobox.com>
Sun, 17 Feb 2008 01:57:06 +0000 (17:57 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 17 Feb 2008 01:57:06 +0000 (17:57 -0800)
* jc/gitignore-ends-with-slash:
  gitignore: lazily find dtype
  gitignore(5): Allow "foo/" in ignore list to match directory "foo"

1  2 
builtin-ls-files.c
cache.h
dir.c
unpack-trees.c

diff --combined builtin-ls-files.c
index d56e33e251036274dbe48e300fcebd8289624cbe,54cb2518dbbfed70dcbe1b5a2112f79c78bfd6a6..dc7eab89b34fed32dbb198a9aa9a7503fc162216
@@@ -189,7 -189,7 +189,7 @@@ static void show_ce_entry(const char *t
                return;
  
        if (tag && *tag && show_valid_bit &&
 -          (ce->ce_flags & htons(CE_VALID))) {
 +          (ce->ce_flags & CE_VALID)) {
                static char alttag[4];
                memcpy(alttag, tag, 3);
                if (isalpha(tag[0]))
        } else {
                printf("%s%06o %s %d\t",
                       tag,
 -                     ntohl(ce->ce_mode),
 +                     ce->ce_mode,
                       abbrev ? find_unique_abbrev(ce->sha1,abbrev)
                                : sha1_to_hex(ce->sha1),
                       ce_stage(ce));
@@@ -238,11 -238,12 +238,12 @@@ static void show_files(struct dir_struc
        if (show_cached | show_stage) {
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
-                       if (excluded(dir, ce->name) != dir->show_ignored)
+                       int dtype = ce_to_dtype(ce);
+                       if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
                                continue;
                        if (show_unmerged && !ce_stage(ce))
                                continue;
 -                      if (ce->ce_flags & htons(CE_UPDATE))
 +                      if (ce->ce_flags & CE_UPDATE)
                                continue;
                        show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
                }
                        struct cache_entry *ce = active_cache[i];
                        struct stat st;
                        int err;
-                       if (excluded(dir, ce->name) != dir->show_ignored)
+                       int dtype = ce_to_dtype(ce);
+                       if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
                                continue;
                        err = lstat(ce->name, &st);
                        if (show_deleted && err)
@@@ -350,7 -352,7 +352,7 @@@ void overlay_tree_on_cache(const char *
                struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
 -              ce->ce_flags |= htons(CE_STAGEMASK);
 +              ce->ce_flags |= CE_STAGEMASK;
        }
  
        if (prefix) {
                         */
                        if (last_stage0 &&
                            !strcmp(last_stage0->name, ce->name))
 -                              ce->ce_flags |= htons(CE_UPDATE);
 +                              ce->ce_flags |= CE_UPDATE;
                }
        }
  }
diff --combined cache.h
index efd31d8569c27ca18f2f7a7e599c115c8be9251a,552983011085cf9900db251b00a8fb7abd9ba12c..18fe8447f3bdff2a772716c9293e44e357178a4c
+++ b/cache.h
@@@ -3,7 -3,6 +3,7 @@@
  
  #include "git-compat-util.h"
  #include "strbuf.h"
 +#include "hash.h"
  
  #include SHA1_HEADER
  #include <zlib.h>
@@@ -95,104 -94,78 +95,116 @@@ struct cache_time 
   * We save the fields in big-endian order to allow using the
   * index file over NFS transparently.
   */
 +struct ondisk_cache_entry {
 +      struct cache_time ctime;
 +      struct cache_time mtime;
 +      unsigned int dev;
 +      unsigned int ino;
 +      unsigned int mode;
 +      unsigned int uid;
 +      unsigned int gid;
 +      unsigned int size;
 +      unsigned char sha1[20];
 +      unsigned short flags;
 +      char name[FLEX_ARRAY]; /* more */
 +};
 +
  struct cache_entry {
 -      struct cache_time ce_ctime;
 -      struct cache_time ce_mtime;
 +      struct cache_entry *next;
 +      unsigned int ce_ctime;
 +      unsigned int ce_mtime;
        unsigned int ce_dev;
        unsigned int ce_ino;
        unsigned int ce_mode;
        unsigned int ce_uid;
        unsigned int ce_gid;
        unsigned int ce_size;
 +      unsigned int ce_flags;
        unsigned char sha1[20];
 -      unsigned short ce_flags;
        char name[FLEX_ARRAY]; /* more */
  };
  
  #define CE_NAMEMASK  (0x0fff)
  #define CE_STAGEMASK (0x3000)
 -#define CE_UPDATE    (0x4000)
  #define CE_VALID     (0x8000)
  #define CE_STAGESHIFT 12
  
 -#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
 -#define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))
 +/* In-memory only */
 +#define CE_UPDATE    (0x10000)
 +#define CE_REMOVE    (0x20000)
 +#define CE_UPTODATE  (0x40000)
 +#define CE_UNHASHED  (0x80000)
 +
 +static inline unsigned create_ce_flags(size_t len, unsigned stage)
 +{
 +      if (len >= CE_NAMEMASK)
 +              len = CE_NAMEMASK;
 +      return (len | (stage << CE_STAGESHIFT));
 +}
 +
 +static inline size_t ce_namelen(const struct cache_entry *ce)
 +{
 +      size_t len = ce->ce_flags & CE_NAMEMASK;
 +      if (len < CE_NAMEMASK)
 +              return len;
 +      return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
 +}
 +
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 -#define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT)
 +#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
 +#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
 +#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 +#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
  static inline unsigned int create_ce_mode(unsigned int mode)
  {
        if (S_ISLNK(mode))
 -              return htonl(S_IFLNK);
 +              return S_IFLNK;
        if (S_ISDIR(mode) || S_ISGITLINK(mode))
 -              return htonl(S_IFGITLINK);
 -      return htonl(S_IFREG | ce_permissions(mode));
 +              return S_IFGITLINK;
 +      return S_IFREG | ce_permissions(mode);
  }
  static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
  {
        extern int trust_executable_bit, has_symlinks;
        if (!has_symlinks && S_ISREG(mode) &&
 -          ce && S_ISLNK(ntohl(ce->ce_mode)))
 +          ce && S_ISLNK(ce->ce_mode))
                return ce->ce_mode;
        if (!trust_executable_bit && S_ISREG(mode)) {
 -              if (ce && S_ISREG(ntohl(ce->ce_mode)))
 +              if (ce && S_ISREG(ce->ce_mode))
                        return ce->ce_mode;
                return create_ce_mode(0666);
        }
        return create_ce_mode(mode);
  }
+ static inline int ce_to_dtype(const struct cache_entry *ce)
+ {
+       unsigned ce_mode = ntohl(ce->ce_mode);
+       if (S_ISREG(ce_mode))
+               return DT_REG;
+       else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
+               return DT_DIR;
+       else if (S_ISLNK(ce_mode))
+               return DT_LNK;
+       else
+               return DT_UNKNOWN;
+ }
  #define canon_mode(mode) \
        (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
        S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
  
  #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
 +#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
  
  struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct cache_tree *cache_tree;
        time_t timestamp;
 -      void *mmap;
 -      size_t mmap_size;
 +      void *alloc;
 +      unsigned name_hash_initialized : 1;
 +      struct hash_table name_hash;
  };
  
  extern struct index_state the_index;
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
 +#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
  #endif
  
  enum object_type {
@@@ -303,7 -275,6 +315,7 @@@ extern int read_index_from(struct index
  extern int write_index(struct index_state *, int newfd);
  extern int discard_index(struct index_state *);
  extern int verify_path(const char *path);
 +extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
  extern int index_name_pos(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 */
@@@ -625,16 -596,11 +637,16 @@@ extern int git_parse_ulong(const char *
  extern int git_config_int(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
  extern int git_config_bool(const char *, const char *);
 +extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_set(const char *, const char *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value);
 +extern int git_env_bool(const char *, int);
 +extern int git_config_system(void);
 +extern int git_config_global(void);
 +extern int config_error_nonbool(const char *);
  
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
@@@ -654,12 -620,12 +666,12 @@@ extern int write_or_whine_pipe(int fd, 
  
  /* pager.c */
  extern void setup_pager(void);
 -extern char *pager_program;
 +extern const char *pager_program;
  extern int pager_in_use(void);
  extern int pager_use_color;
  
 -extern char *editor_program;
 -extern char *excludes_file;
 +extern const char *editor_program;
 +extern const char *excludes_file;
  
  /* base85 */
  int decode_85(char *dst, const char *line, int linelen);
diff --combined dir.c
index 6543105b9622212430a9e5ed131a81074e019d9a,292639b1562d8995025557341e3dc3215cef7b96..1f507daff2c278a70da768e3754a68891b946973
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -17,6 -17,7 +17,7 @@@ struct path_simplify 
  static int read_directory_recursive(struct dir_struct *dir,
        const char *path, const char *base, int baselen,
        int check_only, const struct path_simplify *simplify);
+ static int get_dtype(struct dirent *de, const char *path);
  
  int common_prefix(const char **pathspec)
  {
@@@ -126,18 -127,34 +127,34 @@@ static int no_wildcard(const char *stri
  void add_exclude(const char *string, const char *base,
                 int baselen, struct exclude_list *which)
  {
-       struct exclude *x = xmalloc(sizeof (*x));
+       struct exclude *x;
+       size_t len;
+       int to_exclude = 1;
+       int flags = 0;
  
-       x->to_exclude = 1;
        if (*string == '!') {
-               x->to_exclude = 0;
+               to_exclude = 0;
                string++;
        }
-       x->pattern = string;
+       len = strlen(string);
+       if (len && string[len - 1] == '/') {
+               char *s;
+               x = xmalloc(sizeof(*x) + len);
+               s = (char*)(x+1);
+               memcpy(s, string, len - 1);
+               s[len - 1] = '\0';
+               string = s;
+               x->pattern = s;
+               flags = EXC_FLAG_MUSTBEDIR;
+       } else {
+               x = xmalloc(sizeof(*x));
+               x->pattern = string;
+       }
+       x->to_exclude = to_exclude;
        x->patternlen = strlen(string);
        x->base = base;
        x->baselen = baselen;
-       x->flags = 0;
+       x->flags = flags;
        if (!strchr(string, '/'))
                x->flags |= EXC_FLAG_NODIR;
        if (no_wildcard(string))
@@@ -261,7 -278,7 +278,7 @@@ static void prep_exclude(struct dir_str
   * Return 1 for exclude, 0 for include and -1 for undecided.
   */
  static int excluded_1(const char *pathname,
-                     int pathlen, const char *basename,
+                     int pathlen, const char *basename, int *dtype,
                      struct exclude_list *el)
  {
        int i;
                        const char *exclude = x->pattern;
                        int to_exclude = x->to_exclude;
  
+                       if (x->flags & EXC_FLAG_MUSTBEDIR) {
+                               if (*dtype == DT_UNKNOWN)
+                                       *dtype = get_dtype(NULL, pathname);
+                               if (*dtype != DT_DIR)
+                                       continue;
+                       }
                        if (x->flags & EXC_FLAG_NODIR) {
                                /* match basename */
                                if (x->flags & EXC_FLAG_NOWILDCARD) {
        return -1; /* undecided */
  }
  
- int excluded(struct dir_struct *dir, const char *pathname)
+ int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
  {
        int pathlen = strlen(pathname);
        int st;
  
        prep_exclude(dir, pathname, basename-pathname);
        for (st = EXC_CMDL; st <= EXC_FILE; st++) {
-               switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
+               switch (excluded_1(pathname, pathlen, basename,
+                                  dtype_p, &dir->exclude_list[st])) {
                case 0:
                        return 0;
                case 1:
@@@ -346,7 -371,7 +371,7 @@@ static struct dir_entry *dir_entry_new(
  
  struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
  {
 -      if (cache_name_pos(pathname, len) >= 0)
 +      if (cache_name_exists(pathname, len))
                return NULL;
  
        ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
@@@ -391,7 -416,7 +416,7 @@@ static enum exist_status directory_exis
                        break;
                if (endchar == '/')
                        return index_directory;
 -              if (!endchar && S_ISGITLINK(ntohl(ce->ce_mode)))
 +              if (!endchar && S_ISGITLINK(ce->ce_mode))
                        return index_gitdir;
        }
        return index_nonexistent;
@@@ -508,7 -533,7 +533,7 @@@ static int in_pathspec(const char *path
  
  static int get_dtype(struct dirent *de, const char *path)
  {
-       int dtype = DTYPE(de);
+       int dtype = de ? DTYPE(de) : DT_UNKNOWN;
        struct stat st;
  
        if (dtype != DT_UNKNOWN)
@@@ -560,7 -585,8 +585,8 @@@ static int read_directory_recursive(str
                        if (simplify_away(fullname, baselen + len, simplify))
                                continue;
  
-                       exclude = excluded(dir, fullname);
+                       dtype = DTYPE(de);
+                       exclude = excluded(dir, fullname, &dtype);
                        if (exclude && dir->collect_ignored
                            && in_pathspec(fullname, baselen + len, simplify))
                                dir_add_ignored(dir, fullname, baselen + len);
                        if (exclude && !dir->show_ignored)
                                continue;
  
-                       dtype = get_dtype(de, fullname);
+                       if (dtype == DT_UNKNOWN)
+                               dtype = get_dtype(de, fullname);
  
                        /*
                         * Do we want to see just the ignored files?
diff --combined unpack-trees.c
index ff46fd62fdceeefc2ebd98ae1c9483964cb37bd5,29848e926c9a17b2aaf54b924c7176346c5d3d02..ec558f9005fab372f9bf62d8c3df9ea34f5222bb
@@@ -289,6 -289,7 +289,6 @@@ static struct checkout state
  static void check_updates(struct cache_entry **src, int nr,
                        struct unpack_trees_options *o)
  {
 -      unsigned short mask = htons(CE_UPDATE);
        unsigned cnt = 0, total = 0;
        struct progress *progress = NULL;
        char last_symlink[PATH_MAX];
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < nr; cnt++) {
                        struct cache_entry *ce = src[cnt];
 -                      if (!ce->ce_mode || ce->ce_flags & mask)
 +                      if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
                                total++;
                }
  
        while (nr--) {
                struct cache_entry *ce = *src++;
  
 -              if (!ce->ce_mode || ce->ce_flags & mask)
 +              if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
                        display_progress(progress, ++cnt);
 -              if (!ce->ce_mode) {
 +              if (ce->ce_flags & CE_REMOVE) {
                        if (o->update)
                                unlink_entry(ce->name, last_symlink);
                        continue;
                }
 -              if (ce->ce_flags & mask) {
 -                      ce->ce_flags &= ~mask;
 +              if (ce->ce_flags & CE_UPDATE) {
 +                      ce->ce_flags &= ~CE_UPDATE;
                        if (o->update) {
                                checkout_entry(ce, &state, NULL);
                                *last_symlink = '\0';
@@@ -407,7 -408,7 +407,7 @@@ static void verify_uptodate(struct cach
                 * submodules that are marked to be automatically
                 * checked out.
                 */
 -              if (S_ISGITLINK(ntohl(ce->ce_mode)))
 +              if (S_ISGITLINK(ce->ce_mode))
                        return;
                errno = 0;
        }
@@@ -449,7 -450,7 +449,7 @@@ static int verify_clean_subdirectory(st
        int cnt = 0;
        unsigned char sha1[20];
  
 -      if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
 +      if (S_ISGITLINK(ce->ce_mode) &&
            resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
                /* If we are not going to update the submodule, then
                 * we don't care.
                 */
                if (!ce_stage(ce)) {
                        verify_uptodate(ce, o);
 -                      ce->ce_mode = 0;
 +                      ce->ce_flags |= CE_REMOVE;
                }
                cnt++;
        }
@@@ -521,8 -522,9 +521,9 @@@ static void verify_absent(struct cache_
  
        if (!lstat(ce->name, &st)) {
                int cnt;
+               int dtype = ce_to_dtype(ce);
  
-               if (o->dir && excluded(o->dir, ce->name))
+               if (o->dir && excluded(o->dir, ce->name, &dtype))
                        /*
                         * ce->name is explicitly excluded, so it is Ok to
                         * overwrite it.
                cnt = cache_name_pos(ce->name, strlen(ce->name));
                if (0 <= cnt) {
                        struct cache_entry *ce = active_cache[cnt];
 -                      if (!ce_stage(ce) && !ce->ce_mode)
 +                      if (ce->ce_flags & CE_REMOVE)
                                return;
                }
  
  static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                struct unpack_trees_options *o)
  {
 -      merge->ce_flags |= htons(CE_UPDATE);
 +      merge->ce_flags |= CE_UPDATE;
        if (old) {
                /*
                 * See if we can re-use the old CE directly?
                invalidate_ce_path(merge);
        }
  
 -      merge->ce_flags &= ~htons(CE_STAGEMASK);
 +      merge->ce_flags &= ~CE_STAGEMASK;
        add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
        return 1;
  }
@@@ -612,7 -614,7 +613,7 @@@ static int deleted_entry(struct cache_e
                verify_uptodate(old, o);
        else
                verify_absent(ce, "removed", o);
 -      ce->ce_mode = 0;
 +      ce->ce_flags |= CE_REMOVE;
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
        invalidate_ce_path(ce);
        return 1;
@@@ -633,7 -635,7 +634,7 @@@ static void show_stage_entry(FILE *o
        else
                fprintf(o, "%s%06o %s %d\t%s\n",
                        label,
 -                      ntohl(ce->ce_mode),
 +                      ce->ce_mode,
                        sha1_to_hex(ce->sha1),
                        ce_stage(ce),
                        ce->name);
@@@ -919,7 -921,7 +920,7 @@@ int oneway_merge(struct cache_entry **s
                        struct stat st;
                        if (lstat(old->name, &st) ||
                            ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))
 -                              old->ce_flags |= htons(CE_UPDATE);
 +                              old->ce_flags |= CE_UPDATE;
                }
                return keep_entry(old, o);
        }