contrib/examples/git-revert.sh: use the $( ... ) construct for command substitution
[gitweb.git] / builtin / update-index.c
index 6271b54adc24c8765ca0b179faf53f6af86dc630..7431938fa654ba9af524a7018ab2c7be8242caaa 100644 (file)
@@ -33,6 +33,7 @@ static int mark_valid_only;
 static int mark_skip_worktree_only;
 #define MARK_FLAG 1
 #define UNMARK_FLAG 2
+static struct strbuf mtime_dir = STRBUF_INIT;
 
 __attribute__((format (printf, 1, 2)))
 static void report(const char *fmt, ...)
@@ -48,6 +49,166 @@ static void report(const char *fmt, ...)
        va_end(vp);
 }
 
+static void remove_test_directory(void)
+{
+       if (mtime_dir.len)
+               remove_dir_recursively(&mtime_dir, 0);
+}
+
+static const char *get_mtime_path(const char *path)
+{
+       static struct strbuf sb = STRBUF_INIT;
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s/%s", mtime_dir.buf, path);
+       return sb.buf;
+}
+
+static void xmkdir(const char *path)
+{
+       path = get_mtime_path(path);
+       if (mkdir(path, 0700))
+               die_errno(_("failed to create directory %s"), path);
+}
+
+static int xstat_mtime_dir(struct stat *st)
+{
+       if (stat(mtime_dir.buf, st))
+               die_errno(_("failed to stat %s"), mtime_dir.buf);
+       return 0;
+}
+
+static int create_file(const char *path)
+{
+       int fd;
+       path = get_mtime_path(path);
+       fd = open(path, O_CREAT | O_RDWR, 0644);
+       if (fd < 0)
+               die_errno(_("failed to create file %s"), path);
+       return fd;
+}
+
+static void xunlink(const char *path)
+{
+       path = get_mtime_path(path);
+       if (unlink(path))
+               die_errno(_("failed to delete file %s"), path);
+}
+
+static void xrmdir(const char *path)
+{
+       path = get_mtime_path(path);
+       if (rmdir(path))
+               die_errno(_("failed to delete directory %s"), path);
+}
+
+static void avoid_racy(void)
+{
+       /*
+        * not use if we could usleep(10) if USE_NSEC is defined. The
+        * field nsec could be there, but the OS could choose to
+        * ignore it?
+        */
+       sleep(1);
+}
+
+static int test_if_untracked_cache_is_supported(void)
+{
+       struct stat st;
+       struct stat_data base;
+       int fd, ret = 0;
+
+       strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX");
+       if (!mkdtemp(mtime_dir.buf))
+               die_errno("Could not make temporary directory");
+
+       fprintf(stderr, _("Testing "));
+       atexit(remove_test_directory);
+       xstat_mtime_dir(&st);
+       fill_stat_data(&base, &st);
+       fputc('.', stderr);
+
+       avoid_racy();
+       fd = create_file("newfile");
+       xstat_mtime_dir(&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"));
+               goto done;
+       }
+       fill_stat_data(&base, &st);
+       fputc('.', stderr);
+
+       avoid_racy();
+       xmkdir("new-dir");
+       xstat_mtime_dir(&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"));
+               goto done;
+       }
+       fill_stat_data(&base, &st);
+       fputc('.', stderr);
+
+       avoid_racy();
+       write_or_die(fd, "data", 4);
+       close(fd);
+       xstat_mtime_dir(&st);
+       if (match_stat_data(&base, &st)) {
+               fputc('\n', stderr);
+               fprintf_ln(stderr, _("directory stat info changes "
+                                    "after updating a file"));
+               goto done;
+       }
+       fputc('.', stderr);
+
+       avoid_racy();
+       close(create_file("new-dir/new"));
+       xstat_mtime_dir(&st);
+       if (match_stat_data(&base, &st)) {
+               fputc('\n', stderr);
+               fprintf_ln(stderr, _("directory stat info changes after "
+                                    "adding a file inside subdirectory"));
+               goto done;
+       }
+       fputc('.', stderr);
+
+       avoid_racy();
+       xunlink("newfile");
+       xstat_mtime_dir(&st);
+       if (!match_stat_data(&base, &st)) {
+               fputc('\n', stderr);
+               fprintf_ln(stderr, _("directory stat info does not "
+                                    "change after deleting a file"));
+               goto done;
+       }
+       fill_stat_data(&base, &st);
+       fputc('.', stderr);
+
+       avoid_racy();
+       xunlink("new-dir/new");
+       xrmdir("new-dir");
+       xstat_mtime_dir(&st);
+       if (!match_stat_data(&base, &st)) {
+               fputc('\n', stderr);
+               fprintf_ln(stderr, _("directory stat info does not "
+                                    "change after deleting a directory"));
+               goto done;
+       }
+
+       if (rmdir(mtime_dir.buf))
+               die_errno(_("failed to delete directory %s"), mtime_dir.buf);
+       fprintf_ln(stderr, _(" OK"));
+       ret = 1;
+
+done:
+       strbuf_release(&mtime_dir);
+       return ret;
+}
+
 static int mark_ce_flags(const char *path, int flag, int mark)
 {
        int namelen = strlen(path);
@@ -532,10 +693,9 @@ static int do_unresolve(int ac, const char **av,
 
        for (i = 1; i < ac; i++) {
                const char *arg = av[i];
-               const char *p = prefix_path(prefix, prefix_length, arg);
+               char *p = prefix_path(prefix, prefix_length, arg);
                err |= unresolve_one(p);
-               if (p < arg || p > arg + strlen(arg))
-                       free((char *)p);
+               free(p);
        }
        return err;
 }
@@ -742,6 +902,7 @@ static int reupdate_callback(struct parse_opt_ctx_t *ctx,
 int cmd_update_index(int argc, const char **argv, const char *prefix)
 {
        int newfd, entries, has_errors = 0, line_termination = '\n';
+       int untracked_cache = -1;
        int read_from_stdin = 0;
        int prefix_length = prefix ? strlen(prefix) : 0;
        int preferred_index_format = 0;
@@ -833,6 +994,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        N_("write index in this format")),
                OPT_BOOL(0, "split-index", &split_index,
                        N_("enable or disable split index")),
+               OPT_BOOL(0, "untracked-cache", &untracked_cache,
+                       N_("enable/disable untracked cache")),
+               OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
+                           N_("enable untracked cache without testing the filesystem"), 2),
                OPT_END()
        };
 
@@ -871,14 +1036,14 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                case PARSE_OPT_DONE:
                {
                        const char *path = ctx.argv[0];
-                       const char *p;
+                       char *p;
 
                        setup_work_tree();
                        p = prefix_path(prefix, prefix_length, path);
                        update_one(p);
                        if (set_executable_bit)
                                chmod_path(set_executable_bit, p);
-                       free((char *)p);
+                       free(p);
                        ctx.argc--;
                        ctx.argv++;
                        break;
@@ -909,7 +1074,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 
                setup_work_tree();
                while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
-                       const char *p;
+                       char *p;
                        if (line_termination && buf.buf[0] == '"') {
                                strbuf_reset(&nbuf);
                                if (unquote_c_style(&nbuf, buf.buf, NULL))
@@ -920,7 +1085,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        update_one(p);
                        if (set_executable_bit)
                                chmod_path(set_executable_bit, p);
-                       free((char *)p);
+                       free(p);
                }
                strbuf_release(&nbuf);
                strbuf_release(&buf);
@@ -939,6 +1104,28 @@ 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) {
+               struct untracked_cache *uc;
+
+               if (untracked_cache < 2) {
+                       setup_work_tree();
+                       if (!test_if_untracked_cache_is_supported())
+                               return 1;
+               }
+               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;
+               the_index.cache_changed |= UNTRACKED_CHANGED;
+       }
 
        if (active_cache_changed) {
                if (newfd < 0) {