Updated based on comments from the list, including three new patches:

 - 16/22 allows to ignore untracked cache without destroying it (for
   comparison and verification)
 - 21/22 and 22/22 add some protection against filesystem or operating
   system changes

Also fix 'update-index --untracked-cache' essentially merging the
split index back because I set wrong update flag.

This series is also available on github [1] but you will have to
ignore the few top debugging patches first. Diff against the version
on 'pu' below.

[1] https://github.com/pclouds/git.git untracked-cache

diff --git a/builtin/update-index.c b/builtin/update-index.c
index c1c18db..f23ec83 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -115,6 +115,7 @@ static int test_if_untracked_cache_is_supported(void)
        fd = create_file("dir-mtime-test/newfile");
        xstat("dir-mtime-test", &st);
        if (!match_stat_data(&base, &st)) {
+               close(fd);
                fputc('\n', stderr);
                fprintf_ln(stderr,_("directory stat info does not "
                                    "change after adding a new file"));
@@ -127,6 +128,7 @@ static int test_if_untracked_cache_is_supported(void)
        xmkdir("dir-mtime-test/new-dir");
        xstat("dir-mtime-test", &st);
        if (!match_stat_data(&base, &st)) {
+               close(fd);
                fputc('\n', stderr);
                fprintf_ln(stderr, _("directory stat info does not change "
                                     "after adding a new directory"));
@@ -1094,10 +1096,10 @@ int cmd_update_index(int argc, const char **argv, const 
char *prefix)
                /* should be the same flags used by git-status */
                uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | 
DIR_HIDE_EMPTY_DIRECTORIES;
                the_index.untracked = uc;
-               the_index.cache_changed |= SOMETHING_CHANGED;
+               the_index.cache_changed |= UNTRACKED_CHANGED;
        } else if (!untracked_cache && the_index.untracked) {
                the_index.untracked = NULL;
-               the_index.cache_changed |= SOMETHING_CHANGED;
+               the_index.cache_changed |= UNTRACKED_CHANGED;
        }
 
        if (active_cache_changed) {
diff --git a/compat/mingw.c b/compat/mingw.c
index c5c37e5..b817678 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2128,3 +2128,14 @@ void mingw_startup()
        /* initialize Unicode console */
        winansi_init();
 }
+
+int uname(struct utsname *buf)
+{
+       DWORD v = GetVersion();
+       memset(buf, 0, sizeof(*buf));
+       sprintf(buf->sysname, "Windows");
+       sprintf(buf->release, "%u.%u", v & 0xff, (v >> 8) & 0xff);
+       /* assuming NT variants only.. */
+       sprintf(buf->version, "%u", (v >> 16) & 0x7fff);
+       return 0;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index df0e320..d00ba7a 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -77,6 +77,14 @@ struct itimerval {
 };
 #define ITIMER_REAL 0
 
+struct utsname {
+       char sysname[16];
+       char nodename[1];
+       char release[16];
+       char version[16];
+       char machine[1];
+};
+
 /*
  * sanitize preprocessor namespace polluted by Windows headers defining
  * macros which collide with git local versions
@@ -166,6 +174,7 @@ struct passwd *getpwuid(uid_t uid);
 int setitimer(int type, struct itimerval *in, struct itimerval *out);
 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
 int link(const char *oldpath, const char *newpath);
+int uname(struct utsname *buf);
 
 /*
  * replacements of existing functions
diff --git a/dir.c b/dir.c
index 0ae2188..2324c52 100644
--- a/dir.c
+++ b/dir.c
@@ -482,7 +482,7 @@ void add_exclude(const char *string, const char *base,
 }
 
 static void *read_skip_worktree_file_from_index(const char *path, size_t *size,
-                                               struct sha1_stat *ss)
+                                               struct sha1_stat *sha1_stat)
 {
        int pos, len;
        unsigned long sz;
@@ -501,9 +501,9 @@ static void *read_skip_worktree_file_from_index(const char 
*path, size_t *size,
                return NULL;
        }
        *size = xsize_t(sz);
-       if (ss) {
-               memset(&ss->stat, 0, sizeof(ss->stat));
-               hashcpy(ss->sha1, active_cache[pos]->sha1);
+       if (sha1_stat) {
+               memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
+               hashcpy(sha1_stat->sha1, active_cache[pos]->sha1);
        }
        return data;
 }
@@ -634,7 +634,7 @@ static void invalidate_directory(struct untracked_cache *uc,
  */
 static int add_excludes(const char *fname, const char *base, int baselen,
                        struct exclude_list *el, int check_index,
-                       struct sha1_stat *ss, int ss_valid)
+                       struct sha1_stat *sha1_stat)
 {
        struct stat st;
        int fd, i, lineno = 1;
@@ -648,7 +648,7 @@ static int add_excludes(const char *fname, const char 
*base, int baselen,
                if (0 <= fd)
                        close(fd);
                if (!check_index ||
-                   (buf = read_skip_worktree_file_from_index(fname, &size, 
ss)) == NULL)
+                   (buf = read_skip_worktree_file_from_index(fname, &size, 
sha1_stat)) == NULL)
                        return -1;
                if (size == 0) {
                        free(buf);
@@ -661,9 +661,10 @@ static int add_excludes(const char *fname, const char 
*base, int baselen,
        } else {
                size = xsize_t(st.st_size);
                if (size == 0) {
-                       if (ss) {
-                               fill_stat_data(&ss->stat, &st);
-                               hashcpy(ss->sha1, EMPTY_BLOB_SHA1_BIN);
+                       if (sha1_stat) {
+                               fill_stat_data(&sha1_stat->stat, &st);
+                               hashcpy(sha1_stat->sha1, EMPTY_BLOB_SHA1_BIN);
+                               sha1_stat->valid = 1;
                        }
                        close(fd);
                        return 0;
@@ -676,19 +677,20 @@ static int add_excludes(const char *fname, const char 
*base, int baselen,
                }
                buf[size++] = '\n';
                close(fd);
-               if (ss) {
+               if (sha1_stat) {
                        int pos;
-                       if (ss_valid &&
-                           !match_stat_data_racy(&the_index, &ss->stat, &st))
+                       if (sha1_stat->valid &&
+                           !match_stat_data_racy(&the_index, &sha1_stat->stat, 
&st))
                                ; /* no content change, ss->sha1 still good */
                        else if (check_index &&
                                 (pos = cache_name_pos(fname, strlen(fname))) 
>= 0 &&
                                 !ce_stage(active_cache[pos]) &&
                                 ce_uptodate(active_cache[pos]))
-                               hashcpy(ss->sha1, active_cache[pos]->sha1);
+                               hashcpy(sha1_stat->sha1, 
active_cache[pos]->sha1);
                        else
-                               hash_sha1_file(buf, size, "blob", ss->sha1);
-                       fill_stat_data(&ss->stat, &st);
+                               hash_sha1_file(buf, size, "blob", 
sha1_stat->sha1);
+                       fill_stat_data(&sha1_stat->stat, &st);
+                       sha1_stat->valid = 1;
                }
        }
 
@@ -712,7 +714,7 @@ int add_excludes_from_file_to_list(const char *fname, const 
char *base,
                                   int baselen, struct exclude_list *el,
                                   int check_index)
 {
-       return add_excludes(fname, base, baselen, el, check_index, NULL, 0);
+       return add_excludes(fname, base, baselen, el, check_index, NULL);
 }
 
 struct exclude_list *add_exclude_list(struct dir_struct *dir,
@@ -733,7 +735,7 @@ struct exclude_list *add_exclude_list(struct dir_struct 
*dir,
  * Used to set up core.excludesfile and .git/info/exclude lists.
  */
 static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
-                                    struct sha1_stat *ss, int ss_valid)
+                                    struct sha1_stat *sha1_stat)
 {
        struct exclude_list *el;
        /*
@@ -744,14 +746,14 @@ static void add_excludes_from_file_1(struct dir_struct 
*dir, const char *fname,
        if (!dir->untracked)
                dir->unmanaged_exclude_files++;
        el = add_exclude_list(dir, EXC_FILE, fname);
-       if (add_excludes(fname, "", 0, el, 0, ss, ss_valid) < 0)
+       if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0)
                die("cannot use %s as an exclude file", fname);
 }
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
        dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
-       add_excludes_from_file_1(dir, fname, NULL, 0);
+       add_excludes_from_file_1(dir, fname, NULL);
 }
 
 int match_basename(const char *basename, int basenamelen,
@@ -974,7 +976,7 @@ static void prep_exclude(struct dir_struct *dir, const char 
*base, int baselen)
        while (current < baselen) {
                struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
                const char *cp;
-               struct sha1_stat ss;
+               struct sha1_stat sha1_stat;
 
                if (current < 0) {
                        cp = base;
@@ -1015,7 +1017,8 @@ static void prep_exclude(struct dir_struct *dir, const 
char *base, int baselen)
                }
 
                /* Try to read per-directory file */
-               hashclr(ss.sha1);
+               hashclr(sha1_stat.sha1);
+               sha1_stat.valid = 0;
                if (dir->exclude_per_dir &&
                    /*
                     * If we know that no files have been added in
@@ -1044,7 +1047,7 @@ static void prep_exclude(struct dir_struct *dir, const 
char *base, int baselen)
                        strbuf_addstr(&sb, dir->exclude_per_dir);
                        el->src = strbuf_detach(&sb, NULL);
                        add_excludes(el->src, el->src, stk->baselen, el, 1,
-                                    untracked ? &ss : NULL, 0);
+                                    untracked ? &sha1_stat : NULL);
                }
                /*
                 * NEEDSWORK: when untracked cache is enabled, prep_exclude()
@@ -1060,9 +1063,10 @@ static void prep_exclude(struct dir_struct *dir, const 
char *base, int baselen)
                 * last_exclude_matching(). Be careful about ignore rule
                 * order, though, if you do that.
                 */
-               if (untracked && hashcmp(ss.sha1, untracked->exclude_sha1)) {
+               if (untracked &&
+                   hashcmp(sha1_stat.sha1, untracked->exclude_sha1)) {
                        invalidate_gitignore(dir->untracked, untracked);
-                       hashcpy(untracked->exclude_sha1, ss.sha1);
+                       hashcpy(untracked->exclude_sha1, sha1_stat.sha1);
                }
                dir->exclude_stack = stk;
                current = stk->baselen;
@@ -1457,12 +1461,11 @@ static enum path_treatment treat_path_fast(struct 
dir_struct *dir,
                                           int baselen,
                                           const struct path_simplify *simplify)
 {
+       strbuf_setlen(path, baselen);
        if (!cdir->ucd) {
-               strbuf_setlen(path, baselen);
                strbuf_addstr(path, cdir->file);
                return path_untracked;
        }
-       strbuf_setlen(path, baselen);
        strbuf_addstr(path, cdir->ucd->name);
        /* treat_one_path() does this before it calls treat_directory() */
        if (path->buf[path->len - 1] != '/')
@@ -1474,7 +1477,6 @@ static enum path_treatment treat_path_fast(struct 
dir_struct *dir,
                 * with check_only set.
                 */
                return read_directory_recursive(dir, path->buf, path->len,
-
                                                cdir->ucd, 1, simplify);
        /*
         * We get path_recurse in the first run when
@@ -1582,7 +1584,7 @@ static int open_cached_dir(struct cached_dir *cdir,
        return 0;
 }
 
-int read_cached_dir(struct cached_dir *cdir)
+static int read_cached_dir(struct cached_dir *cdir)
 {
        if (cdir->fdir) {
                cdir->de = readdir(cdir->fdir);
@@ -1796,7 +1798,7 @@ static struct untracked_cache_dir 
*validate_untracked_cache(struct dir_struct *d
        struct untracked_cache_dir *root;
        int i;
 
-       if (!dir->untracked)
+       if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
                return NULL;
 
        /*
@@ -2101,30 +2103,12 @@ void setup_standard_excludes(struct dir_struct *dir)
                home_config_paths(NULL, &xdg_path, "ignore");
                excludes_file = xdg_path;
        }
-       if (!access_or_warn(path, R_OK, 0)) {
-               struct sha1_stat *ss = NULL;
-               int ss_valid = 0;
-               if (dir->untracked) {
-                       ss = &dir->ss_info_exclude;
-                       if (dir->untracked->loaded) {
-                               *ss = dir->untracked->ss_info_exclude;
-                               ss_valid = 1;
-                       }
-               }
-               add_excludes_from_file_1(dir, path, ss, ss_valid);
-       }
-       if (excludes_file && !access_or_warn(excludes_file, R_OK, 0)) {
-               struct sha1_stat *ss = NULL;
-               int ss_valid = 0;
-               if (dir->untracked) {
-                       ss = &dir->ss_excludes_file;
-                       if (dir->untracked->loaded) {
-                               *ss = dir->untracked->ss_excludes_file;
-                               ss_valid = 1;
-                       }
-               }
-               add_excludes_from_file_1(dir, excludes_file, ss, ss_valid);
-       }
+       if (!access_or_warn(path, R_OK, 0))
+               add_excludes_from_file_1(dir, path,
+                                        dir->untracked ? &dir->ss_info_exclude 
: NULL);
+       if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
+               add_excludes_from_file_1(dir, excludes_file,
+                                        dir->untracked ? 
&dir->ss_excludes_file : NULL);
 }
 
 int remove_path(const char *name)
@@ -2243,10 +2227,22 @@ static void write_one_dir(struct strbuf *out, struct 
untracked_cache_dir *untrac
                        write_one_dir(out, untracked->dirs[i]);
 }
 
+static void get_ident_string(struct strbuf *sb)
+{
+       struct utsname uts;
+
+       if (uname(&uts))
+               die_errno(_("failed to get kernel name and information"));
+       strbuf_addf(sb, "Location %s, system %s %s %s", get_git_work_tree(),
+                   uts.sysname, uts.release, uts.version);
+}
+
 void write_untracked_extension(struct strbuf *out, struct untracked_cache 
*untracked)
 {
        struct ondisk_untracked_cache *ouc;
-       int len = 0;
+       struct strbuf sb = STRBUF_INIT;
+       unsigned char varbuf[16];
+       int len = 0, varint_len;
        if (untracked->exclude_per_dir)
                len = strlen(untracked->exclude_per_dir);
        ouc = xmalloc(sizeof(*ouc) + len);
@@ -2256,6 +2252,13 @@ void write_untracked_extension(struct strbuf *out, 
struct untracked_cache *untra
        hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
        ouc->dir_flags = htonl(untracked->dir_flags);
        memcpy(ouc->exclude_per_dir, untracked->exclude_per_dir, len + 1);
+
+       get_ident_string(&sb);
+       varint_len = encode_varint(sb.len + 1, varbuf);
+       strbuf_add(out, varbuf, varint_len);
+       strbuf_add(out, sb.buf, sb.len + 1);
+       strbuf_release(&sb);
+
        strbuf_add(out, ouc, sizeof(*ouc) + len);
        if (untracked->root)
                write_one_dir(out, untracked->root);
@@ -2360,28 +2363,55 @@ static int read_one_dir(struct untracked_cache_dir 
**untracked_,
        return data - data_;
 }
 
+static void load_sha1_stat(struct sha1_stat *sha1_stat,
+                          const struct stat_data *stat,
+                          const unsigned char *sha1)
+{
+       stat_data_from_disk(&sha1_stat->stat, stat);
+       hashcpy(sha1_stat->sha1, sha1);
+       sha1_stat->valid = 1;
+}
+
 struct untracked_cache *read_untracked_extension(const void *data, unsigned 
long sz)
 {
-       const struct ondisk_untracked_cache *ouc = data;
+       const struct ondisk_untracked_cache *ouc;
        struct untracked_cache *uc;
+       const unsigned char *next = data;
+       struct strbuf sb = STRBUF_INIT;
        int len;
 
+       len = decode_varint(&next);
+       if (sz <= (next - (const unsigned char *)data) + len ||
+           next[len - 1] != '\0')
+               return NULL;
+
+       get_ident_string(&sb);
+       if (strcmp(sb.buf, (const char *)next)) {
+               warning(_("system identification does not match, untracked 
cache disabled.\n"
+                         "Stored: %s\nCurrent: %s\n"),
+                       next, sb.buf);
+               strbuf_release(&sb);
+               return NULL;
+       }
+       strbuf_release(&sb);
+       ouc = (const struct ondisk_untracked_cache *)(next + len);
+       sz -= (const char *)ouc - (const char *)data;
+
        if (sz < sizeof(*ouc))
                return NULL;
 
        uc = xcalloc(1, sizeof(*uc));
-       stat_data_from_disk(&uc->ss_info_exclude.stat, &ouc->info_exclude_stat);
-       stat_data_from_disk(&uc->ss_excludes_file.stat, 
&ouc->excludes_file_stat);
-       hashcpy(uc->ss_info_exclude.sha1, ouc->info_exclude_sha1);
-       hashcpy(uc->ss_excludes_file.sha1, ouc->excludes_file_sha1);
+       load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
+                      ouc->info_exclude_sha1);
+       load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
+                      ouc->excludes_file_sha1);
        uc->dir_flags = get_be32(&ouc->dir_flags);
        uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
-       uc->loaded = 1;
        len = sizeof(*ouc) + strlen(ouc->exclude_per_dir);
        if (sz == len)
                return uc;
        if (sz > len &&
-           read_one_dir(&uc->root, (const unsigned char *)data + len,
+           read_one_dir(&uc->root, (const unsigned char *)ouc + len,
                         sz - len) == sz - len)
                return uc;
        free_untracked_cache(uc);
diff --git a/dir.h b/dir.h
index 8c29324..708cdd5 100644
--- a/dir.h
+++ b/dir.h
@@ -77,6 +77,7 @@ struct exclude_list_group {
 struct sha1_stat {
        struct stat_data stat;
        unsigned char sha1[20];
+       int valid;
 };
 
 /*
@@ -110,15 +111,15 @@ struct sha1_stat {
 struct untracked_cache_dir {
        struct untracked_cache_dir **dirs;
        char **untracked;
-       /* null SHA-1 means this directory does not have .gitignore */
-       unsigned char exclude_sha1[20];
        struct stat_data stat_data;
-       unsigned int recurse : 1;
+       unsigned int untracked_alloc, dirs_nr, dirs_alloc;
+       unsigned int untracked_nr;
        unsigned int check_only : 1;
        /* all data except 'dirs' in this struct are good */
        unsigned int valid : 1;
-       unsigned int untracked_nr : 29;
-       unsigned int untracked_alloc, dirs_nr, dirs_alloc;
+       unsigned int recurse : 1;
+       /* null SHA-1 means this directory does not have .gitignore */
+       unsigned char exclude_sha1[20];
        char name[1];
 };
 
@@ -137,7 +138,6 @@ struct untracked_cache {
        int gitignore_invalidated;
        int dir_invalidated;
        int dir_opened;
-       int loaded;
 };
 
 struct dir_struct {
diff --git a/git-compat-util.h b/git-compat-util.h
index f587749..6b1f259 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -125,6 +125,7 @@
 #else
 #include <poll.h>
 #endif
+#include <sys/utsname.h>
 
 #if defined(__MINGW32__)
 /* pull in Windows compatibility stuff */
diff --git a/test-dump-untracked-cache.c b/test-dump-untracked-cache.c
index 710441e..25d855d 100644
--- a/test-dump-untracked-cache.c
+++ b/test-dump-untracked-cache.c
@@ -44,6 +44,7 @@ int main(int ac, char **av)
 {
        struct untracked_cache *uc;
        struct strbuf base = STRBUF_INIT;
+       setup_git_directory();
        if (read_cache() < 0)
                die("unable to read index file");
        uc = the_index.untracked;
-- 
2.1.0.rc0.78.gc0d8480

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to