Skip to content

Commit 83c094a

Browse files
pcloudsgitster
authored andcommitted
untracked cache: save to an index extension
Helped-by: Stefan Beller <[email protected]> Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent be0d9d5 commit 83c094a

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed

Documentation/technical/index-format.txt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,61 @@ Git index format
233233
The remaining index entries after replaced ones will be added to the
234234
final index. These added entries are also sorted by entry name then
235235
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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ static inline unsigned int canon_mode(unsigned int mode)
291291
#define SPLIT_INDEX_ORDERED (1 << 6)
292292

293293
struct split_index;
294+
struct untracked_cache;
295+
294296
struct index_state {
295297
struct cache_entry **cache;
296298
unsigned int version;
@@ -304,6 +306,7 @@ struct index_state {
304306
struct hashmap name_hash;
305307
struct hashmap dir_hash;
306308
unsigned char sha1[20];
309+
struct untracked_cache *untracked;
307310
};
308311

309312
extern struct index_state the_index;

dir.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include "refs.h"
1313
#include "wildmatch.h"
1414
#include "pathspec.h"
15+
#include "varint.h"
16+
#include "ewah/ewok.h"
1517

1618
struct path_simplify {
1719
int len;
@@ -2144,3 +2146,140 @@ void clear_directory(struct dir_struct *dir)
21442146
}
21452147
strbuf_release(&dir->basebuf);
21462148
}
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,4 +298,5 @@ static inline int dir_path_match(const struct dir_entry *ent,
298298
has_trailing_dir);
299299
}
300300

301+
void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
301302
#endif

read-cache.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
3939
#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
4040
#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
4141
#define CACHE_EXT_LINK 0x6c696e6b /* "link" */
42+
#define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */
4243

4344
/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
4445
#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
@@ -2047,6 +2048,17 @@ static int do_write_index(struct index_state *istate, int newfd,
20472048
if (err)
20482049
return -1;
20492050
}
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+
}
20502062

20512063
if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st))
20522064
return -1;

0 commit comments

Comments
 (0)