Skip to content

Commit 2026440

Browse files
spoggitster
authored andcommitted
shallow: set borders which are all reachable after clone shallow since
When shallow cloning based on a date, it happens that not all shallow border commits are reachable. Original implementation of a generic shallow boundary finder based on rev-list sets a commit (from the initial list of border commit candidates) to be the border commit as soon as it finds one of its parentis that wasn't on the list of initial candidates. This results in a successful shallow clone, where some of its declared border commits may not be reachable and they would not actually exist in the cloned repository. Thus the result may contradict existing comment in the code, which correctly states that such commmit should not be considered border. One can inspect such case by running the added test scenario: - 'clone shallow since all borders reachable' The modified implementation of a generic shallow boundary finder based on rev-list ensures that all shallow border commits are reachable also after being grafted. This is achieved by inspecting all parents of each initial border commit candidate. The border commit candidate is set border only when all its parents wern't on the initial list of candidates. Otherwise the border commit candidate is not set as border however its parents that weren't on the list of candidates are set as borders. Signed-off-by: Samo Pogačnik <samo_pogacnik@t-2.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 9a2fb14 commit 2026440

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

shallow.c

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,21 +251,57 @@ struct commit_list *get_shallow_commits_by_rev_list(struct strvec *argv,
251251
* commit A is processed first, then commit B, whose parent is
252252
* A, later. If NOT_SHALLOW on A is cleared at step 1, B
253253
* itself is considered border at step 2, which is incorrect.
254+
*
255+
* We must also consider that B has multiple parents which may
256+
* not all be marked NOT_SHALLOW (as they weren't traversed into
257+
* the not_shallow_list from revs in the first place). Because of
258+
* that an additional step is required to reconsider B as border.
259+
* A commit from the not_shallow_list is considered border only
260+
* when ALL its parents weren't on the not_shallow_list.
261+
* When one or more parents of a commit from the not_shellow_list
262+
* also come from that list, the commit is not considered border,
263+
* but its non-listed parents are considered border commits.
264+
*
265+
* The general processing goes like this:
266+
* 1. Above we've painted the whole not_shallow_list of commits
267+
* NOT_SHALLOW.
268+
* 2. For each commit from the not_shallow_list (the code below)
269+
* we paint SHALLOW this commit and its parent for all its
270+
* parents that had not yet been painted NOT_SHALLOW.
271+
* 3. Commits with all parents being painted only SHALLOW remain
272+
* shallow and are being added to result list.
273+
* 4. Commits without all parents being painted only SHALLOW are
274+
* being excluded as borders, however their parents painted only
275+
* SHALLOW are being added to the result borders list.
254276
*/
255277
for (p = not_shallow_list; p; p = p->next) {
256278
struct commit *c = p->item;
257279
struct commit_list *parent;
280+
int must_not_be_shallow = 0;
258281

259282
if (repo_parse_commit(the_repository, c))
260283
die("unable to parse commit %s",
261284
oid_to_hex(&c->object.oid));
262285

263286
for (parent = c->parents; parent; parent = parent->next)
264-
if (!(parent->item->object.flags & not_shallow_flag)) {
287+
if (parent->item->object.flags & not_shallow_flag) {
288+
must_not_be_shallow = 1;
289+
} else {
265290
c->object.flags |= shallow_flag;
266-
commit_list_insert(c, &result);
267-
break;
291+
parent->item->object.flags |= shallow_flag;
268292
}
293+
if (must_not_be_shallow) {
294+
c->object.flags &= ~shallow_flag;
295+
for (parent = c->parents; parent; parent = parent->next)
296+
if (parent->item->object.flags & shallow_flag) {
297+
parent->item->object.flags |= not_shallow_flag;
298+
commit_list_insert(parent->item, &result);
299+
}
300+
} else {
301+
for (parent = c->parents; parent; parent = parent->next)
302+
parent->item->object.flags &= ~shallow_flag;
303+
commit_list_insert(c, &result);
304+
}
269305
}
270306
free_commit_list(not_shallow_list);
271307

t/t5500-fetch-pack.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,25 @@ test_expect_success 'shallow since with commit graph and already-seen commit' '
904904
)
905905
'
906906

907+
test_expect_success 'clone shallow since all borders reachable' '
908+
test_create_repo shallow-since-all-borders-reachable &&
909+
(
910+
rm -rf shallow123 &&
911+
cd shallow-since-all-borders-reachable &&
912+
GIT_COMMITTER_DATE="2025-08-19 12:34:56" git commit --allow-empty -m one &&
913+
GIT_COMMITTER_DATE="2025-08-20 12:34:56" git switch -c branch &&
914+
GIT_COMMITTER_DATE="2025-08-21 12:34:56" git commit --allow-empty -m two &&
915+
GIT_COMMITTER_DATE="2025-08-22 12:34:56" git commit --allow-empty -m three &&
916+
GIT_COMMITTER_DATE="2025-08-23 12:34:56" git switch main &&
917+
GIT_COMMITTER_DATE="2025-08-24 12:34:56" git merge branch --no-ff &&
918+
GIT_COMMITTER_DATE="2025-08-26 12:34:56" git clone --shallow-since "2025-08-21 12:34:56" "file://$(pwd)/." ../shallow123 &&
919+
cd ../shallow123 &&
920+
echo "Shallow borders:" &&
921+
cat .git/shallow &&
922+
$(for commit in $(cat .git/shallow); do git rev-list $commit 1>/dev/null || exit 1; done)
923+
)
924+
'
925+
907926
test_expect_success 'shallow clone exclude tag two' '
908927
test_create_repo shallow-exclude &&
909928
(

0 commit comments

Comments
 (0)