Merge branch 'nd/checkout-paths-reduce-match-pathspec-calls'
Consolidate repeated pathspec matches on the same paths, while fixing a bug in "git checkout dir/" code started from an unmerged index. * nd/checkout-paths-reduce-match-pathspec-calls: checkout: avoid unnecessary match_pathspec calls
This commit is contained in:
commit
97fefaf6d3
@ -271,24 +271,55 @@ static int checkout_paths(const struct checkout_opts *opts,
|
|||||||
;
|
;
|
||||||
ps_matched = xcalloc(1, pos);
|
ps_matched = xcalloc(1, pos);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure all pathspecs participated in locating the paths
|
||||||
|
* to be checked out.
|
||||||
|
*/
|
||||||
for (pos = 0; pos < active_nr; pos++) {
|
for (pos = 0; pos < active_nr; pos++) {
|
||||||
struct cache_entry *ce = active_cache[pos];
|
struct cache_entry *ce = active_cache[pos];
|
||||||
|
ce->ce_flags &= ~CE_MATCHED;
|
||||||
if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
|
if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
|
||||||
|
/*
|
||||||
|
* "git checkout tree-ish -- path", but this entry
|
||||||
|
* is in the original index; it will not be checked
|
||||||
|
* out to the working tree and it does not matter
|
||||||
|
* if pathspec matched this entry. We will not do
|
||||||
|
* anything to this entry at all.
|
||||||
|
*/
|
||||||
continue;
|
continue;
|
||||||
match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
|
/*
|
||||||
|
* Either this entry came from the tree-ish we are
|
||||||
|
* checking the paths out of, or we are checking out
|
||||||
|
* of the index.
|
||||||
|
*
|
||||||
|
* If it comes from the tree-ish, we already know it
|
||||||
|
* matches the pathspec and could just stamp
|
||||||
|
* CE_MATCHED to it from update_some(). But we still
|
||||||
|
* need ps_matched and read_tree_recursive (and
|
||||||
|
* eventually tree_entry_interesting) cannot fill
|
||||||
|
* ps_matched yet. Once it can, we can avoid calling
|
||||||
|
* match_pathspec() for _all_ entries when
|
||||||
|
* opts->source_tree != NULL.
|
||||||
|
*/
|
||||||
|
if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce),
|
||||||
|
0, ps_matched))
|
||||||
|
ce->ce_flags |= CE_MATCHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (report_path_error(ps_matched, opts->pathspec, opts->prefix))
|
if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) {
|
||||||
|
free(ps_matched);
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
free(ps_matched);
|
||||||
|
|
||||||
/* "checkout -m path" to recreate conflicted state */
|
/* "checkout -m path" to recreate conflicted state */
|
||||||
if (opts->merge)
|
if (opts->merge)
|
||||||
unmerge_cache(opts->pathspec);
|
unmerge_marked_index(&the_index);
|
||||||
|
|
||||||
/* Any unmerged paths? */
|
/* Any unmerged paths? */
|
||||||
for (pos = 0; pos < active_nr; pos++) {
|
for (pos = 0; pos < active_nr; pos++) {
|
||||||
struct cache_entry *ce = active_cache[pos];
|
struct cache_entry *ce = active_cache[pos];
|
||||||
if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
|
if (ce->ce_flags & CE_MATCHED) {
|
||||||
if (!ce_stage(ce))
|
if (!ce_stage(ce))
|
||||||
continue;
|
continue;
|
||||||
if (opts->force) {
|
if (opts->force) {
|
||||||
@ -313,9 +344,7 @@ static int checkout_paths(const struct checkout_opts *opts,
|
|||||||
state.refresh_cache = 1;
|
state.refresh_cache = 1;
|
||||||
for (pos = 0; pos < active_nr; pos++) {
|
for (pos = 0; pos < active_nr; pos++) {
|
||||||
struct cache_entry *ce = active_cache[pos];
|
struct cache_entry *ce = active_cache[pos];
|
||||||
if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
|
if (ce->ce_flags & CE_MATCHED) {
|
||||||
continue;
|
|
||||||
if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
|
|
||||||
if (!ce_stage(ce)) {
|
if (!ce_stage(ce)) {
|
||||||
errs |= checkout_entry(ce, &state, NULL);
|
errs |= checkout_entry(ce, &state, NULL);
|
||||||
continue;
|
continue;
|
||||||
|
3
cache.h
3
cache.h
@ -162,6 +162,9 @@ struct cache_entry {
|
|||||||
#define CE_UNPACKED (1 << 24)
|
#define CE_UNPACKED (1 << 24)
|
||||||
#define CE_NEW_SKIP_WORKTREE (1 << 25)
|
#define CE_NEW_SKIP_WORKTREE (1 << 25)
|
||||||
|
|
||||||
|
/* used to temporarily mark paths matched by pathspecs */
|
||||||
|
#define CE_MATCHED (1 << 26)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extended on-disk flags
|
* Extended on-disk flags
|
||||||
*/
|
*/
|
||||||
|
@ -118,7 +118,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
|
|||||||
struct cache_entry *ce;
|
struct cache_entry *ce;
|
||||||
struct string_list_item *item;
|
struct string_list_item *item;
|
||||||
struct resolve_undo_info *ru;
|
struct resolve_undo_info *ru;
|
||||||
int i, err = 0;
|
int i, err = 0, matched;
|
||||||
|
|
||||||
if (!istate->resolve_undo)
|
if (!istate->resolve_undo)
|
||||||
return pos;
|
return pos;
|
||||||
@ -137,6 +137,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
|
|||||||
ru = item->util;
|
ru = item->util;
|
||||||
if (!ru)
|
if (!ru)
|
||||||
return pos;
|
return pos;
|
||||||
|
matched = ce->ce_flags & CE_MATCHED;
|
||||||
remove_index_entry_at(istate, pos);
|
remove_index_entry_at(istate, pos);
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
struct cache_entry *nce;
|
struct cache_entry *nce;
|
||||||
@ -144,6 +145,8 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
|
|||||||
continue;
|
continue;
|
||||||
nce = make_cache_entry(ru->mode[i], ru->sha1[i],
|
nce = make_cache_entry(ru->mode[i], ru->sha1[i],
|
||||||
ce->name, i + 1, 0);
|
ce->name, i + 1, 0);
|
||||||
|
if (matched)
|
||||||
|
nce->ce_flags |= CE_MATCHED;
|
||||||
if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
|
if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
|
||||||
err = 1;
|
err = 1;
|
||||||
error("cannot unmerge '%s'", ce->name);
|
error("cannot unmerge '%s'", ce->name);
|
||||||
@ -156,6 +159,20 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
|
|||||||
return unmerge_index_entry_at(istate, pos);
|
return unmerge_index_entry_at(istate, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unmerge_marked_index(struct index_state *istate)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!istate->resolve_undo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < istate->cache_nr; i++) {
|
||||||
|
struct cache_entry *ce = istate->cache[i];
|
||||||
|
if (ce->ce_flags & CE_MATCHED)
|
||||||
|
i = unmerge_index_entry_at(istate, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void unmerge_index(struct index_state *istate, const char **pathspec)
|
void unmerge_index(struct index_state *istate, const char **pathspec)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -12,5 +12,6 @@ extern struct string_list *resolve_undo_read(const char *, unsigned long);
|
|||||||
extern void resolve_undo_clear_index(struct index_state *);
|
extern void resolve_undo_clear_index(struct index_state *);
|
||||||
extern int unmerge_index_entry_at(struct index_state *, int);
|
extern int unmerge_index_entry_at(struct index_state *, int);
|
||||||
extern void unmerge_index(struct index_state *, const char **);
|
extern void unmerge_index(struct index_state *, const char **);
|
||||||
|
extern void unmerge_marked_index(struct index_state *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -39,4 +39,26 @@ test_expect_success 'checking out paths out of a tree does not clobber unrelated
|
|||||||
test_cmp expect.next2 dir/next2
|
test_cmp expect.next2 dir/next2
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'do not touch unmerged entries matching $path but not in $tree' '
|
||||||
|
git checkout next &&
|
||||||
|
git reset --hard &&
|
||||||
|
|
||||||
|
cat dir/common >expect.common &&
|
||||||
|
EMPTY_SHA1=$(git hash-object -w --stdin </dev/null) &&
|
||||||
|
git rm dir/next0 &&
|
||||||
|
cat >expect.next0 <<-EOF &&
|
||||||
|
100644 $EMPTY_SHA1 1 dir/next0
|
||||||
|
100644 $EMPTY_SHA1 2 dir/next0
|
||||||
|
EOF
|
||||||
|
git update-index --index-info <expect.next0 &&
|
||||||
|
|
||||||
|
git checkout master dir &&
|
||||||
|
|
||||||
|
test_cmp expect.common dir/common &&
|
||||||
|
test_path_is_file dir/master &&
|
||||||
|
git diff --exit-code master dir/master &&
|
||||||
|
git ls-files -s dir/next0 >actual.next0 &&
|
||||||
|
test_cmp expect.next0 actual.next0
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user