Here is a patch that creates a daemon that tracks file
state with inotify, writes it out to a file upon request,
and changes most of the calls to stat to use said cache.

It has bugs, but I figured it would be smarter to see
if the approach was acceptable at all before spending the
time to root the bugs out.

I've implemented the communication with a file, and not a socket, because I think implementing a socket is going to create
security issues on multiuser systems.  For example, would a
socket allow stat information to cross user boundaries?


Most stat calls are redirected to a cache that is maintained by a
daemon that maintains file system state via inotify.

Signed-off-by: Robert Zeh <robert.allan....@gmail.com>
---
 abspath.c            |   9 ++-
 bisect.c             |   3 +-
 check-racy.c         |   2 +-
 combine-diff.c       |   3 +-
 command-list.txt     |   1 +
 config.c             |   3 +-
 copy.c               |   3 +-
 diff-lib.c           |   3 +-
 diff-no-index.c      |   3 +-
 diff.c               |   9 ++-
 diffcore-order.c     |   3 +-
 dir.c                |   4 +-
filechange-cache.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++
 filechange-cache.h   |  20 +++++
 filechange-daemon.c  | 164 +++++++++++++++++++++++++++++++++++++++++
 filechange-printer.c |  13 ++++
 git.c                |  27 +++++++
 ll-merge.c           |   3 +-
 merge-recursive.c    |   5 +-
 name-hash.c          |   3 +-
 name-hash.h          |   1 +
 notes-merge.c        |   3 +-
 path.c               |   5 +-
 read-cache.c         |  11 +--
 rerere.c             |   7 +-
 setup.c              |   5 +-
 test-chmtime.c       |   2 +-
 test-wildmatch.c     |   2 +-
 unpack-trees.c       |   6 +-
 29 files changed, 486 insertions(+), 40 deletions(-)
 create mode 100644 filechange-cache.c
 create mode 100644 filechange-cache.h
 create mode 100644 filechange-daemon.c
 create mode 100644 filechange-printer.c
 create mode 100644 name-hash.h

diff --git a/abspath.c b/abspath.c
index 40cdc46..798c005 100644
--- a/abspath.c
+++ b/abspath.c
@@ -1,3 +1,4 @@
+#include "filechange-cache.h"
 #include "cache.h"

 /*
@@ -8,7 +9,7 @@
 int is_directory(const char *path)
 {
        struct stat st;
-       return (!stat(path, &st) && S_ISDIR(st.st_mode));
+       return (!cached_stat(path, &st) && S_ISDIR(st.st_mode));
 }

 /* We allow "recursive" symbolic links. Only within reason, though. */
@@ -117,7 +118,7 @@ static const char *real_path_internal(const char *path, int die_on_error)
                        last_elem = NULL;
                }

-               if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
+               if (!cached_lstat(buf, &st) && S_ISLNK(st.st_mode)) {
                        ssize_t len = readlink(buf, next_buf, PATH_MAX);
                        if (len < 0) {
                                if (die_on_error)
@@ -167,9 +168,9 @@ static const char *get_pwd_cwd(void)
                return NULL;
        pwd = getenv("PWD");
        if (pwd && strcmp(pwd, cwd)) {
-               stat(cwd, &cwd_stat);
+               cached_stat(cwd, &cwd_stat);
                if ((cwd_stat.st_dev || cwd_stat.st_ino) &&
-                   !stat(pwd, &pwd_stat) &&
+                   !cached_stat(pwd, &pwd_stat) &&
                    pwd_stat.st_dev == cwd_stat.st_dev &&
                    pwd_stat.st_ino == cwd_stat.st_ino) {
                        strlcpy(cwd, pwd, PATH_MAX);
diff --git a/bisect.c b/bisect.c
index bd1b7b5..d4b1af7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "commit.h"
 #include "diff.h"
+#include "filechange-cache.h"
 #include "revision.h"
 #include "refs.h"
 #include "list-objects.h"
@@ -649,7 +650,7 @@ static int is_expected_rev(const unsigned char *sha1)
        FILE *fp;
        int res = 0;

-       if (stat(filename, &st) || !S_ISREG(st.st_mode))
+       if (cached_stat(filename, &st) || !S_ISREG(st.st_mode))
                return 0;

        fp = fopen(filename, "r");
diff --git a/check-racy.c b/check-racy.c
index 00d92a1..c54be01 100644
--- a/check-racy.c
+++ b/check-racy.c
@@ -11,7 +11,7 @@ int main(int ac, char **av)
                struct cache_entry *ce = active_cache[i];
                struct stat st;

-               if (lstat(ce->name, &st)) {
+               if (cached_lstat(ce->name, &st)) {
                        error("lstat(%s): %s", ce->name, strerror(errno));
                        continue;
                }
diff --git a/combine-diff.c b/combine-diff.c
index 35d41cd..b6a09a5 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -3,6 +3,7 @@
 #include "blob.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "filechange-cache.h"
 #include "quote.h"
 #include "xdiff-interface.h"
 #include "log-tree.h"
@@ -806,7 +807,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                struct stat st;
                int fd = -1;

-               if (lstat(elem->path, &st) < 0)
+               if (cached_lstat(elem->path, &st) < 0)
                        goto deleted_file;

                if (S_ISLNK(st.st_mode)) {
diff --git a/command-list.txt b/command-list.txt
index bf83303..9dec5e1 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -29,6 +29,7 @@ git-count-objects ancillaryinterrogators
 git-credential                          purehelpers
 git-credential-cache                    purehelpers
 git-credential-store                    purehelpers
+git-filechange-daemon                  purehelpers
 git-cvsexportcommit                     foreignscminterface
 git-cvsimport                           foreignscminterface
 git-cvsserver                           foreignscminterface
diff --git a/config.c b/config.c
index aefd80b..99749fe 100644
--- a/config.c
+++ b/config.c
@@ -7,6 +7,7 @@
  */
 #include "cache.h"
 #include "exec_cmd.h"
+#include "filechange-cache.h"
 #include "strbuf.h"
 #include "quote.h"

@@ -1436,7 +1437,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
                        goto out_free;
                }

-               fstat(in_fd, &st);
+               cached_fstat(in_fd, &st);
                contents_sz = xsize_t(st.st_size);
                contents = xmmap(NULL, contents_sz, PROT_READ,
                        MAP_PRIVATE, in_fd, 0);
diff --git a/copy.c b/copy.c
index a7f58fd..972fabe 100644
--- a/copy.c
+++ b/copy.c
@@ -1,3 +1,4 @@
+#include "filechange-cache.h"
 #include "cache.h"

 int copy_fd(int ifd, int ofd)
@@ -39,7 +40,7 @@ static int copy_times(const char *dst, const char *src)
 {
        struct stat st;
        struct utimbuf times;
-       if (stat(src, &st) < 0)
+       if (cached_stat(src, &st) < 0)
                return -1;
        times.actime = st.st_atime;
        times.modtime = st.st_mtime;
diff --git a/diff-lib.c b/diff-lib.c
index f35de0f..8d5a005 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -2,6 +2,7 @@
  * Copyright (C) 2005 Junio C Hamano
  */
 #include "cache.h"
+#include "filechange-cache.h"
 #include "quote.h"
 #include "commit.h"
 #include "diff.h"
@@ -27,7 +28,7 @@
  */
 static int check_removed(const struct cache_entry *ce, struct stat *st)
 {
-       if (lstat(ce->name, st) < 0) {
+       if (cached_lstat(ce->name, st) < 0) {
                if (errno != ENOENT && errno != ENOTDIR)
                        return -1;
                return 1;
diff --git a/diff-no-index.c b/diff-no-index.c
index 74da659..d3fb354 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -7,6 +7,7 @@
 #include "cache.h"
 #include "color.h"
 #include "commit.h"
+#include "filechange-cache.h"
 #include "blob.h"
 #include "tag.h"
 #include "diff.h"
@@ -51,7 +52,7 @@ static int get_mode(const char *path, int *mode)
 #endif
        else if (path == file_from_standard_input)
                *mode = create_ce_mode(0666);
-       else if (lstat(path, &st))
+       else if (cached_lstat(path, &st))
                return error("Could not access '%s'", path);
        else
                *mode = st.st_mode;
diff --git a/diff.c b/diff.c
index 156fec4..a5be122 100644
--- a/diff.c
+++ b/diff.c
@@ -5,6 +5,7 @@
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "filechange-cache.h"
 #include "delta.h"
 #include "xdiff-interface.h"
 #include "color.h"
@@ -2629,7 +2630,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
         * If ce matches the file in the work tree, we can reuse it.
         */
        if (ce_uptodate(ce) ||
-           (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+           (!cached_lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
                return 1;

        return 0;
@@ -2684,7 +2685,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                struct stat st;
                int fd;

-               if (lstat(s->path, &st) < 0) {
+               if (cached_lstat(s->path, &st) < 0) {
                        if (errno == ENOENT) {
                        err_empty:
                                err = -1;
@@ -2826,7 +2827,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
        if (!one->sha1_valid ||
            reuse_worktree_file(name, one->sha1, 1)) {
                struct stat st;
-               if (lstat(name, &st) < 0) {
+               if (cached_lstat(name, &st) < 0) {
                        if (errno == ENOENT)
                                goto not_a_valid_file;
                        die_errno("stat(%s)", name);
@@ -3043,7 +3044,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
                                hashcpy(one->sha1, null_sha1);
                                return;
                        }
-                       if (lstat(one->path, &st) < 0)
+                       if (cached_lstat(one->path, &st) < 0)
                                die_errno("stat '%s'", one->path);
                        if (index_path(one->sha1, one->path, &st, 0))
                                die("cannot hash %s", one->path);
diff --git a/diffcore-order.c b/diffcore-order.c
index 23e9385..636be01 100644
--- a/diffcore-order.c
+++ b/diffcore-order.c
@@ -4,6 +4,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "filechange-cache.h"

 static char **order;
 static int order_cnt;
@@ -22,7 +23,7 @@ static void prepare_order(const char *orderfile)
        fd = open(orderfile, O_RDONLY);
        if (fd < 0)
                return;
-       if (fstat(fd, &st)) {
+       if (cached_fstat(fd, &st)) {
                close(fd);
                return;
        }
diff --git a/dir.c b/dir.c
index 57394e4..a67a592 100644
--- a/dir.c
+++ b/dir.c
@@ -476,7 +476,7 @@ int add_excludes_from_file_to_list(const char *fname,
        char *buf, *entry;

        fd = open(fname, O_RDONLY);
-       if (fd < 0 || fstat(fd, &st) < 0) {
+       if (fd < 0 || cached_fstat(fd, &st) < 0) {
                if (errno != ENOENT)
                        warn_on_inaccessible(fname);
                if (0 <= fd)
@@ -1551,7 +1551,7 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)

                strbuf_setlen(path, len);
                strbuf_addstr(path, e->d_name);
-               if (lstat(path->buf, &st))
+               if (cached_lstat(path->buf, &st))
                        ; /* fall thru */
                else if (S_ISDIR(st.st_mode)) {
                        if (!remove_dir_recurse(path, flag, &kept_down))
diff --git a/filechange-cache.c b/filechange-cache.c
new file mode 100644
index 0000000..80c698f
--- /dev/null
+++ b/filechange-cache.c
@@ -0,0 +1,203 @@
+#include <unistd.h>
+#include <stdio.h>
+#include "builtin.h"
+#include "hash.h"
+#include "name-hash.h"
+#include "strbuf.h"
+#include "filechange-cache.h"
+
+
+static struct hash_table stat_cache;
+static const int CACHE_ENTRY_FILE_SIZE =
+       sizeof(struct stat) + /* sizeof(stat_cache_entry.st) */
+       sizeof(int); /* sizeof(stat_cache_entry.stat_return) */
+
+static void insert_stat_cache_entry(const char *path,
+                                   struct stat_cache_entry *new_entry);
+
+void setup_stat_cache()
+{
+       init_hash(&stat_cache);
+}
+
+static int write_stat_cache_entry(void *void_stat_cache_entry, void *void_fp)
+{
+       FILE *fp = (FILE*)(void_fp);
+       const struct stat_cache_entry *entry =
+               (struct stat_cache_entry*)(void_stat_cache_entry);
+
+       for (; entry; entry = entry->next) {
+               if (fprintf(fp, "%s\n", entry->path) < 0)
+                       die_errno("Unable to write to %s",
+                                 git_path("WT_STATUS_TMP"));
+               if (fwrite(&entry->stat_return,
+                          CACHE_ENTRY_FILE_SIZE, 1, fp) < 0)
+                       die_errno("Unable to write to %s",
+                                 git_path("WT_STATUS_TMP"));
+       }
+       return 0;
+}
+
+void write_stat_cache()
+{
+       const char *status_tmp = git_path("WT_STATUS_TMP");
+       const char *status_output = git_path("WT_STATUS");
+       FILE *fp = fopen(status_tmp, "w");
+       if (!fp)
+               die_errno("Unable to create %s", status_tmp);
+       if (fprintf(fp, "version_format=1\n") < 0)
+               die_errno("Unable to write to %s", status_tmp);
+       for_each_hash(&stat_cache, write_stat_cache_entry, fp);
+       if (fclose(fp) < 0)
+               die_errno("Unable to close %s", status_tmp);
+       if (rename(status_tmp, status_output) < 0)
+               die_errno("Unable to rename %s to %s", status_tmp,
+                         status_output);
+}
+
+static void read_stat_cache_file()
+{
+       struct strbuf line = STRBUF_INIT;
+       const char *status_output = git_path("WT_STATUS");
+       int read_version = 0;
+
+       FILE *fp = fopen(status_output, "r");
+       if (!fp)
+               die_errno("Unable to read %s", status_output);
+       
+       if (strbuf_getline(&line, fp, '\n') != EOF) {
+               sscanf(line.buf, "version_format=%d\n", &read_version);
+               if (read_version != 1) {
+                       die("Expected version 1 of stat_cache file");
+               }
+       }
+
+       while (strbuf_getline(&line, fp, '\n') != EOF) {
+               struct stat_cache_entry *entry =
+                       (struct stat_cache_entry*)(xcalloc(1, sizeof(*entry)));
+               entry->path = xstrdup(line.buf);
+               if (fread(&entry->stat_return,
+                         CACHE_ENTRY_FILE_SIZE, 1, fp) != 1) {
+                       die_errno("Unable to read stat_cache file");
+               }
+               insert_stat_cache_entry(entry->path, entry);
+       }
+
+       strbuf_release(&line);
+}
+
+static int request_stat_cache_file()
+{
+       int count = 0;
+       int stat_return_code = 0;
+       const char *request_path = git_path("REQUEST_WT_STATUS");
+       const char *status_output = git_path("WT_STATUS");
+       struct stat stat_buf;
+
+       char buffer[1] = { 0 };
+       FILE *fp = NULL;
+
+       if (0 != stat(request_path, &stat_buf))
+               return 0;
+       
+       if (unlink(status_output) != 0 && (errno != ENOENT))
+               die_errno("Unable to remove %s", status_output);
+
+       fp = fopen(request_path, "w");
+       if (!fp) {
+               die_errno("Unable to open %s", request_path);
+       }
+
+       if (fwrite(&buffer, 0, 0, fp) != 0)
+               die_errno("Unable to write to %s", request_path);
+       
+       for (count = 0;
+            (count < 10) &&
+                    ((stat_return_code = stat(status_output, &stat_buf)) != 0) 
&&
+                    (errno == ENOENT);
+            count++) {
+               usleep(1000);
+       }
+
+       return stat_return_code == 0;
+}
+
+void read_stat_cache()
+{
+       static int read_cache = 1;
+       if (read_cache && request_stat_cache_file()) {
+               read_stat_cache_file();
+               read_cache = 0;
+       }
+       read_cache = 0;
+}
+
+struct stat_cache_entry *get_stat_cache_entry(const char *path)
+{
+       const unsigned int hash = hash_name(path, strlen(path));
+       struct stat_cache_entry *entry = NULL;
+       for(entry = lookup_hash(hash, &stat_cache); entry;
+           entry = entry->next) {
+               if (!strcmp(path, entry->path)) return entry;
+       }
+       return NULL;
+}
+
+static void insert_stat_cache_entry(const char *path,
+                                   struct stat_cache_entry *new_entry)
+{
+       assert(get_stat_cache_entry(path) == NULL);
+
+       void **insert_result =
+               insert_hash(hash_name(path, strlen(path)), (void*)new_entry,
+                           &stat_cache);
+       if (!insert_result) return;
+       struct stat_cache_entry *existing_entry =
+               (struct stat_cache_entry*)(*insert_result);
+       while(existing_entry->next) {
+               existing_entry = existing_entry->next;
+       }
+       assert(!existing_entry->next);
+       existing_entry->next = new_entry;
+}
+
+void update_stat_cache(const char *path)
+{
+       struct stat_cache_entry *entry = get_stat_cache_entry(path);
+       if (!entry) {
+               entry = (struct stat_cache_entry*)(xcalloc(1, sizeof(*entry)));
+               entry->path = xstrdup(path);
+               insert_stat_cache_entry(path, entry);
+       }
+       
+       entry->stat_return = lstat(path, &entry->st);
+}
+
+int cached_stat(const char *path, struct stat *buf)
+{
+       return stat(path, buf);
+}
+
+int cached_fstat(int fd, struct stat *buf)
+{
+       return fstat(fd, buf);
+}
+
+int cached_lstat(const char *path, struct stat *buf)
+{
+       int stat_return_value = 0;
+       struct stat_cache_entry *entry = 0;
+
+       read_stat_cache();
+
+       entry = get_stat_cache_entry(path);
+
+       stat_return_value = lstat(path, buf);
+       
+       if (entry && (stat_return_value != entry->stat_return) &&
+           (memcpy(&entry->st, buf, sizeof(*buf)))) {
+               abort();
+       }
+       
+       return stat_return_value;
+}
diff --git a/filechange-cache.h b/filechange-cache.h
new file mode 100644
index 0000000..75a9f79
--- /dev/null
+++ b/filechange-cache.h
@@ -0,0 +1,20 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+struct stat_cache_entry {
+       const char *path;
+       struct stat_cache_entry *next;
+       int stat_return;
+       struct stat st;
+};
+
+extern void write_stat_cache();
+extern void read_stat_cache();
+extern void setup_stat_cache();
+extern struct stat_cache_entry *get_stat_cache_entry(const char *path);
+extern void update_stat_cache(const char *path);
+
+extern int cached_stat(const char *path, struct stat *buf);
+extern int cached_fstat(int fd, struct stat *buf);
+extern int cached_lstat(const char *path, struct stat *buf);
diff --git a/filechange-daemon.c b/filechange-daemon.c
new file mode 100644
index 0000000..df6f0d3
--- /dev/null
+++ b/filechange-daemon.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <libgen.h>
+#include <x86_64-linux-gnu/sys/inotify.h>
+
+#include "filechange-cache.h"
+#include "builtin.h"
+#include "dir.h"
+#include "hash.h"
+
+static int request_watch_descriptor = -1;
+static int root_directory_watch_descriptor = -1;
+
+static void setup_environment()
+{
+       setup_stat_cache();
+}
+
+static int setup_inotify()
+{
+       int inotify_fd = inotify_init();
+       if (inotify_fd < 0) {
+               die_errno("Unable to create inotify watch");
+       }
+       return inotify_fd;
+}
+
+static void restart()
+{
+
+}
+
+
+static void watch_control(int inotify_fd)
+{
+       struct stat stat_buf;
+       const char *request_path = git_path("REQUEST_WT_STATUS");
+
+       if ((stat(request_path, &stat_buf) == -1) && (errno == ENOENT)) {
+               FILE *out = fopen(request_path, "w");
+               if (out == NULL)
+                       die_errno("Unable to create %s", request_path);
+       }
+
+       request_watch_descriptor = inotify_add_watch(inotify_fd,
+                                                    request_path, IN_MODIFY);
+       
+       if (request_watch_descriptor < 0)
+               die_errno("Unable to watch %s", get_git_dir());
+}
+
+static void watch_file(int inotify_fd, const char *path)
+{
+       int watch_descriptor = 0;
+       char *path_copy = xstrdup(path);
+       char *dir = dirname(path_copy);
+       const int interest_set =
+               IN_MODIFY  | IN_DELETE | IN_CREATE  |
+               IN_DELETE_SELF | IN_MOVE_SELF |
+               IN_MOVED_TO;
+
+       watch_descriptor = inotify_add_watch(inotify_fd, dir, interest_set);
+       if (watch_descriptor < 0)
+               die_errno("Unable to create inotify watch for %s", dir);
+
+       watch_descriptor = inotify_add_watch(inotify_fd, path, interest_set);
+       if (watch_descriptor < 0)
+               die_errno("Unable to create inotify watch for %s", dir);
+       update_stat_cache(path);
+
+       free(path_copy);
+}
+
+static void watch_directory(int inotify_fd)
+{
+       char buf[PATH_MAX];
+
+       if (!getcwd(buf, sizeof(buf)))
+               die_errno("Unable to get current directory");
+
+       int i = 0;
+       struct dir_struct dir;
+       const char *pathspec[1] = { buf, NULL };
+
+       memset(&dir, 0, sizeof(dir));
+       setup_standard_excludes(&dir);
+
+       fill_directory(&dir, pathspec);
+       for(i = 0; i < dir.nr; i++) {
+               struct dir_entry *ent = dir.entries[i];
+               watch_file(inotify_fd, ent->name);
+               free(ent);
+       }
+
+       free(dir.entries);
+       free(dir.ignored);
+}
+
+static void watch_root_directory(int inotify_fd)
+{
+       char buf[PATH_MAX];
+
+       if (!getcwd(buf, sizeof(buf)))
+               die_errno("Unable to get current directory");
+
+       root_directory_watch_descriptor =
+               inotify_add_watch(inotify_fd, buf, IN_DELETE);
+       if (root_directory_watch_descriptor < 0)
+               die_errno("Unable to watch %s directory", buf);
+}
+
+#define INOTIFY_EVENT_SIZE  (sizeof (struct inotify_event)  + PATH_MAX + 1)
+
+static void remove_request_file(void)
+{
+       const char *request_path = git_path("REQUEST_WT_STATUS");
+       if (unlink(request_path)) {
+               die_errno("Unable to remove %s on exit",
+                         request_path);
+       }
+}
+
+static void loop(int inotify_fd)
+{
+       char buffer[INOTIFY_EVENT_SIZE * 10];
+       int length = 0;
+       
+       while (1) {
+               int i = 0;
+               length = read(inotify_fd, buffer, sizeof(buffer));
+               for(i = 0; i < length; ) {
+                       struct inotify_event *event =
+                               (struct inotify_event*)(buffer+i);
+                       /* printf("event: %d %x %d %s\n", event->wd, 
event->mask,
+                          event->len, event->name); */
+                       if (request_watch_descriptor == event->wd) {
+                               write_stat_cache();
+                       } else if (root_directory_watch_descriptor
+                                  == event->wd) {
+                               printf("root directory died!\n");
+                               exit(0);
+                       } else if (event->mask & IN_Q_OVERFLOW) {
+                               restart();
+                       } else if (event->mask & IN_MODIFY) {
+                               if (event->len)
+                                       update_stat_cache(event->name);
+                       }
+                       
+                       i += sizeof(struct inotify_event) + event->len;
+               }
+       }
+}
+
+int main(int argc, const char **argv)
+{
+       const int inotify_fd = setup_inotify();
+
+       atexit(remove_request_file);
+       setup_environment();
+       watch_control(inotify_fd);
+       watch_root_directory(inotify_fd);
+       watch_directory(inotify_fd);
+       loop(inotify_fd);
+       return 0;
+}
diff --git a/filechange-printer.c b/filechange-printer.c
new file mode 100644
index 0000000..fe43d80
--- /dev/null
+++ b/filechange-printer.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include "filechange-cache.h"
+
+int main()
+{
+       struct stat_cache_entry *entry = NULL;
+       const char *missing = "t/t7201-co.sh";
+       read_stat_cache();
+       
+       entry = get_stat_cache_entry(missing);
+       printf("%p\n", entry);
+       return 0;
+}
diff --git a/git.c b/git.c
index b10c18b..ea92a65 100644
--- a/git.c
+++ b/git.c
@@ -504,6 +504,31 @@ static int run_argv(int *argcp, const char ***argv)
 }


+static void fork_filechange_daemon()
+{
+       struct stat stat_buf;
+       FILE *log = fopen("/tmp/foo.txt", "a");
+       fprintf(log, "cwd = %s\n", get_current_dir_name());
+
+       if (stat(git_path("REQUEST_WT_STATUS"), &stat_buf) == -1) {
+               pid_t child = 0;
+
+               child = fork();
+               fprintf(log, "starting %d\n", (int)child);
+               if (!child) {
+                       fclose(log);
+                       execl("/home/razeh/src/git/git-filechange-daemon",
+                             "/home/razeh/src/git/git-filechange-daemon",
+                             get_current_dir_name(),
+                             (char*) NULL);
+                       die_errno("Unable to launch file change daemon");
+               }
+       } else {
+               fprintf(log, "already running\n");
+       }
+
+}
+
 int main(int argc, const char **argv)
 {
        const char *cmd;
@@ -558,6 +583,8 @@ int main(int argc, const char **argv)
         */
        setup_path();

+       fork_filechange_daemon();
+
        while (1) {
                static int done_help = 0;
                static int was_alias = 0;
diff --git a/ll-merge.c b/ll-merge.c
index fb61ea6..7ced2bb 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -6,6 +6,7 @@

 #include "cache.h"
 #include "attr.h"
+#include "filechange-cache.h"
 #include "xdiff-interface.h"
 #include "run-command.h"
 #include "ll-merge.h"
@@ -195,7 +196,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
        fd = open(temp[1], O_RDONLY);
        if (fd < 0)
                goto bad;
-       if (fstat(fd, &st))
+       if (cached_fstat(fd, &st))
                goto close_bad;
        result->size = st.st_size;
        result->ptr = xmalloc(result->size + 1);
diff --git a/merge-recursive.c b/merge-recursive.c
index ea9dbd3..7d371d6 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -12,6 +12,7 @@
 #include "tree-walk.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "filechange-cache.h"
 #include "tag.h"
 #include "unpack-trees.h"
 #include "string-list.h"
@@ -606,7 +607,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
                        *p = '_';
        while (string_list_has_string(&o->current_file_set, newpath) ||
               string_list_has_string(&o->current_directory_set, newpath) ||
-              lstat(newpath, &st) == 0)
+              cached_lstat(newpath, &st) == 0)
                sprintf(p, "_%d", suffix++);

        string_list_insert(&o->current_file_set, newpath);
@@ -634,7 +635,7 @@ static int dir_in_way(const char *path, int check_working_copy)
        }

        free(dirpath);
-       return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode);
+ return check_working_copy && !cached_lstat(path, &st) && S_ISDIR(st.st_mode);
 }

 static int was_tracked(const char *path)
diff --git a/name-hash.c b/name-hash.c
index d8d25c2..d88185f 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -7,6 +7,7 @@
  */
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "name-hash.h"

 /*
  * This removes bit 5 if bit 6 is set.
@@ -20,7 +21,7 @@ static inline unsigned char icase_hash(unsigned char c)
        return c & ~((c & 0x40) >> 1);
 }

-static unsigned int hash_name(const char *name, int namelen)
+unsigned int hash_name(const char *name, int namelen)
 {
        unsigned int hash = 0x123;

diff --git a/name-hash.h b/name-hash.h
new file mode 100644
index 0000000..3355d94
--- /dev/null
+++ b/name-hash.h
@@ -0,0 +1 @@
+extern unsigned int hash_name(const char *name, int namelen);
diff --git a/notes-merge.c b/notes-merge.c
index 0f67bd3..f792f83 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -3,6 +3,7 @@
 #include "refs.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "filechange-cache.h"
 #include "xdiff-interface.h"
 #include "ll-merge.h"
 #include "dir.h"
@@ -731,7 +732,7 @@ int notes_merge_commit(struct notes_merge_options *o,

                strbuf_addstr(&path, e->d_name);
                /* write file as blob, and add to partial_tree */
-               if (stat(path.buf, &st))
+               if (cached_stat(path.buf, &st))
                        die_errno("Failed to stat '%s'", path.buf);
                if (index_path(blob_sha1, path.buf, &st, HASH_WRITE_OBJECT))
                        die("Failed to write blob object from '%s'", path.buf);
diff --git a/path.c b/path.c
index d3d3f8b..6844d2d 100644
--- a/path.c
+++ b/path.c
@@ -11,6 +11,7 @@
  * which is what it's designed for.
  */
 #include "cache.h"
+#include "filechange-cache.h"
 #include "strbuf.h"
 #include "string-list.h"

@@ -360,7 +361,7 @@ const char *enter_repo(const char *path, int strict)
                for (i = 0; suffix[i]; i++) {
                        struct stat st;
                        strcpy(used_path + len, suffix[i]);
-                       if (!stat(used_path, &st) &&
+                       if (!cached_stat(used_path, &st) &&
                            (S_ISREG(st.st_mode) ||
                            (S_ISDIR(st.st_mode) && 
is_git_directory(used_path)))) {
                                strcat(validated_path, suffix[i]);
@@ -400,7 +401,7 @@ int set_shared_perm(const char *path, int mode)
                return 0;
        }
        if (!mode) {
-               if (lstat(path, &st) < 0)
+               if (cached_lstat(path, &st) < 0)
                        return -1;
                mode = st.st_mode;
                orig_mode = mode;
diff --git a/read-cache.c b/read-cache.c
index 827ae55..508ddc1 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -8,6 +8,7 @@
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
+#include "filechange-cache.h"
 #include "tree.h"
 #include "commit.h"
 #include "blob.h"
@@ -672,7 +673,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, int add_file_to_index(struct index_state *istate, const char *path, int flags)
 {
        struct stat st;
-       if (lstat(path, &st))
+       if (cached_lstat(path, &st))
                die_errno("unable to stat '%s'", path);
        return add_to_index(istate, path, &st, flags);
 }
@@ -1032,7 +1033,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
                return ce;
        }

-       if (lstat(ce->name, &st) < 0) {
+       if (cached_lstat(ce->name, &st) < 0) {
                if (err)
                        *err = errno;
                return NULL;
@@ -1430,7 +1431,7 @@ int read_index_from(struct index_state *istate, const char *path)
                die_errno("index file open failed");
        }

-       if (fstat(fd, &st))
+       if (cached_fstat(fd, &st))
                die_errno("cannot stat the open index");

        mmap_size = xsize_t(st.st_size);
@@ -1618,7 +1619,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
         */
        struct stat st;

-       if (lstat(ce->name, &st) < 0)
+       if (cached_lstat(ce->name, &st) < 0)
                return;
        if (ce_match_stat_basic(ce, &st))
                return;
@@ -1830,7 +1831,7 @@ int write_index(struct index_state *istate, int newfd)
                        return -1;
        }

-       if (ce_flush(&c, newfd) || fstat(newfd, &st))
+       if (ce_flush(&c, newfd) || cached_fstat(newfd, &st))
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
diff --git a/rerere.c b/rerere.c
index a6a5cd5..5115d0e 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "filechange-cache.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff-interface.h"
@@ -28,7 +29,7 @@ const char *rerere_path(const char *hex, const char *file)
 static int has_rerere_resolution(const char *hex)
 {
        struct stat st;
-       return !stat(rerere_path(hex, "postimage"), &st);
+       return !cached_stat(rerere_path(hex, "postimage"), &st);
 }

 static void read_rr(struct string_list *rr)
@@ -681,13 +682,13 @@ int rerere_forget(const char **pathspec)
 static time_t rerere_created_at(const char *name)
 {
        struct stat st;
- return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime; + return cached_stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 }

 static time_t rerere_last_used_at(const char *name)
 {
        struct stat st;
- return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime; + return cached_stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
 }

 static void unlink_rr_item(const char *name)
diff --git a/setup.c b/setup.c
index 2e1521b..690987a 100644
--- a/setup.c
+++ b/setup.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "dir.h"
+#include "filechange-cache.h"
 #include "string-list.h"

 static int inside_git_dir = -1;
@@ -74,7 +75,7 @@ int check_filename(const char *prefix, const char *arg)
                name = prefix_filename(prefix, strlen(prefix), arg);
        else
                name = arg;
-       if (!lstat(name, &st))
+       if (!cached_lstat(name, &st))
                return 1; /* file exists */
        if (errno == ENOENT || errno == ENOTDIR)
                return 0; /* file does not exist */
@@ -638,7 +639,7 @@ static const char *setup_nongit(const char *cwd, int *nongit_ok) static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
 {
        struct stat buf;
-       if (stat(path, &buf)) {
+       if (cached_stat(path, &buf)) {
                die_errno("failed to stat '%*s%s%s'",
                                prefix_len,
                                prefix ? prefix : "",
diff --git a/test-chmtime.c b/test-chmtime.c
index 92713d1..bb5f22a 100644
--- a/test-chmtime.c
+++ b/test-chmtime.c
@@ -81,7 +81,7 @@ int main(int argc, const char *argv[])
                struct stat sb;
                struct utimbuf utb;

-               if (stat(argv[i], &sb) < 0) {
+               if (cached_stat(argv[i], &sb) < 0) {
                        fprintf(stderr, "Failed to stat %s: %s\n",
                                argv[i], strerror(errno));
                        return -1;
diff --git a/test-wildmatch.c b/test-wildmatch.c
index a3e2643..838ff69 100644
--- a/test-wildmatch.c
+++ b/test-wildmatch.c
@@ -19,7 +19,7 @@ static int perf(int ac, char **av)
        if (lang && strcmp(lang, "C"))
                die("Please test it on C locale.");

-       if ((fd = open(file, O_RDONLY)) == -1 || fstat(fd, &st))
+       if ((fd = open(file, O_RDONLY)) == -1 || cached_fstat(fd, &st))
                die_errno("file open");

        buffer = xmalloc(st.st_size + 2);
diff --git a/unpack-trees.c b/unpack-trees.c
index 09e53df..fc20be4 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1430,13 +1430,13 @@ static int verify_absent_1(struct cache_entry *ce,
                char path[PATH_MAX + 1];
                memcpy(path, ce->name, len);
                path[len] = 0;
-               if (lstat(path, &st))
+               if (cached_lstat(path, &st))
                        return error("cannot stat '%s': %s", path,
                                        strerror(errno));

                return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st,
                                error_type, o);
-       } else if (lstat(ce->name, &st)) {
+       } else if (cached_lstat(ce->name, &st)) {
                if (errno != ENOENT)
                        return error("cannot stat '%s': %s", ce->name,
                                     strerror(errno));
@@ -1838,7 +1838,7 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
                int update = 0;
if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) {
                        struct stat st;
-                       if (lstat(old->name, &st) ||
+                       if (cached_lstat(old->name, &st) ||
ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
                                update |= CE_UPDATE;
                }
--
1.8.2.rc0.29.g3a0aba8.dirty


--
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