mirror of
https://github.com/git/git
synced 2024-08-27 19:49:21 +00:00
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);
|
||||
|
||||
/*
|
||||
* Make sure all pathspecs participated in locating the paths
|
||||
* to be checked out.
|
||||
*/
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
struct cache_entry *ce = active_cache[pos];
|
||||
ce->ce_flags &= ~CE_MATCHED;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
free(ps_matched);
|
||||
|
||||
/* "checkout -m path" to recreate conflicted state */
|
||||
if (opts->merge)
|
||||
unmerge_cache(opts->pathspec);
|
||||
unmerge_marked_index(&the_index);
|
||||
|
||||
/* Any unmerged paths? */
|
||||
for (pos = 0; pos < active_nr; 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))
|
||||
continue;
|
||||
if (opts->force) {
|
||||
|
@ -313,9 +344,7 @@ static int checkout_paths(const struct checkout_opts *opts,
|
|||
state.refresh_cache = 1;
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
struct cache_entry *ce = active_cache[pos];
|
||||
if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
|
||||
continue;
|
||||
if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
|
||||
if (ce->ce_flags & CE_MATCHED) {
|
||||
if (!ce_stage(ce)) {
|
||||
errs |= checkout_entry(ce, &state, NULL);
|
||||
continue;
|
||||
|
|
3
cache.h
3
cache.h
|
@ -162,6 +162,9 @@ struct cache_entry {
|
|||
#define CE_UNPACKED (1 << 24)
|
||||
#define CE_NEW_SKIP_WORKTREE (1 << 25)
|
||||
|
||||
/* used to temporarily mark paths matched by pathspecs */
|
||||
#define CE_MATCHED (1 << 26)
|
||||
|
||||
/*
|
||||
* Extended on-disk flags
|
||||
*/
|
||||
|
|
|
@ -118,7 +118,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
|
|||
struct cache_entry *ce;
|
||||
struct string_list_item *item;
|
||||
struct resolve_undo_info *ru;
|
||||
int i, err = 0;
|
||||
int i, err = 0, matched;
|
||||
|
||||
if (!istate->resolve_undo)
|
||||
return pos;
|
||||
|
@ -137,6 +137,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
|
|||
ru = item->util;
|
||||
if (!ru)
|
||||
return pos;
|
||||
matched = ce->ce_flags & CE_MATCHED;
|
||||
remove_index_entry_at(istate, pos);
|
||||
for (i = 0; i < 3; i++) {
|
||||
struct cache_entry *nce;
|
||||
|
@ -144,6 +145,8 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
|
|||
continue;
|
||||
nce = make_cache_entry(ru->mode[i], ru->sha1[i],
|
||||
ce->name, i + 1, 0);
|
||||
if (matched)
|
||||
nce->ce_flags |= CE_MATCHED;
|
||||
if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
|
||||
err = 1;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 int unmerge_index_entry_at(struct index_state *, int);
|
||||
extern void unmerge_index(struct index_state *, const char **);
|
||||
extern void unmerge_marked_index(struct index_state *);
|
||||
|
||||
#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_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
|
||||
|
|
Loading…
Reference in a new issue