update-index: test the system before enabling untracked cache
Helped-by: Eric Sunshine <sunshine@sunshineco.com> Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nguyễn Thái Ngọc Duy committed
Mar 8, 2015 at 17:12 UTC
f64cb88d3521b64b2db9353d14148328063745dc
2 files changed
+174
Documentation/git-update-index.txt
+6
@@ -178,6 +178,12 @@ may not support it yet.
178
system must change `st_mtime` field of a directory if files
179
are added or deleted in that directory.
180
181
+--force-untracked-cache::
182
+ For safety, `--untracked-cache` performs tests on the working
183
+ directory to make sure untracked cache can be used. These
184
+ tests can take a few seconds. `--force-untracked-cache` can be
185
+ used to skip the tests.
186
+
187
\--::
188
Do not interpret any more arguments as options.
189
builtin/update-index.c
+168
@@ -33,6 +33,7 @@ static int mark_valid_only;
33
static int mark_skip_worktree_only;
34
#define MARK_FLAG 1
35
#define UNMARK_FLAG 2
36
+static struct strbuf mtime_dir = STRBUF_INIT;
37
38
__attribute__((format (printf, 1, 2)))
39
static void report(const char *fmt, ...)
@@ -48,6 +49,166 @@ static void report(const char *fmt, ...)
49
va_end(vp);
50
}
51
52
+static void remove_test_directory(void)
53
+{
54
+ if (mtime_dir.len)
55
+ remove_dir_recursively(&mtime_dir, 0);
56
+}
57
+
58
+static const char *get_mtime_path(const char *path)
59
+{
60
+ static struct strbuf sb = STRBUF_INIT;
61
+ strbuf_reset(&sb);
62
+ strbuf_addf(&sb, "%s/%s", mtime_dir.buf, path);
63
+ return sb.buf;
64
+}
65
+
66
+static void xmkdir(const char *path)
67
+{
68
+ path = get_mtime_path(path);
69
+ if (mkdir(path, 0700))
70
+ die_errno(_("failed to create directory %s"), path);
71
+}
72
+
73
+static int xstat_mtime_dir(struct stat *st)
74
+{
75
+ if (stat(mtime_dir.buf, st))
76
+ die_errno(_("failed to stat %s"), mtime_dir.buf);
77
+ return 0;
78
+}
79
+
80
+static int create_file(const char *path)
81
+{
82
+ int fd;
83
+ path = get_mtime_path(path);
84
+ fd = open(path, O_CREAT | O_RDWR, 0644);
85
+ if (fd < 0)
86
+ die_errno(_("failed to create file %s"), path);
87
+ return fd;
88
+}
89
+
90
+static void xunlink(const char *path)
91
+{
92
+ path = get_mtime_path(path);
93
+ if (unlink(path))
94
+ die_errno(_("failed to delete file %s"), path);
95
+}
96
+
97
+static void xrmdir(const char *path)
98
+{
99
+ path = get_mtime_path(path);
100
+ if (rmdir(path))
101
+ die_errno(_("failed to delete directory %s"), path);
102
+}
103
+
104
+static void avoid_racy(void)
105
+{
106
+ /*
107
+ * not use if we could usleep(10) if USE_NSEC is defined. The
108
+ * field nsec could be there, but the OS could choose to
109
+ * ignore it?
110
+ */
111
+ sleep(1);
112
+}
113
+
114
+static int test_if_untracked_cache_is_supported(void)
115
+{
116
+ struct stat st;
117
+ struct stat_data base;
118
+ int fd, ret = 0;
119
+
120
+ strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX");
121
+ if (!mkdtemp(mtime_dir.buf))
122
+ die_errno("Could not make temporary directory");
123
+
124
+ fprintf(stderr, _("Testing "));
125
+ atexit(remove_test_directory);
126
+ xstat_mtime_dir(&st);
127
+ fill_stat_data(&base, &st);
128
+ fputc('.', stderr);
129
+
130
+ avoid_racy();
131
+ fd = create_file("newfile");
132
+ xstat_mtime_dir(&st);
133
+ if (!match_stat_data(&base, &st)) {
134
+ close(fd);
135
+ fputc('\n', stderr);
136
+ fprintf_ln(stderr,_("directory stat info does not "
137
+ "change after adding a new file"));
138
+ goto done;
139
+ }
140
+ fill_stat_data(&base, &st);
141
+ fputc('.', stderr);
142
+
143
+ avoid_racy();
144
+ xmkdir("new-dir");
145
+ xstat_mtime_dir(&st);
146
+ if (!match_stat_data(&base, &st)) {
147
+ close(fd);
148
+ fputc('\n', stderr);
149
+ fprintf_ln(stderr, _("directory stat info does not change "
150
+ "after adding a new directory"));
151
+ goto done;
152
+ }
153
+ fill_stat_data(&base, &st);
154
+ fputc('.', stderr);
155
+
156
+ avoid_racy();
157
+ write_or_die(fd, "data", 4);
158
+ close(fd);
159
+ xstat_mtime_dir(&st);
160
+ if (match_stat_data(&base, &st)) {
161
+ fputc('\n', stderr);
162
+ fprintf_ln(stderr, _("directory stat info changes "
163
+ "after updating a file"));
164
+ goto done;
165
+ }
166
+ fputc('.', stderr);
167
+
168
+ avoid_racy();
169
+ close(create_file("new-dir/new"));
170
+ xstat_mtime_dir(&st);
171
+ if (match_stat_data(&base, &st)) {
172
+ fputc('\n', stderr);
173
+ fprintf_ln(stderr, _("directory stat info changes after "
174
+ "adding a file inside subdirectory"));
175
+ goto done;
176
+ }
177
+ fputc('.', stderr);
178
+
179
+ avoid_racy();
180
+ xunlink("newfile");
181
+ xstat_mtime_dir(&st);
182
+ if (!match_stat_data(&base, &st)) {
183
+ fputc('\n', stderr);
184
+ fprintf_ln(stderr, _("directory stat info does not "
185
+ "change after deleting a file"));
186
+ goto done;
187
+ }
188
+ fill_stat_data(&base, &st);
189
+ fputc('.', stderr);
190
+
191
+ avoid_racy();
192
+ xunlink("new-dir/new");
193
+ xrmdir("new-dir");
194
+ xstat_mtime_dir(&st);
195
+ if (!match_stat_data(&base, &st)) {
196
+ fputc('\n', stderr);
197
+ fprintf_ln(stderr, _("directory stat info does not "
198
+ "change after deleting a directory"));
199
+ goto done;
200
+ }
201
+
202
+ if (rmdir(mtime_dir.buf))
203
+ die_errno(_("failed to delete directory %s"), mtime_dir.buf);
204
+ fprintf_ln(stderr, _(" OK"));
205
+ ret = 1;
206
+
207
+done:
208
+ strbuf_release(&mtime_dir);
209
+ return ret;
210
+}
211
+
212
static int mark_ce_flags(const char *path, int flag, int mark)
213
{
214
int namelen = strlen(path);
@@ -835,6 +996,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
996
N_("enable or disable split index")),
997
OPT_BOOL(0, "untracked-cache", &untracked_cache,
998
N_("enable/disable untracked cache")),
999
+ OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
1000
+ N_("enable untracked cache without testing the filesystem"), 2),
1001
OPT_END()
1002
};
1003
@@ -944,6 +1107,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
1107
if (untracked_cache > 0 && !the_index.untracked) {
1108
struct untracked_cache *uc;
1109
1110
+ if (untracked_cache < 2) {
1111
+ setup_work_tree();
1112
+ if (!test_if_untracked_cache_is_supported())
1113
+ return 1;
1114
+ }
1115
uc = xcalloc(1, sizeof(*uc));
1116
uc->exclude_per_dir = ".gitignore";
1117
/* should be the same flags used by git-status */