untracked cache: save to an index extension
Helped-by: Stefan Beller <sbeller@google.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
83c094ad0dd2104adbbec034f802dceb1d052981
5 files changed
+213
Documentation/technical/index-format.txt
+58
@@ -233,3 +233,61 @@ Git index format
233
The remaining index entries after replaced ones will be added to the
234
final index. These added entries are also sorted by entry name then
235
stage.
236
+
237
+== Untracked cache
238
+
239
+ Untracked cache saves the untracked file list and necessary data to
240
+ verify the cache. The signature for this extension is { 'U', 'N',
241
+ 'T', 'R' }.
242
+
243
+ The extension starts with
244
+
245
+ - Stat data of $GIT_DIR/info/exclude. See "Index entry" section from
246
+ ctime field until "file size".
247
+
248
+ - Stat data of core.excludesfile
249
+
250
+ - 32-bit dir_flags (see struct dir_struct)
251
+
252
+ - 160-bit SHA-1 of $GIT_DIR/info/exclude. Null SHA-1 means the file
253
+ does not exist.
254
+
255
+ - 160-bit SHA-1 of core.excludesfile. Null SHA-1 means the file does
256
+ not exist.
257
+
258
+ - NUL-terminated string of per-dir exclude file name. This usually
259
+ is ".gitignore".
260
+
261
+ - The number of following directory blocks, variable width
262
+ encoding. If this number is zero, the extension ends here with a
263
+ following NUL.
264
+
265
+ - A number of directory blocks in depth-first-search order, each
266
+ consists of
267
+
268
+ - The number of untracked entries, variable width encoding.
269
+
270
+ - The number of sub-directory blocks, variable width encoding.
271
+
272
+ - The directory name terminated by NUL.
273
+
274
+ - A number of untrached file/dir names terminated by NUL.
275
+
276
+The remaining data of each directory block is grouped by type:
277
+
278
+ - An ewah bitmap, the n-th bit marks whether the n-th directory has
279
+ valid untracked cache entries.
280
+
281
+ - An ewah bitmap, the n-th bit records "check-only" bit of
282
+ read_directory_recursive() for the n-th directory.
283
+
284
+ - An ewah bitmap, the n-th bit indicates whether SHA-1 and stat data
285
+ is valid for the n-th directory and exists in the next data.
286
+
287
+ - An array of stat data. The n-th data corresponds with the n-th
288
+ "one" bit in the previous ewah bitmap.
289
+
290
+ - An array of SHA-1. The n-th SHA-1 corresponds with the n-th "one" bit
291
+ in the previous ewah bitmap.
292
+
293
+ - One NUL.
cache.h
+3
@@ -291,6 +291,8 @@ static inline unsigned int canon_mode(unsigned int mode)
291
#define SPLIT_INDEX_ORDERED (1 << 6)
292
293
struct split_index;
294
+struct untracked_cache;
295
+
296
struct index_state {
297
struct cache_entry **cache;
298
unsigned int version;
@@ -304,6 +306,7 @@ struct index_state {
306
struct hashmap name_hash;
307
struct hashmap dir_hash;
308
unsigned char sha1[20];
309
+ struct untracked_cache *untracked;
310
};
311
312
extern struct index_state the_index;
dir.c
+139
@@ -12,6 +12,8 @@
12
#include "refs.h"
13
#include "wildmatch.h"
14
#include "pathspec.h"
15
+#include "varint.h"
16
+#include "ewah/ewok.h"
17
18
struct path_simplify {
19
int len;
@@ -2144,3 +2146,140 @@ void clear_directory(struct dir_struct *dir)
2146
}
2147
strbuf_release(&dir->basebuf);
2148
}
2149
+
2150
+struct ondisk_untracked_cache {
2151
+ struct stat_data info_exclude_stat;
2152
+ struct stat_data excludes_file_stat;
2153
+ uint32_t dir_flags;
2154
+ unsigned char info_exclude_sha1[20];
2155
+ unsigned char excludes_file_sha1[20];
2156
+ char exclude_per_dir[FLEX_ARRAY];
2157
+};
2158
+
2159
+#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1)
2160
+
2161
+struct write_data {
2162
+ int index; /* number of written untracked_cache_dir */
2163
+ struct ewah_bitmap *check_only; /* from untracked_cache_dir */
2164
+ struct ewah_bitmap *valid; /* from untracked_cache_dir */
2165
+ struct ewah_bitmap *sha1_valid; /* set if exclude_sha1 is not null */
2166
+ struct strbuf out;
2167
+ struct strbuf sb_stat;
2168
+ struct strbuf sb_sha1;
2169
+};
2170
+
2171
+static void stat_data_to_disk(struct stat_data *to, const struct stat_data *from)
2172
+{
2173
+ to->sd_ctime.sec = htonl(from->sd_ctime.sec);
2174
+ to->sd_ctime.nsec = htonl(from->sd_ctime.nsec);
2175
+ to->sd_mtime.sec = htonl(from->sd_mtime.sec);
2176
+ to->sd_mtime.nsec = htonl(from->sd_mtime.nsec);
2177
+ to->sd_dev = htonl(from->sd_dev);
2178
+ to->sd_ino = htonl(from->sd_ino);
2179
+ to->sd_uid = htonl(from->sd_uid);
2180
+ to->sd_gid = htonl(from->sd_gid);
2181
+ to->sd_size = htonl(from->sd_size);
2182
+}
2183
+
2184
+static void write_one_dir(struct untracked_cache_dir *untracked,
2185
+ struct write_data *wd)
2186
+{
2187
+ struct stat_data stat_data;
2188
+ struct strbuf *out = &wd->out;
2189
+ unsigned char intbuf[16];
2190
+ unsigned int intlen, value;
2191
+ int i = wd->index++;
2192
+
2193
+ /*
2194
+ * untracked_nr should be reset whenever valid is clear, but
2195
+ * for safety..
2196
+ */
2197
+ if (!untracked->valid) {
2198
+ untracked->untracked_nr = 0;
2199
+ untracked->check_only = 0;
2200
+ }
2201
+
2202
+ if (untracked->check_only)
2203
+ ewah_set(wd->check_only, i);
2204
+ if (untracked->valid) {
2205
+ ewah_set(wd->valid, i);
2206
+ stat_data_to_disk(&stat_data, &untracked->stat_data);
2207
+ strbuf_add(&wd->sb_stat, &stat_data, sizeof(stat_data));
2208
+ }
2209
+ if (!is_null_sha1(untracked->exclude_sha1)) {
2210
+ ewah_set(wd->sha1_valid, i);
2211
+ strbuf_add(&wd->sb_sha1, untracked->exclude_sha1, 20);
2212
+ }
2213
+
2214
+ intlen = encode_varint(untracked->untracked_nr, intbuf);
2215
+ strbuf_add(out, intbuf, intlen);
2216
+
2217
+ /* skip non-recurse directories */
2218
+ for (i = 0, value = 0; i < untracked->dirs_nr; i++)
2219
+ if (untracked->dirs[i]->recurse)
2220
+ value++;
2221
+ intlen = encode_varint(value, intbuf);
2222
+ strbuf_add(out, intbuf, intlen);
2223
+
2224
+ strbuf_add(out, untracked->name, strlen(untracked->name) + 1);
2225
+
2226
+ for (i = 0; i < untracked->untracked_nr; i++)
2227
+ strbuf_add(out, untracked->untracked[i],
2228
+ strlen(untracked->untracked[i]) + 1);
2229
+
2230
+ for (i = 0; i < untracked->dirs_nr; i++)
2231
+ if (untracked->dirs[i]->recurse)
2232
+ write_one_dir(untracked->dirs[i], wd);
2233
+}
2234
+
2235
+void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked)
2236
+{
2237
+ struct ondisk_untracked_cache *ouc;
2238
+ struct write_data wd;
2239
+ unsigned char varbuf[16];
2240
+ int len = 0, varint_len;
2241
+ if (untracked->exclude_per_dir)
2242
+ len = strlen(untracked->exclude_per_dir);
2243
+ ouc = xmalloc(sizeof(*ouc) + len + 1);
2244
+ stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
2245
+ stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
2246
+ hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
2247
+ hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
2248
+ ouc->dir_flags = htonl(untracked->dir_flags);
2249
+ memcpy(ouc->exclude_per_dir, untracked->exclude_per_dir, len + 1);
2250
+ strbuf_add(out, ouc, ouc_size(len));
2251
+ free(ouc);
2252
+ ouc = NULL;
2253
+
2254
+ if (!untracked->root) {
2255
+ varint_len = encode_varint(0, varbuf);
2256
+ strbuf_add(out, varbuf, varint_len);
2257
+ return;
2258
+ }
2259
+
2260
+ wd.index = 0;
2261
+ wd.check_only = ewah_new();
2262
+ wd.valid = ewah_new();
2263
+ wd.sha1_valid = ewah_new();
2264
+ strbuf_init(&wd.out, 1024);
2265
+ strbuf_init(&wd.sb_stat, 1024);
2266
+ strbuf_init(&wd.sb_sha1, 1024);
2267
+ write_one_dir(untracked->root, &wd);
2268
+
2269
+ varint_len = encode_varint(wd.index, varbuf);
2270
+ strbuf_add(out, varbuf, varint_len);
2271
+ strbuf_addbuf(out, &wd.out);
2272
+ ewah_serialize_strbuf(wd.valid, out);
2273
+ ewah_serialize_strbuf(wd.check_only, out);
2274
+ ewah_serialize_strbuf(wd.sha1_valid, out);
2275
+ strbuf_addbuf(out, &wd.sb_stat);
2276
+ strbuf_addbuf(out, &wd.sb_sha1);
2277
+ strbuf_addch(out, '\0'); /* safe guard for string lists */
2278
+
2279
+ ewah_free(wd.valid);
2280
+ ewah_free(wd.check_only);
2281
+ ewah_free(wd.sha1_valid);
2282
+ strbuf_release(&wd.out);
2283
+ strbuf_release(&wd.sb_stat);
2284
+ strbuf_release(&wd.sb_sha1);
2285
+}
dir.h
+1
@@ -298,4 +298,5 @@ static inline int dir_path_match(const struct dir_entry *ent,
298
has_trailing_dir);
299
}
300
301
+void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
302
#endif
read-cache.c
+12
@@ -39,6 +39,7 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
39
#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
40
#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
41
#define CACHE_EXT_LINK 0x6c696e6b /* "link" */
42
+#define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */
43
44
/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
45
#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
@@ -2047,6 +2048,17 @@ static int do_write_index(struct index_state *istate, int newfd,
2048
if (err)
2049
return -1;
2050
}
2051
+ if (!strip_extensions && istate->untracked) {
2052
+ struct strbuf sb = STRBUF_INIT;
2053
+
2054
+ write_untracked_extension(&sb, istate->untracked);
2055
+ err = write_index_ext_header(&c, newfd, CACHE_EXT_UNTRACKED,
2056
+ sb.len) < 0 ||
2057
+ ce_write(&c, newfd, sb.buf, sb.len) < 0;
2058
+ strbuf_release(&sb);
2059
+ if (err)
2060
+ return -1;
2061
+ }
2062
2063
if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st))
2064
return -1;