From a4aea9ce3f34ab67ffff1d0f97a4d78fe7e194b0 Mon Sep 17 00:00:00 2001 From: Evilham Date: Tue, 25 Aug 2026 10:57:32 +0200 Subject: [PATCH 1/2] test(operations.git.repo): test that depth argument in fetch and pull As things are right now, the `depth` argument is only passed on git clone, which means that on subsequent updates the repository gets unshallow, and that is surprising / undesired behaviour. As a side effect, changing the `depth` argument after a git clone doesn't have an effect. This commit adds tests for those cases. --- .../git.repo/branch_pull_depth.json | 44 +++++++++++++++++++ tests/operations/git.repo/clone_depth.json | 19 ++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/operations/git.repo/branch_pull_depth.json create mode 100644 tests/operations/git.repo/clone_depth.json diff --git a/tests/operations/git.repo/branch_pull_depth.json b/tests/operations/git.repo/branch_pull_depth.json new file mode 100644 index 000000000..eb96283c7 --- /dev/null +++ b/tests/operations/git.repo/branch_pull_depth.json @@ -0,0 +1,44 @@ +{ + "args": ["myrepo", "/home/myrepo"], + "kwargs": { + "branch": "mybranch", + "depth": 1, + "fetch_tags": true + }, + "facts": { + "git.GitConfig": { + "repo=/home/myrepo, system=False": { + "remote.origin.url": ["myrepo"] + } + }, + "files.Directory": { + "path=/home/myrepo": {}, + "path=/home/myrepo/.git": { + "mode": 0 + } + }, + "git.GitBranch": { + "repo=/home/myrepo": "master" + }, + "git.GitTag": { + "repo=/home/myrepo": [ + ] + }, + "git.GitLocalCommit": { + "ref=mybranch, repo=/home/myrepo": null + }, + "git.GitRemoteBranchCommit": { + "branch=mybranch, remote=origin, repo=/home/myrepo": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + }, + "commands": [ + "cd /home/myrepo && git fetch --depth 1 --tags", + "cd /home/myrepo && git checkout mybranch", + "cd /home/myrepo && git pull --depth 1" + ], + "second_output_commands": [ + "cd /home/myrepo && git pull --depth 1" + ], + "idempotent": false, + "disable_idempotent_warning_reason": "git branch pull always executed" +} diff --git a/tests/operations/git.repo/clone_depth.json b/tests/operations/git.repo/clone_depth.json new file mode 100644 index 000000000..bd33b0498 --- /dev/null +++ b/tests/operations/git.repo/clone_depth.json @@ -0,0 +1,19 @@ +{ + "args": ["myrepo", "/home/myrepo"], + "kwargs": { + "branch": "mybranch", + "user": "myuser", + "depth": 1, + "pull": false + }, + "facts": { + "files.Directory": { + "path=/home/myrepo": {}, + "path=/home/myrepo/.git": null + } + }, + "commands": [ + "cd /home/myrepo && git clone myrepo --depth 1 --branch mybranch .", + "chown -R myuser /home/myrepo" + ] +} From 97745a8aa2aa651724440b2eb319ded6720c3472 Mon Sep 17 00:00:00 2001 From: Evilham Date: Tue, 25 Aug 2026 11:06:06 +0200 Subject: [PATCH 2/2] fix(operations.git.repo): pass depth argument to fetch and pull Previously, when cloning a repo with the `depth` keyword, if the remote directory changed, fetch and/or pull operations would unshallow the repository, given that the `depth` keyword was not being passed. In turn, this ensures that if the value for the `depth` keyword changes, it will be updated in the deployment, though only when there is a change in the remote repository. --- src/pyinfra/operations/git.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/pyinfra/operations/git.py b/src/pyinfra/operations/git.py index 625bb3299..03ee107ca 100644 --- a/src/pyinfra/operations/git.py +++ b/src/pyinfra/operations/git.py @@ -151,15 +151,20 @@ def repo( git_dir = unix_path_join(dest, ".git") is_repo = host.get_fact(Directory, path=git_dir) + # The depth argument can be passed to clone, fetch and pull operations. + # We prepare the argument list to reuse it later on + depth_options: list[str | QuoteString] = [] + if depth is not None: + depth_options.extend(["--depth", str(depth)]) + # Cloning new repo? if not is_repo: - options: list[str | QuoteString] = [] - if depth is not None: - options.extend(["--depth", str(depth)]) + clone_options: list[str | QuoteString] = [] + clone_options.extend(depth_options) if branch: - options.extend(["--branch", QuoteString(branch)]) + clone_options.extend(["--branch", QuoteString(branch)]) - git_commands.append(StringCommand("clone", QuoteString(src), *options, ".")) + git_commands.append(StringCommand("clone", QuoteString(src), *clone_options, ".")) # Ensuring existing repo else: @@ -177,10 +182,11 @@ def repo( current_branch = host.get_fact(GitBranch, repo=dest) if branch is not None and current_branch != branch: # fetch to ensure we have the branch/tag locally + fetch_options: list[str | QuoteString] = [] + fetch_options.extend(depth_options) if fetch_tags: - git_commands.append(StringCommand("fetch", "--tags")) - else: - git_commands.append(StringCommand("fetch")) + fetch_options.append("--tags") + git_commands.append(StringCommand("fetch", *fetch_options)) git_commands.append(StringCommand("checkout", QuoteString(branch))) if branch and branch in (host.get_fact(GitTag, repo=dest) or []): @@ -209,10 +215,12 @@ def repo( host.noop( f"git repository {dest} is already up to date", ) - elif rebase: - git_commands.append("pull --rebase") else: - git_commands.append("pull") + pull_options: list[str | QuoteString] = [] + pull_options.extend(depth_options) + if rebase: + pull_options.append("--rebase") + git_commands.append(StringCommand("pull", *pull_options)) if update_submodules: if recursive_submodules: