diff --git a/shallow.c b/shallow.c index ddbd325456..e01ddf74b7 100644 --- a/shallow.c +++ b/shallow.c @@ -268,21 +268,57 @@ struct commit_list *get_shallow_commits_by_rev_list(struct strvec *argv, * commit A is processed first, then commit B, whose parent is * A, later. If NOT_SHALLOW on A is cleared at step 1, B * itself is considered border at step 2, which is incorrect. + * + * We must also consider that B has multiple parents which may + * not all be marked NOT_SHALLOW (as they weren't traversed into + * the not_shallow_list from revs in the first place). Because of + * that an additional step is required to reconsider B as border. + * A commit from the not_shallow_list is considered border only + * when ALL its parents weren't on the not_shallow_list. + * When one or more parents of a commit from the not_shellow_list + * also come from that list, the commit is not considered border, + * but its non-listed parents are considered border commits. + * + * The general processing goes like this: + * 1. Above we've painted the whole not_shallow_list of commits + * NOT_SHALLOW. + * 2. For each commit from the not_shallow_list (the code below) + * we paint SHALLOW this commit and its parent for all its + * parents that had not yet been painted NOT_SHALLOW. + * 3. Commits with all parents being painted only SHALLOW remain + * shallow and are being added to result list. + * 4. Commits without all parents being painted only SHALLOW are + * being excluded as borders, however their parents painted only + * SHALLOW are being added to the result borders list. */ for (p = not_shallow_list; p; p = p->next) { struct commit *c = p->item; struct commit_list *parent; + int must_not_be_shallow = 0; if (repo_parse_commit(the_repository, c)) die("unable to parse commit %s", oid_to_hex(&c->object.oid)); for (parent = c->parents; parent; parent = parent->next) - if (!(parent->item->object.flags & not_shallow_flag)) { + if (parent->item->object.flags & not_shallow_flag) { + must_not_be_shallow = 1; + } else { c->object.flags |= shallow_flag; - commit_list_insert(c, &result); - break; + parent->item->object.flags |= shallow_flag; } + if (must_not_be_shallow) { + c->object.flags &= ~shallow_flag; + for (parent = c->parents; parent; parent = parent->next) + if (parent->item->object.flags & shallow_flag) { + parent->item->object.flags |= not_shallow_flag; + commit_list_insert(parent->item, &result); + } + } else { + for (parent = c->parents; parent; parent = parent->next) + parent->item->object.flags &= ~shallow_flag; + commit_list_insert(c, &result); + } } free_commit_list(not_shallow_list); diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 5a8b30e1fd..10efae1d38 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -904,6 +904,25 @@ test_expect_success 'shallow since with commit graph and already-seen commit' ' ) ' +test_expect_success 'clone shallow since all borders reachable' ' + test_create_repo shallow-since-all-borders-reachable && + ( + rm -rf shallow123 && + cd shallow-since-all-borders-reachable && + GIT_COMMITTER_DATE="2025-08-19 12:34:56" git commit --allow-empty -m one && + GIT_COMMITTER_DATE="2025-08-20 12:34:56" git switch -c branch && + GIT_COMMITTER_DATE="2025-08-21 12:34:56" git commit --allow-empty -m two && + GIT_COMMITTER_DATE="2025-08-22 12:34:56" git commit --allow-empty -m three && + GIT_COMMITTER_DATE="2025-08-23 12:34:56" git switch main && + GIT_COMMITTER_DATE="2025-08-24 12:34:56" git merge branch --no-ff && + GIT_COMMITTER_DATE="2025-08-26 12:34:56" git clone --shallow-since "2025-08-21 12:34:56" "file://$(pwd)/." ../shallow123 && + cd ../shallow123 && + echo "Shallow borders:" && + cat .git/shallow && + $(for commit in $(cat .git/shallow); do git rev-list $commit 1>/dev/null || exit 1; done) + ) +' + test_expect_success 'shallow clone exclude tag two' ' test_create_repo shallow-exclude && (