diff --git a/refs/ref-cache.c b/refs/ref-cache.c index ffef01a597579e..a3bc2dc11b7ab2 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -506,7 +506,7 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, sort_ref_dir(dir); slash = strchr(slash, '/'); - len = slash ? (size_t)(slash - refname) : strlen(refname); + len = slash ? (size_t)(slash - refname + 1) : strlen(refname); for (idx = 0; idx < dir->nr; idx++) { cmp = strncmp(refname, dir->entries[idx]->name, len); @@ -520,6 +520,8 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, slash = slash + 1; level->index = idx; + if (cmp > 0) + break; if (dir->entries[idx]->flag & REF_DIR) { /* push down a level */ dir = get_ref_dir(dir->entries[idx]); @@ -532,7 +534,7 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, level->prefix_state = PREFIX_CONTAINS_DIR; } else { /* reduce the index so the leaf node is iterated over */ - if (cmp <= 0 && !slash) + if (cmp < 0 || (cmp == 0 && !slash)) level->index = idx - 1; /* * while the seek path may not be exhausted, our diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index 7f060d97bf509c..826fc1b9f2bfbc 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -746,6 +746,61 @@ test_expect_success 'start after used with a pattern' ' test_cmp expect actual ' +for storage in loose packed mixed +do + test_expect_success "start after respects directory boundaries ($storage)" ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit --no-tag base && + git update-ref refs/tags/packed HEAD && + if test "$storage" = mixed + then + git pack-refs --all || return 1 + fi && + git update-ref --stdin <<-\EOF && + create refs/tags/a-leaf HEAD + create refs/tags/a/nested HEAD + create refs/tags/a0 HEAD + create refs/tags/b HEAD + create refs/tags/releases-z HEAD + create refs/tags/releases/v1 HEAD + create refs/tags/releases/v2 HEAD + EOF + if test "$storage" = packed + then + git pack-refs --all || return 1 + fi && + echo refs/tags/releases/v2 >expect && + git for-each-ref --no-sort --format="%(refname)" \ + --start-after=refs/tags/releases/v1 >actual && + test_cmp expect actual && + cat >expect <<-\EOF && + refs/tags/a0 + refs/tags/b + refs/tags/packed + refs/tags/releases-z + refs/tags/releases/v1 + refs/tags/releases/v2 + EOF + git for-each-ref --no-sort --format="%(refname)" \ + --start-after=refs/tags/a/next >actual && + test_cmp expect actual && + tail -n 5 expect >expect-later && + git for-each-ref --no-sort --format="%(refname)" \ + --start-after=refs/tags/aa/missing >actual && + test_cmp expect-later actual && + for marker in refs/tags/zzzz refs/zzzz zzzz + do + git for-each-ref --no-sort --format="%(refname)" \ + --start-after="$marker" >actual && + test_must_be_empty actual || return 1 + done + ) + ' +done + test_expect_success 'start after used with custom sort order' ' cat >expect <<-\EOF && fatal: cannot use --start-after with custom sort options