Merge branch 'js/split-index-fixes'

The index files can become corrupt under certain conditions when
the split-index feature is in use, especially together with
fsmonitor, which have been corrected.

* js/split-index-fixes:
  unpack-trees: take care to propagate the split-index flag
  fsmonitor: avoid overriding `cache_changed` bits
  split-index; stop abusing the `base_oid` to strip the "link" extension
  split-index & fsmonitor: demonstrate a bug
This commit is contained in:
Junio C Hamano 2023-04-04 14:28:27 -07:00
commit f315a8b609
4 changed files with 72 additions and 18 deletions

View file

@ -86,7 +86,7 @@ static inline void mark_fsmonitor_valid(struct index_state *istate, struct cache
!(ce->ce_flags & CE_FSMONITOR_VALID)) {
if (S_ISGITLINK(ce->ce_mode))
return;
istate->cache_changed = 1;
istate->cache_changed |= FSMONITOR_CHANGED;
ce->ce_flags |= CE_FSMONITOR_VALID;
trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
}

View file

@ -2903,6 +2903,16 @@ static int record_ieot(void)
return !git_config_get_index_threads(&val) && val != 1;
}
enum write_extensions {
WRITE_NO_EXTENSION = 0,
WRITE_SPLIT_INDEX_EXTENSION = 1<<0,
WRITE_CACHE_TREE_EXTENSION = 1<<1,
WRITE_RESOLVE_UNDO_EXTENSION = 1<<2,
WRITE_UNTRACKED_CACHE_EXTENSION = 1<<3,
WRITE_FSMONITOR_EXTENSION = 1<<4,
};
#define WRITE_ALL_EXTENSIONS ((enum write_extensions)-1)
/*
* On success, `tempfile` is closed. If it is the temporary file
* of a `struct lock_file`, we will therefore effectively perform
@ -2911,7 +2921,7 @@ static int record_ieot(void)
* rely on it.
*/
static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
int strip_extensions, unsigned flags)
enum write_extensions write_extensions, unsigned flags)
{
uint64_t start = getnanotime();
struct hashfile *f;
@ -3084,8 +3094,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
return -1;
}
if (!strip_extensions && istate->split_index &&
!is_null_oid(&istate->split_index->base_oid)) {
if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
istate->split_index) {
struct strbuf sb = STRBUF_INIT;
if (istate->sparse_index)
@ -3099,7 +3109,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
if (!strip_extensions && !drop_cache_tree && istate->cache_tree) {
if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
!drop_cache_tree && istate->cache_tree) {
struct strbuf sb = STRBUF_INIT;
cache_tree_write(&sb, istate->cache_tree);
@ -3109,7 +3120,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
if (!strip_extensions && istate->resolve_undo) {
if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
istate->resolve_undo) {
struct strbuf sb = STRBUF_INIT;
resolve_undo_write(&sb, istate->resolve_undo);
@ -3120,7 +3132,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
if (!strip_extensions && istate->untracked) {
if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
istate->untracked) {
struct strbuf sb = STRBUF_INIT;
write_untracked_extension(&sb, istate->untracked);
@ -3131,7 +3144,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
if (!strip_extensions && istate->fsmonitor_last_update) {
if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
istate->fsmonitor_last_update) {
struct strbuf sb = STRBUF_INIT;
write_fsmonitor_extension(&sb, istate);
@ -3205,8 +3219,10 @@ static int commit_locked_index(struct lock_file *lk)
return commit_lock_file(lk);
}
static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
unsigned flags)
static int do_write_locked_index(struct index_state *istate,
struct lock_file *lock,
unsigned flags,
enum write_extensions write_extensions)
{
int ret;
int was_full = istate->sparse_index == INDEX_EXPANDED;
@ -3224,7 +3240,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
*/
trace2_region_enter_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
ret = do_write_index(istate, lock->tempfile, 0, flags);
ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
trace2_region_leave_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
@ -3253,7 +3269,7 @@ static int write_split_index(struct index_state *istate,
{
int ret;
prepare_to_write_split_index(istate);
ret = do_write_locked_index(istate, lock, flags);
ret = do_write_locked_index(istate, lock, flags, WRITE_ALL_EXTENSIONS);
finish_writing_split_index(istate);
return ret;
}
@ -3328,7 +3344,7 @@ static int write_shared_index(struct index_state *istate,
trace2_region_enter_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
ret = do_write_index(si->base, *temp, 1, flags);
ret = do_write_index(si->base, *temp, WRITE_NO_EXTENSION, flags);
trace2_region_leave_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
@ -3405,9 +3421,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
if ((!si && !test_split_index_env) ||
alternate_index_output ||
(istate->cache_changed & ~EXTMASK)) {
if (si)
oidclr(&si->base_oid);
ret = do_write_locked_index(istate, lock, flags);
ret = do_write_locked_index(istate, lock, flags,
~WRITE_SPLIT_INDEX_EXTENSION);
goto out;
}
@ -3433,8 +3448,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
/* Same initial permissions as the main .git/index file */
temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
if (!temp) {
oidclr(&si->base_oid);
ret = do_write_locked_index(istate, lock, flags);
ret = do_write_locked_index(istate, lock, flags,
~WRITE_SPLIT_INDEX_EXTENSION);
goto out;
}
ret = write_shared_index(istate, &temp, flags);

View file

@ -995,4 +995,41 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
'
test_expect_success 'split-index and FSMonitor work well together' '
git init split-index &&
test_when_finished "git -C \"$PWD/split-index\" \
fsmonitor--daemon stop" &&
(
cd split-index &&
git config core.splitIndex true &&
# force split-index in most cases
git config splitIndex.maxPercentChange 99 &&
git config core.fsmonitor true &&
# Create the following commit topology:
#
# * merge three
# |\
# | * three
# * | merge two
# |\|
# | * two
# * | one
# |/
# * 5a5efd7 initial
test_commit initial &&
test_commit two &&
test_commit three &&
git reset --hard initial &&
test_commit one &&
test_tick &&
git merge two &&
test_tick &&
git merge three &&
git rebase --force-rebase -r one
)
'
test_done

View file

@ -1926,6 +1926,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
* avoid having to create a new one.
*/
o->internal.result.split_index = o->src_index->split_index;
if (o->src_index->cache_changed & SPLIT_INDEX_ORDERED)
o->internal.result.cache_changed |= SPLIT_INDEX_ORDERED;
o->internal.result.split_index->refcount++;
} else {
o->internal.result.split_index =