mirror of
https://github.com/git/git.git
synced 2024-11-18 21:23:51 +01:00
Merge branch 'nd/split-index'
An experiment to use two files (the base file and incremental changes relative to it) to represent the index to reduce I/O cost of rewriting a large index when only small part of the working tree changes. * nd/split-index: (32 commits) t1700: new tests for split-index mode t2104: make sure split index mode is off for the version test read-cache: force split index mode with GIT_TEST_SPLIT_INDEX read-tree: note about dropping split-index mode or index version read-tree: force split-index mode off on --index-output rev-parse: add --shared-index-path to get shared index path update-index --split-index: do not split if $GIT_DIR is read only update-index: new options to enable/disable split index mode split-index: strip pathname of on-disk replaced entries split-index: do not invalidate cache-tree at read time split-index: the reading part split-index: the writing part read-cache: mark updated entries for split index read-cache: save deleted entries in split index read-cache: mark new entries for split index read-cache: split-index mode read-cache: save index SHA-1 after reading entry.c: update cache_changed if refresh_cache is set in checkout_entry() cache-tree: mark istate->cache_changed on prime_cache_tree() cache-tree: mark istate->cache_changed on cache tree update ...
This commit is contained in:
commit
788cef81d4
1
.gitignore
vendored
1
.gitignore
vendored
@ -181,6 +181,7 @@
|
||||
/test-date
|
||||
/test-delta
|
||||
/test-dump-cache-tree
|
||||
/test-dump-split-index
|
||||
/test-scrap-cache-tree
|
||||
/test-genrandom
|
||||
/test-hashmap
|
||||
|
@ -245,6 +245,10 @@ print a message to stderr and exit with nonzero status.
|
||||
--show-toplevel::
|
||||
Show the absolute path of the top-level directory.
|
||||
|
||||
--shared-index-path::
|
||||
Show the path to the shared index file in split index mode, or
|
||||
empty if not in split-index mode.
|
||||
|
||||
Other Options
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
|
@ -161,6 +161,17 @@ may not support it yet.
|
||||
Only meaningful with `--stdin` or `--index-info`; paths are
|
||||
separated with NUL character instead of LF.
|
||||
|
||||
--split-index::
|
||||
--no-split-index::
|
||||
Enable or disable split index mode. If enabled, the index is
|
||||
split into two files, $GIT_DIR/index and $GIT_DIR/sharedindex.<SHA-1>.
|
||||
Changes are accumulated in $GIT_DIR/index while the shared
|
||||
index file contains all index entries stays unchanged. If
|
||||
split-index mode is already enabled and `--split-index` is
|
||||
given again, all changes in $GIT_DIR/index are pushed back to
|
||||
the shared index file. This mode is designed for very large
|
||||
indexes that take a signficant amount of time to read or write.
|
||||
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
|
@ -155,6 +155,10 @@ index::
|
||||
The current index file for the repository. It is
|
||||
usually not found in a bare repository.
|
||||
|
||||
sharedindex.<SHA-1>::
|
||||
The shared index part, to be referenced by $GIT_DIR/index and
|
||||
other temporary index files. Only valid in split index mode.
|
||||
|
||||
info::
|
||||
Additional information about the repository is recorded
|
||||
in this directory.
|
||||
|
@ -129,6 +129,9 @@ Git index format
|
||||
(Version 4) In version 4, the padding after the pathname does not
|
||||
exist.
|
||||
|
||||
Interpretation of index entries in split index mode is completely
|
||||
different. See below for details.
|
||||
|
||||
== Extensions
|
||||
|
||||
=== Cached tree
|
||||
@ -198,3 +201,35 @@ Git index format
|
||||
- At most three 160-bit object names of the entry in stages from 1 to 3
|
||||
(nothing is written for a missing stage).
|
||||
|
||||
=== Split index
|
||||
|
||||
In split index mode, the majority of index entries could be stored
|
||||
in a separate file. This extension records the changes to be made on
|
||||
top of that to produce the final index.
|
||||
|
||||
The signature for this extension is { 'l', 'i, 'n', 'k' }.
|
||||
|
||||
The extension consists of:
|
||||
|
||||
- 160-bit SHA-1 of the shared index file. The shared index file path
|
||||
is $GIT_DIR/sharedindex.<SHA-1>. If all 160 bits are zero, the
|
||||
index does not require a shared index file.
|
||||
|
||||
- An ewah-encoded delete bitmap, each bit represents an entry in the
|
||||
shared index. If a bit is set, its corresponding entry in the
|
||||
shared index will be removed from the final index. Note, because
|
||||
a delete operation changes index entry positions, but we do need
|
||||
original positions in replace phase, it's best to just mark
|
||||
entries for removal, then do a mass deletion after replacement.
|
||||
|
||||
- An ewah-encoded replace bitmap, each bit represents an entry in
|
||||
the shared index. If a bit is set, its corresponding entry in the
|
||||
shared index will be replaced with an entry in this index
|
||||
file. All replaced entries are stored in sorted order in this
|
||||
index. The first "1" bit in the replace bitmap corresponds to the
|
||||
first index entry, the second "1" bit to the second entry and so
|
||||
on. Replaced entries may have empty path names to save space.
|
||||
|
||||
The remaining index entries after replaced ones will be added to the
|
||||
final index. These added entries are also sorted by entry namme then
|
||||
stage.
|
||||
|
2
Makefile
2
Makefile
@ -552,6 +552,7 @@ TEST_PROGRAMS_NEED_X += test-ctype
|
||||
TEST_PROGRAMS_NEED_X += test-date
|
||||
TEST_PROGRAMS_NEED_X += test-delta
|
||||
TEST_PROGRAMS_NEED_X += test-dump-cache-tree
|
||||
TEST_PROGRAMS_NEED_X += test-dump-split-index
|
||||
TEST_PROGRAMS_NEED_X += test-genrandom
|
||||
TEST_PROGRAMS_NEED_X += test-hashmap
|
||||
TEST_PROGRAMS_NEED_X += test-index-version
|
||||
@ -875,6 +876,7 @@ LIB_OBJS += sha1_name.o
|
||||
LIB_OBJS += shallow.o
|
||||
LIB_OBJS += sideband.o
|
||||
LIB_OBJS += sigchain.o
|
||||
LIB_OBJS += split-index.o
|
||||
LIB_OBJS += strbuf.o
|
||||
LIB_OBJS += streaming.o
|
||||
LIB_OBJS += string-list.o
|
||||
|
@ -299,7 +299,6 @@ static int add_files(struct dir_struct *dir, int flags)
|
||||
int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int exit_status = 0;
|
||||
int newfd;
|
||||
struct pathspec pathspec;
|
||||
struct dir_struct dir;
|
||||
int flags;
|
||||
@ -345,7 +344,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
add_new_files = !take_worktree_changes && !refresh_only;
|
||||
require_pathspec = !take_worktree_changes;
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
hold_locked_index(&lock_file, 1);
|
||||
|
||||
flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
|
||||
(show_only ? ADD_CACHE_PRETEND : 0) |
|
||||
@ -443,8 +442,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
|
||||
finish:
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(&lock_file))
|
||||
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
||||
die(_("Unable to write new index file"));
|
||||
}
|
||||
|
||||
|
@ -3084,13 +3084,15 @@ static void prepare_fn_table(struct patch *patch)
|
||||
}
|
||||
}
|
||||
|
||||
static int checkout_target(struct cache_entry *ce, struct stat *st)
|
||||
static int checkout_target(struct index_state *istate,
|
||||
struct cache_entry *ce, struct stat *st)
|
||||
{
|
||||
struct checkout costate;
|
||||
|
||||
memset(&costate, 0, sizeof(costate));
|
||||
costate.base_dir = "";
|
||||
costate.refresh_cache = 1;
|
||||
costate.istate = istate;
|
||||
if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
|
||||
return error(_("cannot checkout %s"), ce->name);
|
||||
return 0;
|
||||
@ -3257,7 +3259,7 @@ static int load_current(struct image *image, struct patch *patch)
|
||||
if (lstat(name, &st)) {
|
||||
if (errno != ENOENT)
|
||||
return error(_("%s: %s"), name, strerror(errno));
|
||||
if (checkout_target(ce, &st))
|
||||
if (checkout_target(&the_index, ce, &st))
|
||||
return -1;
|
||||
}
|
||||
if (verify_index_match(ce, &st))
|
||||
@ -3411,7 +3413,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
|
||||
}
|
||||
*ce = active_cache[pos];
|
||||
if (stat_ret < 0) {
|
||||
if (checkout_target(*ce, st))
|
||||
if (checkout_target(&the_index, *ce, st))
|
||||
return -1;
|
||||
}
|
||||
if (!cached && verify_index_match(*ce, st))
|
||||
@ -3644,7 +3646,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
|
||||
{
|
||||
struct patch *patch;
|
||||
struct index_state result = { NULL };
|
||||
int fd;
|
||||
static struct lock_file lock;
|
||||
|
||||
/* Once we start supporting the reverse patch, it may be
|
||||
* worth showing the new sha1 prefix, but until then...
|
||||
@ -3682,8 +3684,8 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
|
||||
die ("Could not add %s to temporary index", name);
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT, 0666);
|
||||
if (fd < 0 || write_index(&result, fd) || close(fd))
|
||||
hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
|
||||
if (write_locked_index(&result, &lock, COMMIT_LOCK))
|
||||
die ("Could not write temporary index to %s", filename);
|
||||
|
||||
discard_index(&result);
|
||||
@ -4502,8 +4504,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
|
||||
}
|
||||
|
||||
if (update_index) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(&lock_file))
|
||||
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
||||
die(_("Unable to write new index file"));
|
||||
}
|
||||
|
||||
|
@ -2389,7 +2389,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
|
||||
* right now, but someday we might optimize diff-index --cached
|
||||
* with cache-tree information.
|
||||
*/
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
cache_tree_invalidate_path(&the_index, path);
|
||||
|
||||
return commit;
|
||||
}
|
||||
|
@ -135,6 +135,7 @@ static int option_parse_u(const struct option *opt,
|
||||
int *newfd = opt->value;
|
||||
|
||||
state.refresh_cache = 1;
|
||||
state.istate = &the_index;
|
||||
if (*newfd < 0)
|
||||
*newfd = hold_locked_index(&lock_file, 1);
|
||||
return 0;
|
||||
@ -279,8 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
|
||||
checkout_all(prefix, prefix_length);
|
||||
|
||||
if (0 <= newfd &&
|
||||
(write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(&lock_file)))
|
||||
write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
||||
die("Unable to write new index file");
|
||||
return 0;
|
||||
}
|
||||
|
@ -225,7 +225,6 @@ static int checkout_paths(const struct checkout_opts *opts,
|
||||
int flag;
|
||||
struct commit *head;
|
||||
int errs = 0;
|
||||
int newfd;
|
||||
struct lock_file *lock_file;
|
||||
|
||||
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
|
||||
@ -256,7 +255,7 @@ static int checkout_paths(const struct checkout_opts *opts,
|
||||
|
||||
lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
newfd = hold_locked_index(lock_file, 1);
|
||||
hold_locked_index(lock_file, 1);
|
||||
if (read_cache_preload(&opts->pathspec) < 0)
|
||||
return error(_("corrupt index file"));
|
||||
|
||||
@ -337,6 +336,7 @@ static int checkout_paths(const struct checkout_opts *opts,
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.force = 1;
|
||||
state.refresh_cache = 1;
|
||||
state.istate = &the_index;
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
struct cache_entry *ce = active_cache[pos];
|
||||
if (ce->ce_flags & CE_MATCHED) {
|
||||
@ -352,8 +352,7 @@ static int checkout_paths(const struct checkout_opts *opts,
|
||||
}
|
||||
}
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
|
||||
die(_("unable to write new index file"));
|
||||
|
||||
read_ref_full("HEAD", rev, 0, &flag);
|
||||
@ -444,8 +443,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
|
||||
{
|
||||
int ret;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
int newfd = hold_locked_index(lock_file, 1);
|
||||
|
||||
hold_locked_index(lock_file, 1);
|
||||
if (read_cache_preload(NULL) < 0)
|
||||
return error(_("corrupt index file"));
|
||||
|
||||
@ -553,8 +552,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
|
||||
}
|
||||
}
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
|
||||
die(_("unable to write new index file"));
|
||||
|
||||
if (!opts->force && !opts->quiet)
|
||||
|
@ -617,7 +617,7 @@ static int checkout(void)
|
||||
struct unpack_trees_options opts;
|
||||
struct tree *tree;
|
||||
struct tree_desc t;
|
||||
int err = 0, fd;
|
||||
int err = 0;
|
||||
|
||||
if (option_no_checkout)
|
||||
return 0;
|
||||
@ -641,7 +641,7 @@ static int checkout(void)
|
||||
setup_work_tree();
|
||||
|
||||
lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
fd = hold_locked_index(lock_file, 1);
|
||||
hold_locked_index(lock_file, 1);
|
||||
|
||||
memset(&opts, 0, sizeof opts);
|
||||
opts.update = 1;
|
||||
@ -657,8 +657,7 @@ static int checkout(void)
|
||||
if (unpack_trees(1, &t, &opts) < 0)
|
||||
die(_("unable to checkout working tree"));
|
||||
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
|
||||
die(_("unable to write new index file"));
|
||||
|
||||
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
|
||||
|
@ -305,7 +305,6 @@ static void refresh_cache_or_die(int refresh_flags)
|
||||
static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
const struct commit *current_head, int is_status)
|
||||
{
|
||||
int fd;
|
||||
struct string_list partial;
|
||||
struct pathspec pathspec;
|
||||
int refresh_flags = REFRESH_QUIET;
|
||||
@ -321,12 +320,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
|
||||
if (interactive) {
|
||||
char *old_index_env = NULL;
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
hold_locked_index(&index_lock, 1);
|
||||
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
close_lock_file(&index_lock))
|
||||
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
|
||||
die(_("unable to create temporary index"));
|
||||
|
||||
old_index_env = getenv(INDEX_ENVIRONMENT);
|
||||
@ -360,12 +358,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
* (B) on failure, rollback the real index.
|
||||
*/
|
||||
if (all || (also && pathspec.nr)) {
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
hold_locked_index(&index_lock, 1);
|
||||
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
update_main_cache_tree(WRITE_TREE_SILENT);
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
close_lock_file(&index_lock))
|
||||
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
|
||||
die(_("unable to write new_index file"));
|
||||
commit_style = COMMIT_NORMAL;
|
||||
return index_lock.filename;
|
||||
@ -381,12 +378,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
* We still need to refresh the index here.
|
||||
*/
|
||||
if (!only && !pathspec.nr) {
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
hold_locked_index(&index_lock, 1);
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
if (active_cache_changed) {
|
||||
update_main_cache_tree(WRITE_TREE_SILENT);
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(&index_lock))
|
||||
if (write_locked_index(&the_index, &index_lock,
|
||||
COMMIT_LOCK))
|
||||
die(_("unable to write new_index file"));
|
||||
} else {
|
||||
rollback_lock_file(&index_lock);
|
||||
@ -432,24 +429,22 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
if (read_cache() < 0)
|
||||
die(_("cannot read the index"));
|
||||
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
hold_locked_index(&index_lock, 1);
|
||||
add_remove_files(&partial);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
close_lock_file(&index_lock))
|
||||
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
|
||||
die(_("unable to write new_index file"));
|
||||
|
||||
fd = hold_lock_file_for_update(&false_lock,
|
||||
git_path("next-index-%"PRIuMAX,
|
||||
(uintmax_t) getpid()),
|
||||
LOCK_DIE_ON_ERROR);
|
||||
hold_lock_file_for_update(&false_lock,
|
||||
git_path("next-index-%"PRIuMAX,
|
||||
(uintmax_t) getpid()),
|
||||
LOCK_DIE_ON_ERROR);
|
||||
|
||||
create_base_index(current_head);
|
||||
add_remove_files(&partial);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
close_lock_file(&false_lock))
|
||||
if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
|
||||
die(_("unable to write temporary index file"));
|
||||
|
||||
discard_cache();
|
||||
|
@ -657,14 +657,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
|
||||
struct commit_list *remoteheads,
|
||||
struct commit *head, const char *head_arg)
|
||||
{
|
||||
int index_fd;
|
||||
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
index_fd = hold_locked_index(lock, 1);
|
||||
hold_locked_index(lock, 1);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
if (active_cache_changed &&
|
||||
(write_cache(index_fd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock)))
|
||||
write_locked_index(&the_index, lock, COMMIT_LOCK))
|
||||
return error(_("Unable to write index."));
|
||||
rollback_lock_file(lock);
|
||||
|
||||
@ -672,7 +670,6 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
|
||||
int clean, x;
|
||||
struct commit *result;
|
||||
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
||||
int index_fd;
|
||||
struct commit_list *reversed = NULL;
|
||||
struct merge_options o;
|
||||
struct commit_list *j;
|
||||
@ -700,12 +697,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
|
||||
for (j = common; j; j = j->next)
|
||||
commit_list_insert(j->item, &reversed);
|
||||
|
||||
index_fd = hold_locked_index(lock, 1);
|
||||
hold_locked_index(lock, 1);
|
||||
clean = merge_recursive(&o, head,
|
||||
remoteheads->item, reversed, &result);
|
||||
if (active_cache_changed &&
|
||||
(write_cache(index_fd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock)))
|
||||
write_locked_index(&the_index, lock, COMMIT_LOCK))
|
||||
die (_("unable to write %s"), get_index_file());
|
||||
rollback_lock_file(lock);
|
||||
return clean ? 0 : 1;
|
||||
|
@ -63,7 +63,7 @@ static struct lock_file lock_file;
|
||||
|
||||
int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, newfd, gitmodules_modified = 0;
|
||||
int i, gitmodules_modified = 0;
|
||||
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
|
||||
struct option builtin_mv_options[] = {
|
||||
OPT__VERBOSE(&verbose, N_("be verbose")),
|
||||
@ -85,7 +85,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
if (--argc < 1)
|
||||
usage_with_options(builtin_mv_usage, builtin_mv_options);
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
hold_locked_index(&lock_file, 1);
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
|
||||
@ -276,8 +276,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
stage_updated_gitmodules();
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(&lock_file))
|
||||
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
||||
die(_("Unable to write new index file"));
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ static struct lock_file lock_file;
|
||||
|
||||
int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
{
|
||||
int i, newfd, stage = 0;
|
||||
int i, stage = 0;
|
||||
unsigned char sha1[20];
|
||||
struct tree_desc t[MAX_UNPACK_TREES];
|
||||
struct unpack_trees_options opts;
|
||||
@ -149,12 +149,21 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
argc = parse_options(argc, argv, unused_prefix, read_tree_options,
|
||||
read_tree_usage, 0);
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
hold_locked_index(&lock_file, 1);
|
||||
|
||||
prefix_set = opts.prefix ? 1 : 0;
|
||||
if (1 < opts.merge + opts.reset + prefix_set)
|
||||
die("Which one? -m, --reset, or --prefix?");
|
||||
|
||||
/*
|
||||
* NEEDSWORK
|
||||
*
|
||||
* The old index should be read anyway even if we're going to
|
||||
* destroy all index entries because we still need to preserve
|
||||
* certain information such as index version or split-index
|
||||
* mode.
|
||||
*/
|
||||
|
||||
if (opts.reset || opts.merge || opts.prefix) {
|
||||
if (read_cache_unmerged() && (opts.prefix || opts.merge))
|
||||
die("You need to resolve your current index first");
|
||||
@ -231,10 +240,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
* what came from the tree.
|
||||
*/
|
||||
if (nr_trees == 1 && !opts.prefix)
|
||||
prime_cache_tree(&active_cache_tree, trees[0]);
|
||||
prime_cache_tree(&the_index, trees[0]);
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(&lock_file))
|
||||
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
||||
die("unable to write new index file");
|
||||
return 0;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
|
||||
|
||||
if (reset_type == MIXED || reset_type == HARD) {
|
||||
tree = parse_tree_indirect(sha1);
|
||||
prime_cache_tree(&active_cache_tree, tree);
|
||||
prime_cache_tree(&the_index, tree);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -353,7 +353,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (reset_type != SOFT) {
|
||||
struct lock_file *lock = xcalloc(1, sizeof(*lock));
|
||||
int newfd = hold_locked_index(lock, 1);
|
||||
hold_locked_index(lock, 1);
|
||||
if (reset_type == MIXED) {
|
||||
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
|
||||
if (read_from_tree(&pathspec, sha1, intent_to_add))
|
||||
@ -369,8 +369,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
die(_("Could not reset index file to revision '%s'."), rev);
|
||||
}
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock))
|
||||
if (write_locked_index(&the_index, lock, COMMIT_LOCK))
|
||||
die(_("Could not write new index file."));
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "parse-options.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "split-index.h"
|
||||
|
||||
#define DO_REVS 1
|
||||
#define DO_NOREV 2
|
||||
@ -775,6 +776,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
|
||||
: "false");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--shared-index-path")) {
|
||||
if (read_cache() < 0)
|
||||
die(_("Could not read the index"));
|
||||
if (the_index.split_index) {
|
||||
const unsigned char *sha1 = the_index.split_index->base_sha1;
|
||||
puts(git_path("sharedindex.%s", sha1_to_hex(sha1)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (starts_with(arg, "--since=")) {
|
||||
show_datestring("--max-age=", arg+8);
|
||||
continue;
|
||||
|
@ -278,7 +278,7 @@ static struct option builtin_rm_options[] = {
|
||||
|
||||
int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, newfd;
|
||||
int i;
|
||||
struct pathspec pathspec;
|
||||
char *seen;
|
||||
|
||||
@ -293,7 +293,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
if (!index_only)
|
||||
setup_work_tree();
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
hold_locked_index(&lock_file, 1);
|
||||
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
@ -427,8 +427,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(&lock_file))
|
||||
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
||||
die(_("Unable to write new index file"));
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "parse-options.h"
|
||||
#include "pathspec.h"
|
||||
#include "dir.h"
|
||||
#include "split-index.h"
|
||||
|
||||
/*
|
||||
* Default to not allowing changes to the list of files. The
|
||||
@ -55,8 +56,9 @@ static int mark_ce_flags(const char *path, int flag, int mark)
|
||||
active_cache[pos]->ce_flags |= flag;
|
||||
else
|
||||
active_cache[pos]->ce_flags &= ~flag;
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
active_cache_changed = 1;
|
||||
active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
cache_tree_invalidate_path(&the_index, path);
|
||||
active_cache_changed |= CE_ENTRY_CHANGED;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
@ -267,8 +269,9 @@ static void chmod_path(int flip, const char *path)
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
active_cache_changed = 1;
|
||||
cache_tree_invalidate_path(&the_index, path);
|
||||
ce->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
active_cache_changed |= CE_ENTRY_CHANGED;
|
||||
report("chmod %cx '%s'", flip, path);
|
||||
return;
|
||||
fail:
|
||||
@ -743,6 +746,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
char set_executable_bit = 0;
|
||||
struct refresh_params refresh_args = {0, &has_errors};
|
||||
int lock_error = 0;
|
||||
int split_index = -1;
|
||||
struct lock_file *lock_file;
|
||||
struct parse_opt_ctx_t ctx;
|
||||
int parseopt_state = PARSE_OPT_UNKNOWN;
|
||||
@ -825,6 +829,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
resolve_undo_clear_callback},
|
||||
OPT_INTEGER(0, "index-version", &preferred_index_format,
|
||||
N_("write index in this format")),
|
||||
OPT_BOOL(0, "split-index", &split_index,
|
||||
N_("enable or disable split index")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@ -892,7 +898,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
INDEX_FORMAT_LB, INDEX_FORMAT_UB);
|
||||
|
||||
if (the_index.version != preferred_index_format)
|
||||
active_cache_changed = 1;
|
||||
active_cache_changed |= SOMETHING_CHANGED;
|
||||
the_index.version = preferred_index_format;
|
||||
}
|
||||
|
||||
@ -918,14 +924,27 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
if (split_index > 0) {
|
||||
init_split_index(&the_index);
|
||||
the_index.cache_changed |= SPLIT_INDEX_ORDERED;
|
||||
} else if (!split_index && the_index.split_index) {
|
||||
/*
|
||||
* can't discard_split_index(&the_index); because that
|
||||
* will destroy split_index->base->cache[], which may
|
||||
* be shared with the_index.cache[]. So yeah we're
|
||||
* leaking a bit here.
|
||||
*/
|
||||
the_index.split_index = NULL;
|
||||
the_index.cache_changed |= SOMETHING_CHANGED;
|
||||
}
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (newfd < 0) {
|
||||
if (refresh_args.flags & REFRESH_QUIET)
|
||||
exit(128);
|
||||
unable_to_lock_index_die(get_index_file(), lock_error);
|
||||
}
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
|
||||
die("Unable to write new index file");
|
||||
}
|
||||
|
||||
|
52
cache-tree.c
52
cache-tree.c
@ -98,7 +98,7 @@ struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
|
||||
return find_subtree(it, path, pathlen, 1);
|
||||
}
|
||||
|
||||
void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
|
||||
static int do_invalidate_path(struct cache_tree *it, const char *path)
|
||||
{
|
||||
/* a/b/c
|
||||
* ==> invalidate self
|
||||
@ -116,7 +116,7 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
|
||||
#endif
|
||||
|
||||
if (!it)
|
||||
return;
|
||||
return 0;
|
||||
slash = strchrnul(path, '/');
|
||||
namelen = slash - path;
|
||||
it->entry_count = -1;
|
||||
@ -137,14 +137,21 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
|
||||
(it->subtree_nr - pos - 1));
|
||||
it->subtree_nr--;
|
||||
}
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
down = find_subtree(it, path, namelen, 0);
|
||||
if (down)
|
||||
cache_tree_invalidate_path(down->cache_tree, slash + 1);
|
||||
do_invalidate_path(down->cache_tree, slash + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int verify_cache(const struct cache_entry * const *cache,
|
||||
void cache_tree_invalidate_path(struct index_state *istate, const char *path)
|
||||
{
|
||||
if (do_invalidate_path(istate->cache_tree, path))
|
||||
istate->cache_changed |= CACHE_TREE_CHANGED;
|
||||
}
|
||||
|
||||
static int verify_cache(struct cache_entry **cache,
|
||||
int entries, int flags)
|
||||
{
|
||||
int i, funny;
|
||||
@ -229,7 +236,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
|
||||
}
|
||||
|
||||
static int update_one(struct cache_tree *it,
|
||||
const struct cache_entry * const *cache,
|
||||
struct cache_entry **cache,
|
||||
int entries,
|
||||
const char *base,
|
||||
int baselen,
|
||||
@ -391,18 +398,19 @@ static int update_one(struct cache_tree *it,
|
||||
return i;
|
||||
}
|
||||
|
||||
int cache_tree_update(struct cache_tree *it,
|
||||
const struct cache_entry * const *cache,
|
||||
int entries,
|
||||
int flags)
|
||||
int cache_tree_update(struct index_state *istate, int flags)
|
||||
{
|
||||
int i, skip;
|
||||
i = verify_cache(cache, entries, flags);
|
||||
struct cache_tree *it = istate->cache_tree;
|
||||
struct cache_entry **cache = istate->cache;
|
||||
int entries = istate->cache_nr;
|
||||
int skip, i = verify_cache(cache, entries, flags);
|
||||
|
||||
if (i)
|
||||
return i;
|
||||
i = update_one(it, cache, entries, "", 0, &skip, flags);
|
||||
if (i < 0)
|
||||
return i;
|
||||
istate->cache_changed |= CACHE_TREE_CHANGED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -590,13 +598,10 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
|
||||
|
||||
was_valid = cache_tree_fully_valid(active_cache_tree);
|
||||
if (!was_valid) {
|
||||
if (cache_tree_update(active_cache_tree,
|
||||
(const struct cache_entry * const *)active_cache,
|
||||
active_nr, flags) < 0)
|
||||
if (cache_tree_update(&the_index, flags) < 0)
|
||||
return WRITE_TREE_UNMERGED_INDEX;
|
||||
if (0 <= newfd) {
|
||||
if (!write_cache(newfd, active_cache, active_nr) &&
|
||||
!commit_lock_file(lock_file))
|
||||
if (!write_locked_index(&the_index, lock_file, COMMIT_LOCK))
|
||||
newfd = -1;
|
||||
}
|
||||
/* Not being able to write is fine -- we are only interested
|
||||
@ -649,11 +654,12 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
|
||||
it->entry_count = cnt;
|
||||
}
|
||||
|
||||
void prime_cache_tree(struct cache_tree **it, struct tree *tree)
|
||||
void prime_cache_tree(struct index_state *istate, struct tree *tree)
|
||||
{
|
||||
cache_tree_free(it);
|
||||
*it = cache_tree();
|
||||
prime_cache_tree_rec(*it, tree);
|
||||
cache_tree_free(&istate->cache_tree);
|
||||
istate->cache_tree = cache_tree();
|
||||
prime_cache_tree_rec(istate->cache_tree, tree);
|
||||
istate->cache_changed |= CACHE_TREE_CHANGED;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -692,7 +698,5 @@ int update_main_cache_tree(int flags)
|
||||
{
|
||||
if (!the_index.cache_tree)
|
||||
the_index.cache_tree = cache_tree();
|
||||
return cache_tree_update(the_index.cache_tree,
|
||||
(const struct cache_entry * const *)the_index.cache,
|
||||
the_index.cache_nr, flags);
|
||||
return cache_tree_update(&the_index, flags);
|
||||
}
|
||||
|
@ -23,14 +23,14 @@ struct cache_tree {
|
||||
|
||||
struct cache_tree *cache_tree(void);
|
||||
void cache_tree_free(struct cache_tree **);
|
||||
void cache_tree_invalidate_path(struct cache_tree *, const char *);
|
||||
void cache_tree_invalidate_path(struct index_state *, const char *);
|
||||
struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
|
||||
|
||||
void cache_tree_write(struct strbuf *, struct cache_tree *root);
|
||||
struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
|
||||
|
||||
int cache_tree_fully_valid(struct cache_tree *);
|
||||
int cache_tree_update(struct cache_tree *, const struct cache_entry * const *, int, int);
|
||||
int cache_tree_update(struct index_state *, int);
|
||||
|
||||
int update_main_cache_tree(int);
|
||||
|
||||
@ -46,7 +46,7 @@ int update_main_cache_tree(int);
|
||||
#define WRITE_TREE_PREFIX_ERROR (-3)
|
||||
|
||||
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);
|
||||
void prime_cache_tree(struct cache_tree **, struct tree *);
|
||||
void prime_cache_tree(struct index_state *, struct tree *);
|
||||
|
||||
extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
|
||||
|
||||
|
27
cache.h
27
cache.h
@ -150,6 +150,7 @@ struct cache_entry {
|
||||
unsigned int ce_mode;
|
||||
unsigned int ce_flags;
|
||||
unsigned int ce_namelen;
|
||||
unsigned int index; /* for link extension */
|
||||
unsigned char sha1[20];
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
@ -160,7 +161,7 @@ struct cache_entry {
|
||||
#define CE_STAGESHIFT 12
|
||||
|
||||
/*
|
||||
* Range 0xFFFF0000 in ce_flags is divided into
|
||||
* Range 0xFFFF0FFF in ce_flags is divided into
|
||||
* two parts: in-memory flags and on-disk ones.
|
||||
* Flags in CE_EXTENDED_FLAGS will get saved on-disk
|
||||
* if you want to save a new flag, add it in
|
||||
@ -183,6 +184,9 @@ struct cache_entry {
|
||||
/* used to temporarily mark paths matched by pathspecs */
|
||||
#define CE_MATCHED (1 << 26)
|
||||
|
||||
#define CE_UPDATE_IN_BASE (1 << 27)
|
||||
#define CE_STRIP_NAME (1 << 28)
|
||||
|
||||
/*
|
||||
* Extended on-disk flags
|
||||
*/
|
||||
@ -283,12 +287,22 @@ static inline unsigned int canon_mode(unsigned int mode)
|
||||
|
||||
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
|
||||
|
||||
#define SOMETHING_CHANGED (1 << 0) /* unclassified changes go here */
|
||||
#define CE_ENTRY_CHANGED (1 << 1)
|
||||
#define CE_ENTRY_REMOVED (1 << 2)
|
||||
#define CE_ENTRY_ADDED (1 << 3)
|
||||
#define RESOLVE_UNDO_CHANGED (1 << 4)
|
||||
#define CACHE_TREE_CHANGED (1 << 5)
|
||||
#define SPLIT_INDEX_ORDERED (1 << 6)
|
||||
|
||||
struct split_index;
|
||||
struct index_state {
|
||||
struct cache_entry **cache;
|
||||
unsigned int version;
|
||||
unsigned int cache_nr, cache_alloc, cache_changed;
|
||||
struct string_list *resolve_undo;
|
||||
struct cache_tree *cache_tree;
|
||||
struct split_index *split_index;
|
||||
struct cache_time timestamp;
|
||||
unsigned name_hash_initialized : 1,
|
||||
initialized : 1;
|
||||
@ -317,7 +331,6 @@ extern void free_name_hash(struct index_state *istate);
|
||||
#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
|
||||
#define is_cache_unborn() is_index_unborn(&the_index)
|
||||
#define read_cache_unmerged() read_index_unmerged(&the_index)
|
||||
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
|
||||
#define discard_cache() discard_index(&the_index)
|
||||
#define unmerged_cache() unmerged_index(&the_index)
|
||||
#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
|
||||
@ -472,12 +485,17 @@ extern int daemonize(void);
|
||||
} while (0)
|
||||
|
||||
/* Initialize and use the cache information */
|
||||
struct lock_file;
|
||||
extern int read_index(struct index_state *);
|
||||
extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
|
||||
extern int do_read_index(struct index_state *istate, const char *path,
|
||||
int must_exist); /* for testting only! */
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int is_index_unborn(struct index_state *);
|
||||
extern int read_index_unmerged(struct index_state *);
|
||||
extern int write_index(struct index_state *, int newfd);
|
||||
#define COMMIT_LOCK (1 << 0)
|
||||
#define CLOSE_LOCK (1 << 1)
|
||||
extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
|
||||
extern int discard_index(struct index_state *);
|
||||
extern int unmerged_index(const struct index_state *);
|
||||
extern int verify_path(const char *path);
|
||||
@ -489,6 +507,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
|
||||
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
|
||||
#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */
|
||||
#define ADD_CACHE_NEW_ONLY 16 /* Do not replace existing ones */
|
||||
#define ADD_CACHE_KEEP_CACHE_TREE 32 /* Do not invalidate cache-tree */
|
||||
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
|
||||
extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
|
||||
extern int remove_index_entry_at(struct index_state *, int pos);
|
||||
@ -566,7 +585,6 @@ extern int commit_lock_file(struct lock_file *);
|
||||
extern void update_index_if_able(struct index_state *, struct lock_file *);
|
||||
|
||||
extern int hold_locked_index(struct lock_file *, int);
|
||||
extern int commit_locked_index(struct lock_file *);
|
||||
extern void set_alternate_index_output(const char *);
|
||||
extern int close_lock_file(struct lock_file *);
|
||||
extern void rollback_lock_file(struct lock_file *);
|
||||
@ -1078,6 +1096,7 @@ const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
|
||||
extern int ident_cmp(const struct ident_split *, const struct ident_split *);
|
||||
|
||||
struct checkout {
|
||||
struct index_state *istate;
|
||||
const char *base_dir;
|
||||
int base_dir_len;
|
||||
unsigned force:1,
|
||||
|
3
entry.c
3
entry.c
@ -210,9 +210,12 @@ static int write_entry(struct cache_entry *ce,
|
||||
|
||||
finish:
|
||||
if (state->refresh_cache) {
|
||||
assert(state->istate);
|
||||
if (!fstat_done)
|
||||
lstat(ce->name, &st);
|
||||
fill_stat_cache_info(ce, &st);
|
||||
ce->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
state->istate->cache_changed |= CE_ENTRY_CHANGED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -110,9 +110,9 @@ int ewah_serialize(struct ewah_bitmap *self, int fd)
|
||||
return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
|
||||
}
|
||||
|
||||
int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)
|
||||
int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
|
||||
{
|
||||
uint8_t *ptr = map;
|
||||
const uint8_t *ptr = map;
|
||||
size_t i;
|
||||
|
||||
self->bit_size = get_be32(ptr);
|
||||
|
@ -99,8 +99,7 @@ int ewah_serialize(struct ewah_bitmap *self, int fd);
|
||||
int ewah_serialize_native(struct ewah_bitmap *self, int fd);
|
||||
|
||||
int ewah_deserialize(struct ewah_bitmap *self, int fd);
|
||||
int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len);
|
||||
int ewah_read_mmap_native(struct ewah_bitmap *self, void *map, size_t len);
|
||||
int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
|
||||
|
||||
uint32_t ewah_checksum(struct ewah_bitmap *self);
|
||||
|
||||
|
20
lockfile.c
20
lockfile.c
@ -5,7 +5,6 @@
|
||||
#include "sigchain.h"
|
||||
|
||||
static struct lock_file *lock_file_list;
|
||||
static const char *alternate_index_output;
|
||||
|
||||
static void remove_lock_file(void)
|
||||
{
|
||||
@ -252,25 +251,6 @@ int hold_locked_index(struct lock_file *lk, int die_on_error)
|
||||
: 0);
|
||||
}
|
||||
|
||||
void set_alternate_index_output(const char *name)
|
||||
{
|
||||
alternate_index_output = name;
|
||||
}
|
||||
|
||||
int commit_locked_index(struct lock_file *lk)
|
||||
{
|
||||
if (alternate_index_output) {
|
||||
if (lk->fd >= 0 && close_lock_file(lk))
|
||||
return -1;
|
||||
if (rename(lk->filename, alternate_index_output))
|
||||
return -1;
|
||||
lk->filename[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return commit_lock_file(lk);
|
||||
}
|
||||
|
||||
void rollback_lock_file(struct lock_file *lk)
|
||||
{
|
||||
if (lk->filename[0]) {
|
||||
|
@ -267,9 +267,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
|
||||
active_cache_tree = cache_tree();
|
||||
|
||||
if (!cache_tree_fully_valid(active_cache_tree) &&
|
||||
cache_tree_update(active_cache_tree,
|
||||
(const struct cache_entry * const *)active_cache,
|
||||
active_nr, 0) < 0)
|
||||
cache_tree_update(&the_index, 0) < 0)
|
||||
die(_("error building trees"));
|
||||
|
||||
result = lookup_tree(active_cache_tree->sha1);
|
||||
@ -2001,7 +1999,7 @@ int merge_recursive_generic(struct merge_options *o,
|
||||
const unsigned char **base_list,
|
||||
struct commit **result)
|
||||
{
|
||||
int clean, index_fd;
|
||||
int clean;
|
||||
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
||||
struct commit *head_commit = get_ref(head, o->branch1);
|
||||
struct commit *next_commit = get_ref(merge, o->branch2);
|
||||
@ -2018,12 +2016,11 @@ int merge_recursive_generic(struct merge_options *o,
|
||||
}
|
||||
}
|
||||
|
||||
index_fd = hold_locked_index(lock, 1);
|
||||
hold_locked_index(lock, 1);
|
||||
clean = merge_recursive(o, head_commit, next_commit, ca,
|
||||
result);
|
||||
if (active_cache_changed &&
|
||||
(write_cache(index_fd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock)))
|
||||
write_locked_index(&the_index, lock, COMMIT_LOCK))
|
||||
return error(_("Unable to write index."));
|
||||
|
||||
return clean ? 0 : 1;
|
||||
|
7
merge.c
7
merge.c
@ -50,13 +50,13 @@ int checkout_fast_forward(const unsigned char *head,
|
||||
struct tree *trees[MAX_UNPACK_TREES];
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc t[MAX_UNPACK_TREES];
|
||||
int i, fd, nr_trees = 0;
|
||||
int i, nr_trees = 0;
|
||||
struct dir_struct dir;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
fd = hold_locked_index(lock_file, 1);
|
||||
hold_locked_index(lock_file, 1);
|
||||
|
||||
memset(&trees, 0, sizeof(trees));
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
@ -89,8 +89,7 @@ int checkout_fast_forward(const unsigned char *head,
|
||||
}
|
||||
if (unpack_trees(nr_trees, t, &opts))
|
||||
return -1;
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
|
||||
die(_("unable to write new index file"));
|
||||
return 0;
|
||||
}
|
||||
|
272
read-cache.c
272
read-cache.c
@ -14,6 +14,8 @@
|
||||
#include "resolve-undo.h"
|
||||
#include "strbuf.h"
|
||||
#include "varint.h"
|
||||
#include "split-index.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
|
||||
unsigned int options);
|
||||
@ -34,8 +36,15 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
|
||||
#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
|
||||
#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
|
||||
#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
|
||||
#define CACHE_EXT_LINK 0x6c696e6b /* "link" */
|
||||
|
||||
/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
|
||||
#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
|
||||
CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
|
||||
SPLIT_INDEX_ORDERED)
|
||||
|
||||
struct index_state the_index;
|
||||
static const char *alternate_index_output;
|
||||
|
||||
static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
|
||||
{
|
||||
@ -47,10 +56,12 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
|
||||
{
|
||||
struct cache_entry *old = istate->cache[nr];
|
||||
|
||||
replace_index_entry_in_base(istate, old, ce);
|
||||
remove_name_hash(istate, old);
|
||||
free(old);
|
||||
set_index_entry(istate, nr, ce);
|
||||
istate->cache_changed = 1;
|
||||
ce->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
istate->cache_changed |= CE_ENTRY_CHANGED;
|
||||
}
|
||||
|
||||
void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)
|
||||
@ -62,9 +73,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
|
||||
copy_cache_entry(new, old);
|
||||
new->ce_flags &= ~CE_HASHED;
|
||||
new->ce_namelen = namelen;
|
||||
new->index = 0;
|
||||
memcpy(new->name, new_name, namelen + 1);
|
||||
|
||||
cache_tree_invalidate_path(istate->cache_tree, old->name);
|
||||
cache_tree_invalidate_path(istate, old->name);
|
||||
remove_index_entry_at(istate, nr);
|
||||
add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
|
||||
}
|
||||
@ -483,8 +495,8 @@ int remove_index_entry_at(struct index_state *istate, int pos)
|
||||
|
||||
record_resolve_undo(istate, ce);
|
||||
remove_name_hash(istate, ce);
|
||||
free(ce);
|
||||
istate->cache_changed = 1;
|
||||
save_or_free_index_entry(istate, ce);
|
||||
istate->cache_changed |= CE_ENTRY_REMOVED;
|
||||
istate->cache_nr--;
|
||||
if (pos >= istate->cache_nr)
|
||||
return 0;
|
||||
@ -507,12 +519,14 @@ void remove_marked_cache_entries(struct index_state *istate)
|
||||
for (i = j = 0; i < istate->cache_nr; i++) {
|
||||
if (ce_array[i]->ce_flags & CE_REMOVE) {
|
||||
remove_name_hash(istate, ce_array[i]);
|
||||
free(ce_array[i]);
|
||||
save_or_free_index_entry(istate, ce_array[i]);
|
||||
}
|
||||
else
|
||||
ce_array[j++] = ce_array[i];
|
||||
}
|
||||
istate->cache_changed = 1;
|
||||
if (j == istate->cache_nr)
|
||||
return;
|
||||
istate->cache_changed |= CE_ENTRY_REMOVED;
|
||||
istate->cache_nr = j;
|
||||
}
|
||||
|
||||
@ -521,7 +535,7 @@ int remove_file_from_index(struct index_state *istate, const char *path)
|
||||
int pos = index_name_pos(istate, path, strlen(path));
|
||||
if (pos < 0)
|
||||
pos = -pos-1;
|
||||
cache_tree_invalidate_path(istate->cache_tree, path);
|
||||
cache_tree_invalidate_path(istate, path);
|
||||
while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path))
|
||||
remove_index_entry_at(istate, pos);
|
||||
return 0;
|
||||
@ -570,7 +584,9 @@ static int different_name(struct cache_entry *ce, struct cache_entry *alias)
|
||||
* So we use the CE_ADDED flag to verify that the alias was an old
|
||||
* one before we accept it as
|
||||
*/
|
||||
static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias)
|
||||
static struct cache_entry *create_alias_ce(struct index_state *istate,
|
||||
struct cache_entry *ce,
|
||||
struct cache_entry *alias)
|
||||
{
|
||||
int len;
|
||||
struct cache_entry *new;
|
||||
@ -583,7 +599,7 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_
|
||||
new = xcalloc(1, cache_entry_size(len));
|
||||
memcpy(new->name, alias->name, len);
|
||||
copy_cache_entry(new, ce);
|
||||
free(ce);
|
||||
save_or_free_index_entry(istate, ce);
|
||||
return new;
|
||||
}
|
||||
|
||||
@ -676,7 +692,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
|
||||
set_object_name_for_intent_to_add_entry(ce);
|
||||
|
||||
if (ignore_case && alias && different_name(ce, alias))
|
||||
ce = create_alias_ce(ce, alias);
|
||||
ce = create_alias_ce(istate, ce, alias);
|
||||
ce->ce_flags |= CE_ADDED;
|
||||
|
||||
/* It was suspected to be racily clean, but it turns out to be Ok */
|
||||
@ -939,7 +955,8 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
|
||||
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
|
||||
int new_only = option & ADD_CACHE_NEW_ONLY;
|
||||
|
||||
cache_tree_invalidate_path(istate->cache_tree, ce->name);
|
||||
if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
|
||||
cache_tree_invalidate_path(istate, ce->name);
|
||||
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
|
||||
|
||||
/* existing match? Just replace it. */
|
||||
@ -1002,7 +1019,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
|
||||
istate->cache + pos,
|
||||
(istate->cache_nr - pos - 1) * sizeof(ce));
|
||||
set_index_entry(istate, pos, ce);
|
||||
istate->cache_changed = 1;
|
||||
istate->cache_changed |= CE_ENTRY_ADDED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1101,6 +1118,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
|
||||
!(ce->ce_flags & CE_VALID))
|
||||
updated->ce_flags &= ~CE_VALID;
|
||||
|
||||
/* istate->cache_changed is updated in the caller */
|
||||
return updated;
|
||||
}
|
||||
|
||||
@ -1182,7 +1200,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
|
||||
* means the index is not valid anymore.
|
||||
*/
|
||||
ce->ce_flags &= ~CE_VALID;
|
||||
istate->cache_changed = 1;
|
||||
ce->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
istate->cache_changed |= CE_ENTRY_CHANGED;
|
||||
}
|
||||
if (quiet)
|
||||
continue;
|
||||
@ -1334,6 +1353,10 @@ static int read_index_extension(struct index_state *istate,
|
||||
case CACHE_EXT_RESOLVE_UNDO:
|
||||
istate->resolve_undo = resolve_undo_read(data, sz);
|
||||
break;
|
||||
case CACHE_EXT_LINK:
|
||||
if (read_link_extension(istate, data, sz))
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
if (*ext < 'A' || 'Z' < *ext)
|
||||
return error("index uses %.4s extension, which we do not understand",
|
||||
@ -1368,6 +1391,7 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
|
||||
ce->ce_stat_data.sd_size = get_be32(&ondisk->size);
|
||||
ce->ce_flags = flags & ~CE_NAMEMASK;
|
||||
ce->ce_namelen = len;
|
||||
ce->index = 0;
|
||||
hashcpy(ce->sha1, ondisk->sha1);
|
||||
memcpy(ce->name, name, len);
|
||||
ce->name[len] = '\0';
|
||||
@ -1442,7 +1466,7 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
|
||||
}
|
||||
|
||||
/* remember to discard_cache() before reading a different cache! */
|
||||
int read_index_from(struct index_state *istate, const char *path)
|
||||
int do_read_index(struct index_state *istate, const char *path, int must_exist)
|
||||
{
|
||||
int fd, i;
|
||||
struct stat st;
|
||||
@ -1459,9 +1483,9 @@ int read_index_from(struct index_state *istate, const char *path)
|
||||
istate->timestamp.nsec = 0;
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
if (!must_exist && errno == ENOENT)
|
||||
return 0;
|
||||
die_errno("index file open failed");
|
||||
die_errno("%s: index file open failed", path);
|
||||
}
|
||||
|
||||
if (fstat(fd, &st))
|
||||
@ -1480,7 +1504,7 @@ int read_index_from(struct index_state *istate, const char *path)
|
||||
if (verify_hdr(hdr, mmap_size) < 0)
|
||||
goto unmap;
|
||||
|
||||
hashcpy(istate->sha1, (unsigned char *)hdr + mmap_size - 20);
|
||||
hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - 20);
|
||||
istate->version = ntohl(hdr->hdr_version);
|
||||
istate->cache_nr = ntohl(hdr->hdr_entries);
|
||||
istate->cache_alloc = alloc_nr(istate->cache_nr);
|
||||
@ -1534,6 +1558,40 @@ unmap:
|
||||
die("index file corrupt");
|
||||
}
|
||||
|
||||
int read_index_from(struct index_state *istate, const char *path)
|
||||
{
|
||||
struct split_index *split_index;
|
||||
int ret;
|
||||
|
||||
/* istate->initialized covers both .git/index and .git/sharedindex.xxx */
|
||||
if (istate->initialized)
|
||||
return istate->cache_nr;
|
||||
|
||||
ret = do_read_index(istate, path, 0);
|
||||
split_index = istate->split_index;
|
||||
if (!split_index)
|
||||
return ret;
|
||||
|
||||
if (is_null_sha1(split_index->base_sha1))
|
||||
return ret;
|
||||
|
||||
if (split_index->base)
|
||||
discard_index(split_index->base);
|
||||
else
|
||||
split_index->base = xcalloc(1, sizeof(*split_index->base));
|
||||
ret = do_read_index(split_index->base,
|
||||
git_path("sharedindex.%s",
|
||||
sha1_to_hex(split_index->base_sha1)), 1);
|
||||
if (hashcmp(split_index->base_sha1, split_index->base->sha1))
|
||||
die("broken index, expect %s in %s, got %s",
|
||||
sha1_to_hex(split_index->base_sha1),
|
||||
git_path("sharedindex.%s",
|
||||
sha1_to_hex(split_index->base_sha1)),
|
||||
sha1_to_hex(split_index->base->sha1));
|
||||
merge_base_index(istate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int is_index_unborn(struct index_state *istate)
|
||||
{
|
||||
return (!istate->cache_nr && !istate->timestamp.sec);
|
||||
@ -1543,8 +1601,15 @@ int discard_index(struct index_state *istate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < istate->cache_nr; i++)
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
if (istate->cache[i]->index &&
|
||||
istate->split_index &&
|
||||
istate->split_index->base &&
|
||||
istate->cache[i]->index <= istate->split_index->base->cache_nr &&
|
||||
istate->cache[i] == istate->split_index->base->cache[istate->cache[i]->index - 1])
|
||||
continue;
|
||||
free(istate->cache[i]);
|
||||
}
|
||||
resolve_undo_clear_index(istate);
|
||||
istate->cache_nr = 0;
|
||||
istate->cache_changed = 0;
|
||||
@ -1556,6 +1621,7 @@ int discard_index(struct index_state *istate)
|
||||
free(istate->cache);
|
||||
istate->cache = NULL;
|
||||
istate->cache_alloc = 0;
|
||||
discard_split_index(istate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1616,7 +1682,7 @@ static int write_index_ext_header(git_SHA_CTX *context, int fd,
|
||||
(ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int ce_flush(git_SHA_CTX *context, int fd)
|
||||
static int ce_flush(git_SHA_CTX *context, int fd, unsigned char *sha1)
|
||||
{
|
||||
unsigned int left = write_buffer_len;
|
||||
|
||||
@ -1634,6 +1700,7 @@ static int ce_flush(git_SHA_CTX *context, int fd)
|
||||
|
||||
/* Append the SHA1 signature at the end */
|
||||
git_SHA1_Final(write_buffer + left, context);
|
||||
hashcpy(sha1, write_buffer + left);
|
||||
left += 20;
|
||||
return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
|
||||
}
|
||||
@ -1705,7 +1772,7 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
|
||||
ondisk->size = htonl(ce->ce_stat_data.sd_size);
|
||||
hashcpy(ondisk->sha1, ce->sha1);
|
||||
|
||||
flags = ce->ce_flags;
|
||||
flags = ce->ce_flags & ~CE_NAMEMASK;
|
||||
flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
|
||||
ondisk->flags = htons(flags);
|
||||
if (ce->ce_flags & CE_EXTENDED) {
|
||||
@ -1724,9 +1791,15 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
|
||||
{
|
||||
int size;
|
||||
struct ondisk_cache_entry *ondisk;
|
||||
int saved_namelen = saved_namelen; /* compiler workaround */
|
||||
char *name;
|
||||
int result;
|
||||
|
||||
if (ce->ce_flags & CE_STRIP_NAME) {
|
||||
saved_namelen = ce_namelen(ce);
|
||||
ce->ce_namelen = 0;
|
||||
}
|
||||
|
||||
if (!previous_name) {
|
||||
size = ondisk_ce_size(ce);
|
||||
ondisk = xcalloc(1, size);
|
||||
@ -1758,6 +1831,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
|
||||
strbuf_splice(previous_name, common, to_remove,
|
||||
ce->name + common, ce_namelen(ce) - common);
|
||||
}
|
||||
if (ce->ce_flags & CE_STRIP_NAME) {
|
||||
ce->ce_namelen = saved_namelen;
|
||||
ce->ce_flags &= ~CE_STRIP_NAME;
|
||||
}
|
||||
|
||||
result = ce_write(c, fd, ondisk, size);
|
||||
free(ondisk);
|
||||
@ -1827,13 +1904,13 @@ static int has_racy_timestamp(struct index_state *istate)
|
||||
void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
|
||||
{
|
||||
if ((istate->cache_changed || has_racy_timestamp(istate)) &&
|
||||
verify_index(istate) && !write_index(istate, lockfile->fd))
|
||||
commit_locked_index(lockfile);
|
||||
else
|
||||
verify_index(istate) &&
|
||||
write_locked_index(istate, lockfile, COMMIT_LOCK))
|
||||
rollback_lock_file(lockfile);
|
||||
}
|
||||
|
||||
int write_index(struct index_state *istate, int newfd)
|
||||
static int do_write_index(struct index_state *istate, int newfd,
|
||||
int strip_extensions)
|
||||
{
|
||||
git_SHA_CTX c;
|
||||
struct cache_header hdr;
|
||||
@ -1855,8 +1932,11 @@ int write_index(struct index_state *istate, int newfd)
|
||||
}
|
||||
}
|
||||
|
||||
if (!istate->version)
|
||||
if (!istate->version) {
|
||||
istate->version = get_index_format_default();
|
||||
if (getenv("GIT_TEST_SPLIT_INDEX"))
|
||||
init_split_index(istate);
|
||||
}
|
||||
|
||||
/* demote version 3 to version 2 when the latter suffices */
|
||||
if (istate->version == 3 || istate->version == 2)
|
||||
@ -1896,7 +1976,18 @@ int write_index(struct index_state *istate, int newfd)
|
||||
strbuf_release(&previous_name_buf);
|
||||
|
||||
/* Write extension data here */
|
||||
if (istate->cache_tree) {
|
||||
if (!strip_extensions && istate->split_index) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
err = write_link_extension(&sb, istate) < 0 ||
|
||||
write_index_ext_header(&c, newfd, CACHE_EXT_LINK,
|
||||
sb.len) < 0 ||
|
||||
ce_write(&c, newfd, sb.buf, sb.len) < 0;
|
||||
strbuf_release(&sb);
|
||||
if (err)
|
||||
return -1;
|
||||
}
|
||||
if (!strip_extensions && istate->cache_tree) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
cache_tree_write(&sb, istate->cache_tree);
|
||||
@ -1906,7 +1997,7 @@ int write_index(struct index_state *istate, int newfd)
|
||||
if (err)
|
||||
return -1;
|
||||
}
|
||||
if (istate->resolve_undo) {
|
||||
if (!strip_extensions && istate->resolve_undo) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
resolve_undo_write(&sb, istate->resolve_undo);
|
||||
@ -1918,13 +2009,138 @@ int write_index(struct index_state *istate, int newfd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ce_flush(&c, newfd) || fstat(newfd, &st))
|
||||
if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st))
|
||||
return -1;
|
||||
istate->timestamp.sec = (unsigned int)st.st_mtime;
|
||||
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_alternate_index_output(const char *name)
|
||||
{
|
||||
alternate_index_output = name;
|
||||
}
|
||||
|
||||
static int commit_locked_index(struct lock_file *lk)
|
||||
{
|
||||
if (alternate_index_output) {
|
||||
if (lk->fd >= 0 && close_lock_file(lk))
|
||||
return -1;
|
||||
if (rename(lk->filename, alternate_index_output))
|
||||
return -1;
|
||||
lk->filename[0] = 0;
|
||||
return 0;
|
||||
} else {
|
||||
return commit_lock_file(lk);
|
||||
}
|
||||
}
|
||||
|
||||
static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
|
||||
unsigned flags)
|
||||
{
|
||||
int ret = do_write_index(istate, lock->fd, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
|
||||
(COMMIT_LOCK | CLOSE_LOCK));
|
||||
if (flags & COMMIT_LOCK)
|
||||
return commit_locked_index(lock);
|
||||
else if (flags & CLOSE_LOCK)
|
||||
return close_lock_file(lock);
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_split_index(struct index_state *istate,
|
||||
struct lock_file *lock,
|
||||
unsigned flags)
|
||||
{
|
||||
int ret;
|
||||
prepare_to_write_split_index(istate);
|
||||
ret = do_write_locked_index(istate, lock, flags);
|
||||
finish_writing_split_index(istate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *temporary_sharedindex;
|
||||
|
||||
static void remove_temporary_sharedindex(void)
|
||||
{
|
||||
if (temporary_sharedindex) {
|
||||
unlink_or_warn(temporary_sharedindex);
|
||||
free(temporary_sharedindex);
|
||||
temporary_sharedindex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_temporary_sharedindex_on_signal(int signo)
|
||||
{
|
||||
remove_temporary_sharedindex();
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
static int write_shared_index(struct index_state *istate,
|
||||
struct lock_file *lock, unsigned flags)
|
||||
{
|
||||
struct split_index *si = istate->split_index;
|
||||
static int installed_handler;
|
||||
int fd, ret;
|
||||
|
||||
temporary_sharedindex = git_pathdup("sharedindex_XXXXXX");
|
||||
fd = mkstemp(temporary_sharedindex);
|
||||
if (fd < 0) {
|
||||
free(temporary_sharedindex);
|
||||
temporary_sharedindex = NULL;
|
||||
hashclr(si->base_sha1);
|
||||
return do_write_locked_index(istate, lock, flags);
|
||||
}
|
||||
if (!installed_handler) {
|
||||
atexit(remove_temporary_sharedindex);
|
||||
sigchain_push_common(remove_temporary_sharedindex_on_signal);
|
||||
}
|
||||
move_cache_to_base_index(istate);
|
||||
ret = do_write_index(si->base, fd, 1);
|
||||
close(fd);
|
||||
if (ret) {
|
||||
remove_temporary_sharedindex();
|
||||
return ret;
|
||||
}
|
||||
ret = rename(temporary_sharedindex,
|
||||
git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
|
||||
free(temporary_sharedindex);
|
||||
temporary_sharedindex = NULL;
|
||||
if (!ret)
|
||||
hashcpy(si->base_sha1, si->base->sha1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int write_locked_index(struct index_state *istate, struct lock_file *lock,
|
||||
unsigned flags)
|
||||
{
|
||||
struct split_index *si = istate->split_index;
|
||||
|
||||
if (!si || alternate_index_output ||
|
||||
(istate->cache_changed & ~EXTMASK)) {
|
||||
if (si)
|
||||
hashclr(si->base_sha1);
|
||||
return do_write_locked_index(istate, lock, flags);
|
||||
}
|
||||
|
||||
if (getenv("GIT_TEST_SPLIT_INDEX")) {
|
||||
int v = si->base_sha1[0];
|
||||
if ((v & 15) < 6)
|
||||
istate->cache_changed |= SPLIT_INDEX_ORDERED;
|
||||
}
|
||||
if (istate->cache_changed & SPLIT_INDEX_ORDERED) {
|
||||
int ret = write_shared_index(istate, lock, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return write_split_index(istate, lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the index file that is potentially unmerged into given
|
||||
* index_state, dropping any unmerged entries. Returns true if
|
||||
|
3
rerere.c
3
rerere.c
@ -492,8 +492,7 @@ static int update_paths(struct string_list *update)
|
||||
}
|
||||
|
||||
if (!status && active_cache_changed) {
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(&index_lock))
|
||||
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
|
||||
die("Unable to write new index file");
|
||||
} else if (fd >= 0)
|
||||
rollback_lock_file(&index_lock);
|
||||
|
@ -110,7 +110,7 @@ void resolve_undo_clear_index(struct index_state *istate)
|
||||
string_list_clear(resolve_undo, 1);
|
||||
free(resolve_undo);
|
||||
istate->resolve_undo = NULL;
|
||||
istate->cache_changed = 1;
|
||||
istate->cache_changed |= RESOLVE_UNDO_CHANGED;
|
||||
}
|
||||
|
||||
int unmerge_index_entry_at(struct index_state *istate, int pos)
|
||||
|
16
sequencer.c
16
sequencer.c
@ -263,11 +263,11 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
||||
{
|
||||
struct merge_options o;
|
||||
struct tree *result, *next_tree, *base_tree, *head_tree;
|
||||
int clean, index_fd;
|
||||
int clean;
|
||||
const char **xopt;
|
||||
static struct lock_file index_lock;
|
||||
|
||||
index_fd = hold_locked_index(&index_lock, 1);
|
||||
hold_locked_index(&index_lock, 1);
|
||||
|
||||
read_cache();
|
||||
|
||||
@ -288,8 +288,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
||||
next_tree, base_tree, &result);
|
||||
|
||||
if (active_cache_changed &&
|
||||
(write_cache(index_fd, active_cache, active_nr) ||
|
||||
commit_locked_index(&index_lock)))
|
||||
write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
|
||||
/* TRANSLATORS: %s will be "revert" or "cherry-pick" */
|
||||
die(_("%s: Unable to write new index file"), action_name(opts));
|
||||
rollback_lock_file(&index_lock);
|
||||
@ -341,9 +340,7 @@ static int is_index_unchanged(void)
|
||||
active_cache_tree = cache_tree();
|
||||
|
||||
if (!cache_tree_fully_valid(active_cache_tree))
|
||||
if (cache_tree_update(active_cache_tree,
|
||||
(const struct cache_entry * const *)active_cache,
|
||||
active_nr, 0))
|
||||
if (cache_tree_update(&the_index, 0))
|
||||
return error(_("Unable to update cache tree\n"));
|
||||
|
||||
return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
|
||||
@ -643,9 +640,8 @@ static void read_and_refresh_cache(struct replay_opts *opts)
|
||||
if (read_index_preload(&the_index, NULL) < 0)
|
||||
die(_("git %s: failed to read the index"), action_name(opts));
|
||||
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
|
||||
if (the_index.cache_changed) {
|
||||
if (write_index(&the_index, index_fd) ||
|
||||
commit_locked_index(&index_lock))
|
||||
if (the_index.cache_changed && index_fd >= 0) {
|
||||
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
|
||||
die(_("git %s: failed to refresh the index"), action_name(opts));
|
||||
}
|
||||
rollback_lock_file(&index_lock);
|
||||
|
328
split-index.c
Normal file
328
split-index.c
Normal file
@ -0,0 +1,328 @@
|
||||
#include "cache.h"
|
||||
#include "split-index.h"
|
||||
#include "ewah/ewok.h"
|
||||
|
||||
struct split_index *init_split_index(struct index_state *istate)
|
||||
{
|
||||
if (!istate->split_index) {
|
||||
istate->split_index = xcalloc(1, sizeof(*istate->split_index));
|
||||
istate->split_index->refcount = 1;
|
||||
}
|
||||
return istate->split_index;
|
||||
}
|
||||
|
||||
int read_link_extension(struct index_state *istate,
|
||||
const void *data_, unsigned long sz)
|
||||
{
|
||||
const unsigned char *data = data_;
|
||||
struct split_index *si;
|
||||
int ret;
|
||||
|
||||
if (sz < 20)
|
||||
return error("corrupt link extension (too short)");
|
||||
si = init_split_index(istate);
|
||||
hashcpy(si->base_sha1, data);
|
||||
data += 20;
|
||||
sz -= 20;
|
||||
if (!sz)
|
||||
return 0;
|
||||
si->delete_bitmap = ewah_new();
|
||||
ret = ewah_read_mmap(si->delete_bitmap, data, sz);
|
||||
if (ret < 0)
|
||||
return error("corrupt delete bitmap in link extension");
|
||||
data += ret;
|
||||
sz -= ret;
|
||||
si->replace_bitmap = ewah_new();
|
||||
ret = ewah_read_mmap(si->replace_bitmap, data, sz);
|
||||
if (ret < 0)
|
||||
return error("corrupt replace bitmap in link extension");
|
||||
if (ret != sz)
|
||||
return error("garbage at the end of link extension");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_strbuf(void *user_data, const void *data, size_t len)
|
||||
{
|
||||
struct strbuf *sb = user_data;
|
||||
strbuf_add(sb, data, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
int write_link_extension(struct strbuf *sb,
|
||||
struct index_state *istate)
|
||||
{
|
||||
struct split_index *si = istate->split_index;
|
||||
strbuf_add(sb, si->base_sha1, 20);
|
||||
if (!si->delete_bitmap && !si->replace_bitmap)
|
||||
return 0;
|
||||
ewah_serialize_to(si->delete_bitmap, write_strbuf, sb);
|
||||
ewah_serialize_to(si->replace_bitmap, write_strbuf, sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark_base_index_entries(struct index_state *base)
|
||||
{
|
||||
int i;
|
||||
/*
|
||||
* To keep track of the shared entries between
|
||||
* istate->base->cache[] and istate->cache[], base entry
|
||||
* position is stored in each base entry. All positions start
|
||||
* from 1 instead of 0, which is resrved to say "this is a new
|
||||
* entry".
|
||||
*/
|
||||
for (i = 0; i < base->cache_nr; i++)
|
||||
base->cache[i]->index = i + 1;
|
||||
}
|
||||
|
||||
void move_cache_to_base_index(struct index_state *istate)
|
||||
{
|
||||
struct split_index *si = istate->split_index;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* do not delete old si->base, its index entries may be shared
|
||||
* with istate->cache[]. Accept a bit of leaking here because
|
||||
* this code is only used by short-lived update-index.
|
||||
*/
|
||||
si->base = xcalloc(1, sizeof(*si->base));
|
||||
si->base->version = istate->version;
|
||||
/* zero timestamp disables racy test in ce_write_index() */
|
||||
si->base->timestamp = istate->timestamp;
|
||||
ALLOC_GROW(si->base->cache, istate->cache_nr, si->base->cache_alloc);
|
||||
si->base->cache_nr = istate->cache_nr;
|
||||
memcpy(si->base->cache, istate->cache,
|
||||
sizeof(*istate->cache) * istate->cache_nr);
|
||||
mark_base_index_entries(si->base);
|
||||
for (i = 0; i < si->base->cache_nr; i++)
|
||||
si->base->cache[i]->ce_flags &= ~CE_UPDATE_IN_BASE;
|
||||
}
|
||||
|
||||
static void mark_entry_for_delete(size_t pos, void *data)
|
||||
{
|
||||
struct index_state *istate = data;
|
||||
if (pos >= istate->cache_nr)
|
||||
die("position for delete %d exceeds base index size %d",
|
||||
(int)pos, istate->cache_nr);
|
||||
istate->cache[pos]->ce_flags |= CE_REMOVE;
|
||||
istate->split_index->nr_deletions = 1;
|
||||
}
|
||||
|
||||
static void replace_entry(size_t pos, void *data)
|
||||
{
|
||||
struct index_state *istate = data;
|
||||
struct split_index *si = istate->split_index;
|
||||
struct cache_entry *dst, *src;
|
||||
|
||||
if (pos >= istate->cache_nr)
|
||||
die("position for replacement %d exceeds base index size %d",
|
||||
(int)pos, istate->cache_nr);
|
||||
if (si->nr_replacements >= si->saved_cache_nr)
|
||||
die("too many replacements (%d vs %d)",
|
||||
si->nr_replacements, si->saved_cache_nr);
|
||||
dst = istate->cache[pos];
|
||||
if (dst->ce_flags & CE_REMOVE)
|
||||
die("entry %d is marked as both replaced and deleted",
|
||||
(int)pos);
|
||||
src = si->saved_cache[si->nr_replacements];
|
||||
if (ce_namelen(src))
|
||||
die("corrupt link extension, entry %d should have "
|
||||
"zero length name", (int)pos);
|
||||
src->index = pos + 1;
|
||||
src->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
src->ce_namelen = dst->ce_namelen;
|
||||
copy_cache_entry(dst, src);
|
||||
free(src);
|
||||
si->nr_replacements++;
|
||||
}
|
||||
|
||||
void merge_base_index(struct index_state *istate)
|
||||
{
|
||||
struct split_index *si = istate->split_index;
|
||||
unsigned int i;
|
||||
|
||||
mark_base_index_entries(si->base);
|
||||
|
||||
si->saved_cache = istate->cache;
|
||||
si->saved_cache_nr = istate->cache_nr;
|
||||
istate->cache_nr = si->base->cache_nr;
|
||||
istate->cache = NULL;
|
||||
istate->cache_alloc = 0;
|
||||
ALLOC_GROW(istate->cache, istate->cache_nr, istate->cache_alloc);
|
||||
memcpy(istate->cache, si->base->cache,
|
||||
sizeof(*istate->cache) * istate->cache_nr);
|
||||
|
||||
si->nr_deletions = 0;
|
||||
si->nr_replacements = 0;
|
||||
ewah_each_bit(si->replace_bitmap, replace_entry, istate);
|
||||
ewah_each_bit(si->delete_bitmap, mark_entry_for_delete, istate);
|
||||
if (si->nr_deletions)
|
||||
remove_marked_cache_entries(istate);
|
||||
|
||||
for (i = si->nr_replacements; i < si->saved_cache_nr; i++) {
|
||||
if (!ce_namelen(si->saved_cache[i]))
|
||||
die("corrupt link extension, entry %d should "
|
||||
"have non-zero length name", i);
|
||||
add_index_entry(istate, si->saved_cache[i],
|
||||
ADD_CACHE_OK_TO_ADD |
|
||||
ADD_CACHE_KEEP_CACHE_TREE |
|
||||
/*
|
||||
* we may have to replay what
|
||||
* merge-recursive.c:update_stages()
|
||||
* does, which has this flag on
|
||||
*/
|
||||
ADD_CACHE_SKIP_DFCHECK);
|
||||
si->saved_cache[i] = NULL;
|
||||
}
|
||||
|
||||
ewah_free(si->delete_bitmap);
|
||||
ewah_free(si->replace_bitmap);
|
||||
free(si->saved_cache);
|
||||
si->delete_bitmap = NULL;
|
||||
si->replace_bitmap = NULL;
|
||||
si->saved_cache = NULL;
|
||||
si->saved_cache_nr = 0;
|
||||
}
|
||||
|
||||
void prepare_to_write_split_index(struct index_state *istate)
|
||||
{
|
||||
struct split_index *si = init_split_index(istate);
|
||||
struct cache_entry **entries = NULL, *ce;
|
||||
int i, nr_entries = 0, nr_alloc = 0;
|
||||
|
||||
si->delete_bitmap = ewah_new();
|
||||
si->replace_bitmap = ewah_new();
|
||||
|
||||
if (si->base) {
|
||||
/* Go through istate->cache[] and mark CE_MATCHED to
|
||||
* entry with positive index. We'll go through
|
||||
* base->cache[] later to delete all entries in base
|
||||
* that are not marked eith either CE_MATCHED or
|
||||
* CE_UPDATE_IN_BASE. If istate->cache[i] is a
|
||||
* duplicate, deduplicate it.
|
||||
*/
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
struct cache_entry *base;
|
||||
/* namelen is checked separately */
|
||||
const unsigned int ondisk_flags =
|
||||
CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
|
||||
unsigned int ce_flags, base_flags, ret;
|
||||
ce = istate->cache[i];
|
||||
if (!ce->index)
|
||||
continue;
|
||||
if (ce->index > si->base->cache_nr) {
|
||||
ce->index = 0;
|
||||
continue;
|
||||
}
|
||||
ce->ce_flags |= CE_MATCHED; /* or "shared" */
|
||||
base = si->base->cache[ce->index - 1];
|
||||
if (ce == base)
|
||||
continue;
|
||||
if (ce->ce_namelen != base->ce_namelen ||
|
||||
strcmp(ce->name, base->name)) {
|
||||
ce->index = 0;
|
||||
continue;
|
||||
}
|
||||
ce_flags = ce->ce_flags;
|
||||
base_flags = base->ce_flags;
|
||||
/* only on-disk flags matter */
|
||||
ce->ce_flags &= ondisk_flags;
|
||||
base->ce_flags &= ondisk_flags;
|
||||
ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
|
||||
offsetof(struct cache_entry, name) -
|
||||
offsetof(struct cache_entry, ce_stat_data));
|
||||
ce->ce_flags = ce_flags;
|
||||
base->ce_flags = base_flags;
|
||||
if (ret)
|
||||
ce->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
free(base);
|
||||
si->base->cache[ce->index - 1] = ce;
|
||||
}
|
||||
for (i = 0; i < si->base->cache_nr; i++) {
|
||||
ce = si->base->cache[i];
|
||||
if ((ce->ce_flags & CE_REMOVE) ||
|
||||
!(ce->ce_flags & CE_MATCHED))
|
||||
ewah_set(si->delete_bitmap, i);
|
||||
else if (ce->ce_flags & CE_UPDATE_IN_BASE) {
|
||||
ewah_set(si->replace_bitmap, i);
|
||||
ce->ce_flags |= CE_STRIP_NAME;
|
||||
ALLOC_GROW(entries, nr_entries+1, nr_alloc);
|
||||
entries[nr_entries++] = ce;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
ce = istate->cache[i];
|
||||
if ((!si->base || !ce->index) && !(ce->ce_flags & CE_REMOVE)) {
|
||||
assert(!(ce->ce_flags & CE_STRIP_NAME));
|
||||
ALLOC_GROW(entries, nr_entries+1, nr_alloc);
|
||||
entries[nr_entries++] = ce;
|
||||
}
|
||||
ce->ce_flags &= ~CE_MATCHED;
|
||||
}
|
||||
|
||||
/*
|
||||
* take cache[] out temporarily, put entries[] in its place
|
||||
* for writing
|
||||
*/
|
||||
si->saved_cache = istate->cache;
|
||||
si->saved_cache_nr = istate->cache_nr;
|
||||
istate->cache = entries;
|
||||
istate->cache_nr = nr_entries;
|
||||
}
|
||||
|
||||
void finish_writing_split_index(struct index_state *istate)
|
||||
{
|
||||
struct split_index *si = init_split_index(istate);
|
||||
|
||||
ewah_free(si->delete_bitmap);
|
||||
ewah_free(si->replace_bitmap);
|
||||
si->delete_bitmap = NULL;
|
||||
si->replace_bitmap = NULL;
|
||||
free(istate->cache);
|
||||
istate->cache = si->saved_cache;
|
||||
istate->cache_nr = si->saved_cache_nr;
|
||||
}
|
||||
|
||||
void discard_split_index(struct index_state *istate)
|
||||
{
|
||||
struct split_index *si = istate->split_index;
|
||||
if (!si)
|
||||
return;
|
||||
istate->split_index = NULL;
|
||||
si->refcount--;
|
||||
if (si->refcount)
|
||||
return;
|
||||
if (si->base) {
|
||||
discard_index(si->base);
|
||||
free(si->base);
|
||||
}
|
||||
free(si);
|
||||
}
|
||||
|
||||
void save_or_free_index_entry(struct index_state *istate, struct cache_entry *ce)
|
||||
{
|
||||
if (ce->index &&
|
||||
istate->split_index &&
|
||||
istate->split_index->base &&
|
||||
ce->index <= istate->split_index->base->cache_nr &&
|
||||
ce == istate->split_index->base->cache[ce->index - 1])
|
||||
ce->ce_flags |= CE_REMOVE;
|
||||
else
|
||||
free(ce);
|
||||
}
|
||||
|
||||
void replace_index_entry_in_base(struct index_state *istate,
|
||||
struct cache_entry *old,
|
||||
struct cache_entry *new)
|
||||
{
|
||||
if (old->index &&
|
||||
istate->split_index &&
|
||||
istate->split_index->base &&
|
||||
old->index <= istate->split_index->base->cache_nr) {
|
||||
new->index = old->index;
|
||||
if (old != istate->split_index->base->cache[new->index - 1])
|
||||
free(istate->split_index->base->cache[new->index - 1]);
|
||||
istate->split_index->base->cache[new->index - 1] = new;
|
||||
}
|
||||
}
|
35
split-index.h
Normal file
35
split-index.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef SPLIT_INDEX_H
|
||||
#define SPLIT_INDEX_H
|
||||
|
||||
struct index_state;
|
||||
struct strbuf;
|
||||
struct ewah_bitmap;
|
||||
|
||||
struct split_index {
|
||||
unsigned char base_sha1[20];
|
||||
struct index_state *base;
|
||||
struct ewah_bitmap *delete_bitmap;
|
||||
struct ewah_bitmap *replace_bitmap;
|
||||
struct cache_entry **saved_cache;
|
||||
unsigned int saved_cache_nr;
|
||||
unsigned int nr_deletions;
|
||||
unsigned int nr_replacements;
|
||||
int refcount;
|
||||
};
|
||||
|
||||
struct split_index *init_split_index(struct index_state *istate);
|
||||
void save_or_free_index_entry(struct index_state *istate, struct cache_entry *ce);
|
||||
void replace_index_entry_in_base(struct index_state *istate,
|
||||
struct cache_entry *old,
|
||||
struct cache_entry *new);
|
||||
int read_link_extension(struct index_state *istate,
|
||||
const void *data, unsigned long sz);
|
||||
int write_link_extension(struct strbuf *sb,
|
||||
struct index_state *istate);
|
||||
void move_cache_to_base_index(struct index_state *istate);
|
||||
void merge_base_index(struct index_state *istate);
|
||||
void prepare_to_write_split_index(struct index_state *istate);
|
||||
void finish_writing_split_index(struct index_state *istate);
|
||||
void discard_split_index(struct index_state *istate);
|
||||
|
||||
#endif
|
194
t/t1700-split-index.sh
Executable file
194
t/t1700-split-index.sh
Executable file
@ -0,0 +1,194 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='split index mode tests'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
# We need total control of index splitting here
|
||||
sane_unset GIT_TEST_SPLIT_INDEX
|
||||
|
||||
test_expect_success 'enable split index' '
|
||||
git update-index --split-index &&
|
||||
test-dump-split-index .git/index >actual &&
|
||||
cat >expect <<EOF &&
|
||||
own 8299b0bcd1ac364e5f1d7768efb62fa2da79a339
|
||||
base 39d890139ee5356c7ef572216cebcd27aa41f9df
|
||||
replacements:
|
||||
deletions:
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'add one file' '
|
||||
: >one &&
|
||||
git update-index --add one &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
cat >expect <<EOF &&
|
||||
base 39d890139ee5356c7ef572216cebcd27aa41f9df
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
replacements:
|
||||
deletions:
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'disable split index' '
|
||||
git update-index --no-split-index &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
BASE=`test-dump-split-index .git/index | grep "^own" | sed "s/own/base/"` &&
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
cat >expect <<EOF &&
|
||||
not a split index
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'enable split index again, "one" now belongs to base index"' '
|
||||
git update-index --split-index &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
cat >expect <<EOF &&
|
||||
$BASE
|
||||
replacements:
|
||||
deletions:
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'modify original file, base index untouched' '
|
||||
echo modified >one &&
|
||||
git update-index one &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
q_to_tab >expect <<EOF &&
|
||||
$BASE
|
||||
100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
|
||||
replacements: 0
|
||||
deletions:
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'add another file, which stays index' '
|
||||
: >two &&
|
||||
git update-index --add two &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
q_to_tab >expect <<EOF &&
|
||||
$BASE
|
||||
100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
|
||||
replacements: 0
|
||||
deletions:
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'remove file not in base index' '
|
||||
git update-index --force-remove two &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
q_to_tab >expect <<EOF &&
|
||||
$BASE
|
||||
100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
|
||||
replacements: 0
|
||||
deletions:
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'remove file in base index' '
|
||||
git update-index --force-remove one &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
cat >expect <<EOF &&
|
||||
$BASE
|
||||
replacements:
|
||||
deletions: 0
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'add original file back' '
|
||||
: >one &&
|
||||
git update-index --add one &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual &&
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
cat >expect <<EOF &&
|
||||
$BASE
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
replacements:
|
||||
deletions: 0
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'add new file' '
|
||||
: >two &&
|
||||
git update-index --add two &&
|
||||
git ls-files --stage >actual &&
|
||||
cat >expect <<EOF &&
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'unify index, two files remain' '
|
||||
git update-index --no-split-index &&
|
||||
git ls-files --stage >ls-files.actual &&
|
||||
cat >ls-files.expect <<EOF &&
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
|
||||
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
|
||||
EOF
|
||||
test_cmp ls-files.expect ls-files.actual
|
||||
|
||||
test-dump-split-index .git/index | sed "/^own/d" >actual &&
|
||||
cat >expect <<EOF &&
|
||||
not a split index
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
@ -7,6 +7,8 @@ test_description='skip-worktree bit test'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
sane_unset GIT_TEST_SPLIT_INDEX
|
||||
|
||||
test_set_index_version 3
|
||||
|
||||
cat >expect.full <<EOF
|
||||
|
@ -56,11 +56,12 @@ static int dump_cache_tree(struct cache_tree *it,
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
struct index_state istate;
|
||||
struct cache_tree *another = cache_tree();
|
||||
if (read_cache() < 0)
|
||||
die("unable to read index file");
|
||||
cache_tree_update(another,
|
||||
(const struct cache_entry * const *)active_cache,
|
||||
active_nr, WRITE_TREE_DRY_RUN);
|
||||
istate = the_index;
|
||||
istate.cache_tree = another;
|
||||
cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
|
||||
return dump_cache_tree(active_cache_tree, another, "");
|
||||
}
|
||||
|
34
test-dump-split-index.c
Normal file
34
test-dump-split-index.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include "cache.h"
|
||||
#include "split-index.h"
|
||||
#include "ewah/ewok.h"
|
||||
|
||||
static void show_bit(size_t pos, void *data)
|
||||
{
|
||||
printf(" %d", (int)pos);
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
struct split_index *si;
|
||||
int i;
|
||||
|
||||
do_read_index(&the_index, av[1], 1);
|
||||
printf("own %s\n", sha1_to_hex(the_index.sha1));
|
||||
si = the_index.split_index;
|
||||
if (!si) {
|
||||
printf("not a split index\n");
|
||||
return 0;
|
||||
}
|
||||
printf("base %s\n", sha1_to_hex(si->base_sha1));
|
||||
for (i = 0; i < the_index.cache_nr; i++) {
|
||||
struct cache_entry *ce = the_index.cache[i];
|
||||
printf("%06o %s %d\t%s\n", ce->ce_mode,
|
||||
sha1_to_hex(ce->sha1), ce_stage(ce), ce->name);
|
||||
}
|
||||
printf("replacements:");
|
||||
ewah_each_bit(si->replace_bitmap, show_bit, NULL);
|
||||
printf("\ndeletions:");
|
||||
ewah_each_bit(si->delete_bitmap, show_bit, NULL);
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
@ -6,12 +6,11 @@ static struct lock_file index_lock;
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
int fd = hold_locked_index(&index_lock, 1);
|
||||
hold_locked_index(&index_lock, 1);
|
||||
if (read_cache() < 0)
|
||||
die("unable to read index file");
|
||||
active_cache_tree = NULL;
|
||||
if (write_cache(fd, active_cache, active_nr)
|
||||
|| commit_lock_file(&index_lock))
|
||||
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
|
||||
die("unable to write index file");
|
||||
return 0;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "progress.h"
|
||||
#include "refs.h"
|
||||
#include "attr.h"
|
||||
#include "split-index.h"
|
||||
|
||||
/*
|
||||
* Error messages expected by scripts out of plumbing commands such as
|
||||
@ -241,7 +242,9 @@ static int verify_absent_sparse(const struct cache_entry *ce,
|
||||
enum unpack_trees_error_types,
|
||||
struct unpack_trees_options *o);
|
||||
|
||||
static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
|
||||
static int apply_sparse_checkout(struct index_state *istate,
|
||||
struct cache_entry *ce,
|
||||
struct unpack_trees_options *o)
|
||||
{
|
||||
int was_skip_worktree = ce_skip_worktree(ce);
|
||||
|
||||
@ -249,6 +252,10 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
|
||||
ce->ce_flags |= CE_SKIP_WORKTREE;
|
||||
else
|
||||
ce->ce_flags &= ~CE_SKIP_WORKTREE;
|
||||
if (was_skip_worktree != ce_skip_worktree(ce)) {
|
||||
ce->ce_flags |= CE_UPDATE_IN_BASE;
|
||||
istate->cache_changed |= CE_ENTRY_CHANGED;
|
||||
}
|
||||
|
||||
/*
|
||||
* if (!was_skip_worktree && !ce_skip_worktree()) {
|
||||
@ -1009,6 +1016,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||
state.force = 1;
|
||||
state.quiet = 1;
|
||||
state.refresh_cache = 1;
|
||||
state.istate = &o->result;
|
||||
|
||||
memset(&el, 0, sizeof(el));
|
||||
if (!core_apply_sparse_checkout || !o->update)
|
||||
@ -1025,6 +1033,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||
o->result.timestamp.sec = o->src_index->timestamp.sec;
|
||||
o->result.timestamp.nsec = o->src_index->timestamp.nsec;
|
||||
o->result.version = o->src_index->version;
|
||||
o->result.split_index = o->src_index->split_index;
|
||||
if (o->result.split_index)
|
||||
o->result.split_index->refcount++;
|
||||
hashcpy(o->result.sha1, o->src_index->sha1);
|
||||
o->merge_size = len;
|
||||
mark_all_ce_unused(o->src_index);
|
||||
|
||||
@ -1115,7 +1127,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (apply_sparse_checkout(ce, o)) {
|
||||
if (apply_sparse_checkout(&o->result, ce, o)) {
|
||||
if (!o->show_all_errors)
|
||||
goto return_failed;
|
||||
ret = -1;
|
||||
@ -1243,7 +1255,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
|
||||
struct unpack_trees_options *o)
|
||||
{
|
||||
if (ce)
|
||||
cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
|
||||
cache_tree_invalidate_path(o->src_index, ce->name);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user