untracked cache: guard and disable on system changes
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Sun, 8 Mar 2015 10:12:46 +0000 (17:12 +0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 12 Mar 2015 20:45:18 +0000 (13:45 -0700)
If the user enables untracked cache, then

- move worktree to an unsupported filesystem
- or simply upgrade OS
- or move the whole (portable) disk from one machine to another
- or access a shared fs from another machine

there's no guarantee that untracked cache can still function properly.
Record the worktree location and OS footprint in the cache. If it
changes, err on the safe side and disable the cache. The user can
'update-index --untracked-cache' again to make sure all conditions are
met.

This adds a new requirement that setup_git_directory* must be called
before read_cache() because we need worktree location by then, or the
cache is dropped.

This change does not cover all bases, you can fool it if you try
hard. The point is to stop accidents.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: brian m. carlson <sandals@crustytoothpaste.net>
Helped-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/technical/index-format.txt
builtin/update-index.c
dir.c
dir.h
git-compat-util.h
test-dump-untracked-cache.c
index db59a13600382004bbfc93d1b882afd0d3f47d61..b7093af8b23e6a83741b81678e7aae0c5ff7c88a 100644 (file)
@@ -242,6 +242,10 @@ Git index format
 
   The extension starts with
 
+  - A sequence of NUL-terminated strings, preceded by the size of the
+    sequence in variable width encoding. Each string describes the
+    environment where the cache can be used.
+
   - Stat data of $GIT_DIR/info/exclude. See "Index entry" section from
     ctime field until "file size".
 
index c6951ad3cee7721c82c3a2db076dfd043474396a..790a6aa9db3ce93036e7cb533abe47bee47ce580 100644 (file)
@@ -1104,7 +1104,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                the_index.split_index = NULL;
                the_index.cache_changed |= SOMETHING_CHANGED;
        }
-       if (untracked_cache > 0 && !the_index.untracked) {
+       if (untracked_cache > 0) {
                struct untracked_cache *uc;
 
                if (untracked_cache < 2) {
@@ -1112,11 +1112,15 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        if (!test_if_untracked_cache_is_supported())
                                return 1;
                }
-               uc = xcalloc(1, sizeof(*uc));
-               uc->exclude_per_dir = ".gitignore";
-               /* should be the same flags used by git-status */
-               uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
-               the_index.untracked = uc;
+               if (!the_index.untracked) {
+                       uc = xcalloc(1, sizeof(*uc));
+                       strbuf_init(&uc->ident, 100);
+                       uc->exclude_per_dir = ".gitignore";
+                       /* should be the same flags used by git-status */
+                       uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+                       the_index.untracked = uc;
+               }
+               add_untracked_ident(the_index.untracked);
                the_index.cache_changed |= UNTRACKED_CHANGED;
        } else if (!untracked_cache && the_index.untracked) {
                the_index.untracked = NULL;
diff --git a/dir.c b/dir.c
index 2cf8f35cc789f241163ebb127183be172a517c7c..e9eaf97efec17e4e96c3ee6cc09e7d4a74304f6d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1794,6 +1794,40 @@ static int treat_leading_path(struct dir_struct *dir,
        return rc;
 }
 
+static const char *get_ident_string(void)
+{
+       static struct strbuf sb = STRBUF_INIT;
+       struct utsname uts;
+
+       if (sb.len)
+               return sb.buf;
+       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);
+       return sb.buf;
+}
+
+static int ident_in_untracked(const struct untracked_cache *uc)
+{
+       const char *end = uc->ident.buf + uc->ident.len;
+       const char *p   = uc->ident.buf;
+
+       for (p = uc->ident.buf; p < end; p += strlen(p) + 1)
+               if (!strcmp(p, get_ident_string()))
+                       return 1;
+       return 0;
+}
+
+void add_untracked_ident(struct untracked_cache *uc)
+{
+       if (ident_in_untracked(uc))
+               return;
+       strbuf_addstr(&uc->ident, get_ident_string());
+       /* this strbuf contains a list of strings, save NUL too */
+       strbuf_addch(&uc->ident, 0);
+}
+
 static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
                                                      int base_len,
                                                      const struct pathspec *pathspec)
@@ -1860,6 +1894,11 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
                if (ce_skip_worktree(active_cache[i]))
                        return NULL;
 
+       if (!ident_in_untracked(dir->untracked)) {
+               warning(_("Untracked cache is disabled on this system."));
+               return NULL;
+       }
+
        if (!dir->untracked->root) {
                const int len = sizeof(*dir->untracked->root);
                dir->untracked->root = xmalloc(len);
@@ -2268,6 +2307,11 @@ 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);
+
+       varint_len = encode_varint(untracked->ident.len, varbuf);
+       strbuf_add(out, varbuf, varint_len);
+       strbuf_add(out, untracked->ident.buf, untracked->ident.len);
+
        strbuf_add(out, ouc, ouc_size(len));
        free(ouc);
        ouc = NULL;
@@ -2453,17 +2497,26 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
        struct untracked_cache *uc;
        struct read_data rd;
        const unsigned char *next = data, *end = (const unsigned char *)data + sz;
-       int len;
+       const char *ident;
+       int ident_len, len;
 
        if (sz <= 1 || end[-1] != '\0')
                return NULL;
        end--;
 
+       ident_len = decode_varint(&next);
+       if (next + ident_len > end)
+               return NULL;
+       ident = (const char *)next;
+       next += ident_len;
+
        ouc = (const struct ondisk_untracked_cache *)next;
        if (next + ouc_size(0) > end)
                return NULL;
 
        uc = xcalloc(1, sizeof(*uc));
+       strbuf_init(&uc->ident, ident_len);
+       strbuf_add(&uc->ident, ident, ident_len);
        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,
diff --git a/dir.h b/dir.h
index 2ce7dd3d2765dbe021c346472183bf06e97e21cc..6ccbc454ac47da69e1b07b43961b9aaf9a21cbe0 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -127,6 +127,7 @@ struct untracked_cache {
        struct sha1_stat ss_info_exclude;
        struct sha1_stat ss_excludes_file;
        const char *exclude_per_dir;
+       struct strbuf ident;
        /*
         * dir_struct#flags must match dir_flags or the untracked
         * cache is ignored.
@@ -305,4 +306,5 @@ void untracked_cache_add_to_index(struct index_state *, const char *);
 void free_untracked_cache(struct untracked_cache *);
 struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz);
 void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
+void add_untracked_ident(struct untracked_cache *);
 #endif
index a3095be962c04d5b55c54ec39c6c3e6989372c36..1663537791e42e19f2d8cbd2493993eae41b55ff 100644 (file)
 #elif defined(_MSC_VER)
 #include "compat/msvc.h"
 #else
+#include <sys/utsname.h>
 #include <sys/wait.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
index 710441ee054626908d69550bb2011daad0488331..25d855d98b5cc718a6da63824e1c0f9ae39ab77c 100644 (file)
@@ -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;