Skip to content

Commit 50f6468

Browse files
committed
feat(run-task): implement shallow git clones
1 parent 4450da7 commit 50f6468

File tree

2 files changed

+270
-25
lines changed

2 files changed

+270
-25
lines changed

src/taskgraph/run-task/run-task

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ def git_fetch(
550550
ref: str,
551551
remote: str = "origin",
552552
tags: bool = False,
553+
shallow: bool = False,
553554
env: Optional[Dict[str, str]] = None,
554555
):
555556
args = ["git", "fetch"]
@@ -558,6 +559,35 @@ def git_fetch(
558559
args.extend(["--tags", "--force"])
559560

560561
args.extend([remote, ref])
562+
563+
if shallow:
564+
# If we have a full sha, we can fetch it directly
565+
if re.match(r"^[a-f0-9]{40}$", ref):
566+
fetch_args = args[:2] + ["--depth=1"] + args[2:]
567+
ret = run_command(b"vcs", fetch_args, cwd=destination_path, extra_env=env)
568+
if ret == 0:
569+
return
570+
571+
# Otherwise we need to incrementally deepen the repo until we detect
572+
# the ref.
573+
for deepen in range(10, 100, 10):
574+
fetch_args = args[:2] + [f"--deepen={deepen}"] + args[2:]
575+
run_command(b"vcs", fetch_args, cwd=destination_path, extra_env=env)
576+
577+
# Check if the target ref exists, if not deepen further.
578+
ret = run_command(
579+
b"vcs",
580+
["git", "cat-file", "-e", "FETCH_HEAD"],
581+
cwd=destination_path,
582+
extra_env=env,
583+
)
584+
if ret == 0:
585+
return
586+
587+
print(f"unable to fetch {ref} from {remote} in shallow clone")
588+
sys.exit(1)
589+
590+
# Non-shallow repo
561591
retry_required_command(b"vcs", args, cwd=destination_path, extra_env=env)
562592

563593

@@ -600,6 +630,19 @@ def _clean_git_checkout(destination_path):
600630
print_line(b"vcs", b"successfully cleaned git checkout!\n")
601631

602632

633+
def shortref(ref: str) -> str:
634+
"""Normalize a git ref to its short form.
635+
636+
Returns the ref unchanged if it's already in short form.
637+
"""
638+
# Strip common ref prefixes
639+
for prefix in ("refs/heads/", "refs/tags/"):
640+
if ref.startswith(prefix):
641+
return ref[len(prefix) :]
642+
643+
return ref
644+
645+
603646
def git_checkout(
604647
destination_path: str,
605648
head_repo: str,
@@ -609,6 +652,7 @@ def git_checkout(
609652
commit: Optional[str],
610653
ssh_key_file: Optional[Path],
611654
ssh_known_hosts_file: Optional[Path],
655+
shallow: bool = False,
612656
):
613657
env = {
614658
# abort if transfer speed is lower than 1kB/s for 1 minute
@@ -652,16 +696,24 @@ def git_checkout(
652696
args = [
653697
"git",
654698
"clone",
655-
base_repo if base_repo else head_repo,
656-
destination_path,
657699
]
658700

701+
if shallow:
702+
args.extend(["--depth=1", "--no-checkout"])
703+
704+
args.extend(
705+
[
706+
base_repo if base_repo else head_repo,
707+
destination_path,
708+
]
709+
)
710+
659711
retry_required_command(b"vcs", args, extra_env=env)
660712

661713
# First fetch the base_rev. This allows Taskgraph to compute the files
662714
# changed by the push.
663715
if base_rev and base_rev != NULL_REVISION:
664-
git_fetch(destination_path, base_rev, env=env)
716+
git_fetch(destination_path, base_rev, shallow=shallow, env=env)
665717

666718
# Next fetch the head ref.
667719

@@ -676,16 +728,27 @@ def git_checkout(
676728

677729
# If a ref isn't provided, we fetch all refs from head_repo, which may be slow.
678730
target = ref if ref else "+refs/heads/*:refs/remotes/work/*"
679-
git_fetch(destination_path, target, remote=head_repo, tags=tags, env=env)
731+
git_fetch(
732+
destination_path,
733+
target,
734+
remote=head_repo,
735+
tags=tags,
736+
shallow=shallow,
737+
env=env,
738+
)
739+
740+
# If we have a shallow clone and specific commit, we need to fetch it too.
741+
if shallow and commit and commit != ref:
742+
git_fetch(destination_path, commit, remote=head_repo, shallow=shallow, env=env)
680743

681744
args = [
682745
"git",
683746
"checkout",
684747
"-f",
685748
]
686749

687-
if ref:
688-
args.extend(["-B", ref])
750+
if ref and ref != commit:
751+
args.extend(["-B", shortref(ref)])
689752

690753
# `git fetch` set `FETCH_HEAD` reference to the last commit of the desired branch
691754
args.append(commit if commit else "FETCH_HEAD")
@@ -862,11 +925,17 @@ def add_vcs_arguments(parser, project, name):
862925
f"--{project}-sparse-profile",
863926
help=f"Path to sparse profile for {name} checkout",
864927
)
928+
parser.add_argument(
929+
f"--{project}-shallow-clone",
930+
action="store_true",
931+
help=f"Use shallow clone for {name}",
932+
)
865933

866934

867935
def collect_vcs_options(args, project, name):
868936
checkout = getattr(args, f"{project}_checkout")
869937
sparse_profile = getattr(args, f"{project}_sparse_profile")
938+
shallow_clone = getattr(args, f"{project}_shallow_clone")
870939

871940
env_prefix = project.upper()
872941

@@ -911,6 +980,7 @@ def collect_vcs_options(args, project, name):
911980
"repo-type": repo_type,
912981
"ssh-secret-name": private_key_secret,
913982
"pip-requirements": pip_requirements,
983+
"shallow-clone": shallow_clone,
914984
}
915985

916986

@@ -958,6 +1028,7 @@ def vcs_checkout_from_args(options):
9581028
revision,
9591029
ssh_key_file,
9601030
ssh_known_hosts_file,
1031+
shallow=options.get("shallow-clone", False),
9611032
)
9621033
elif options["repo-type"] == "hg":
9631034
if not revision and not ref:

0 commit comments

Comments
 (0)