Merge branch 'aj/stash-patch-optimize-temporary-index'
"git stash -p" has been optimized by reusing cached index entries in its temporary index, avoiding unnecessary lstat() calls on unchanged files. * aj/stash-patch-optimize-temporary-index: stash: reuse cached index entries in --patch temporary indexmain
commit
de5383c2ce
|
|
@ -372,6 +372,56 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int create_index_from_tree(const struct object_id *tree_id,
|
||||
const char *index_path)
|
||||
{
|
||||
int nr_trees = 1;
|
||||
int ret = 0;
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc t[MAX_UNPACK_TREES];
|
||||
struct tree *tree;
|
||||
struct index_state dst_istate = INDEX_STATE_INIT(the_repository);
|
||||
struct lock_file lock_file = LOCK_INIT;
|
||||
|
||||
repo_read_index_preload(the_repository, NULL, 0);
|
||||
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
|
||||
|
||||
hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
tree = repo_parse_tree_indirect(the_repository, tree_id);
|
||||
if (!tree || repo_parse_tree(the_repository, tree)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
|
||||
|
||||
opts.head_idx = 1;
|
||||
opts.src_index = the_repository->index;
|
||||
opts.dst_index = &dst_istate;
|
||||
opts.merge = 1;
|
||||
opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
|
||||
opts.fn = oneway_merge;
|
||||
|
||||
if (unpack_trees(nr_trees, t, &opts)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (write_locked_index(&dst_istate, &lock_file, COMMIT_LOCK)) {
|
||||
ret = error(_("unable to write new index file"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
release_index(&dst_istate);
|
||||
if (ret)
|
||||
rollback_lock_file(&lock_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
|
||||
{
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
|
@ -1321,18 +1371,26 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
|||
struct interactive_options *interactive_opts)
|
||||
{
|
||||
int ret = 0;
|
||||
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
|
||||
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
|
||||
struct commit *head_commit;
|
||||
const struct object_id *head_tree;
|
||||
struct index_state istate = INDEX_STATE_INIT(the_repository);
|
||||
char *old_index_env = NULL, *old_repo_index_file;
|
||||
|
||||
remove_path(stash_index_path.buf);
|
||||
|
||||
cp_read_tree.git_cmd = 1;
|
||||
strvec_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
|
||||
strvec_pushf(&cp_read_tree.env, "GIT_INDEX_FILE=%s",
|
||||
stash_index_path.buf);
|
||||
if (run_command(&cp_read_tree)) {
|
||||
head_commit = lookup_commit(the_repository, &info->b_commit);
|
||||
if (!head_commit || repo_parse_commit(the_repository, head_commit)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
head_tree = get_commit_tree_oid(head_commit);
|
||||
if (!head_tree) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (create_index_from_tree(head_tree, stash_index_path.buf)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description="Performance tests for git stash -p"
|
||||
|
||||
. ./perf-lib.sh
|
||||
|
||||
test_perf_fresh_repo
|
||||
|
||||
test_expect_success "setup" '
|
||||
mkdir files &&
|
||||
test_seq 1 100000 | while read i; do
|
||||
echo "content $i" >files/$i.txt || return 1
|
||||
done &&
|
||||
git add files/ &&
|
||||
git commit -q -m "add tracked files" &&
|
||||
echo modified >files/1.txt
|
||||
'
|
||||
|
||||
test_perf "stash -p, no fsmonitor" \
|
||||
--setup 'echo modified >files/1.txt' '
|
||||
printf "q\n" | git stash -p >/dev/null 2>&1 || true
|
||||
'
|
||||
|
||||
if test_have_prereq FSMONITOR_DAEMON
|
||||
then
|
||||
test_expect_success "enable builtin fsmonitor" '
|
||||
git config core.fsmonitor true &&
|
||||
git fsmonitor--daemon start &&
|
||||
git update-index --fsmonitor &&
|
||||
git status >/dev/null 2>&1
|
||||
'
|
||||
|
||||
test_perf "stash -p, builtin fsmonitor" \
|
||||
--setup 'echo modified >files/1.txt && git status >/dev/null 2>&1' '
|
||||
printf "q\n" | git stash -p >/dev/null 2>&1 || true
|
||||
'
|
||||
|
||||
test_expect_success "stop builtin fsmonitor" '
|
||||
git fsmonitor--daemon stop
|
||||
'
|
||||
fi
|
||||
|
||||
test_done
|
||||
Loading…
Reference in New Issue