git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

git: add contract tests for force-push resilience, error recovery, and edge cases - #65750

Merged
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests
Apr 25, 2026
Merged

git: add contract tests for force-push resilience, error recovery, and edge cases#65750
potiuk merged 1 commit into
apache:mainfrom
Dev-iL:2604/git_tests

Conversation

@Dev-iL

Copy link
Copy Markdown
Collaborator

Summary

Add 12 unit tests to the Git DAG bundle provider, expanding test coverage for force-push resilience, error recovery, and edge cases. The new tests cover previously untested defensive paths.

Context

These tests were motivated by an investigation into repeated recloning behavior observed after force-pushing a tracked tag.

Force-push resilience (6 tests)

TestWhat it proves
test_refresh_tag_force_pushed_to_unrelated_commitTag moved to orphan commit is followed
test_refresh_branch_force_pushed_to_unrelated_commitBranch equivalent of the above
test_refresh_tag_moved_forward_and_backwardNon-monotonic tag movement across multiple refreshes
test_refresh_after_force_push_does_not_recloneRepo.clone_from is never called during refresh
test_repeated_refreshes_after_force_push_stablePost-force-push state doesn't degrade
test_reinitialize_reuses_repos_after_force_pushNew bundle object reuses existing repos (processor restart)

Error handling (4 tests)

TestWhat it proves
test_refresh_survives_upstream_tag_deletionDeleted upstream tags persist locally; refresh succeeds
test_refresh_failure_preserves_previous_checkoutFailed fetch leaves working tree intact
test_refresh_versioned_bundle_raisesrefresh() on versioned bundle raises AirflowException
test_clone_repo_invalid_repository_error_retryCorrupted working repo triggers cleanup + retry

Edge cases (2 tests)

TestWhat it proves
test_refresh_with_real_submodules_after_ref_changeReal submodule (two repos) stays intact through ref change
test_refresh_ambiguous_ref_prefers_branch_over_tagDocuments branch-over-tag preference when names collide

Notable finding

git reset --hard fails when the target tree references a different submodule commit than the current checkout, because the existing mysub/.git file triggers git's invalid-path protection (verified on git 2.43). This means refresh() with submodules=True cannot handle upstream submodule reference changes. A fix would require deiniting submodules before the reset. This is a pre-existing limitation, not introduced by this PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 4.6 and GPT 5.4-Mini following the guidelines
-->


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@Dev-iLDev-iL changed the title Add tests for git bundle force-push resilience, error recovery, and edge casesgit: add contract tests to force-push resilience, error recovery, and edge casesApr 24, 2026
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
@Dev-iLDev-iL changed the title git: add contract tests to force-push resilience, error recovery, and edge casesgit: add contract tests for force-push resilience, error recovery, and edge casesApr 24, 2026
@potiuk
potiuk merged commit 19ae9ee into apache:mainApr 25, 2026
88 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

@Dev-iL
Dev-iL deleted the 2604/git_tests branch April 26, 2026 05:58
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
potiuk pushed a commit that referenced this pull request Apr 26, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
…overy, etc. (#65750) (#65854)
Expand GitDagBundle test coverage with 12 new tests addressing gaps
identified during investigation of repeated recloning after tag force-push.
Force-push resilience:
- Branch force-pushed to unrelated commit follows the new ref
- Tag force-pushed to unrelated commit follows the new ref
- Tag moved forward then backward across multiple refreshes
- Refresh after force-push never triggers Repo.clone_from
- Repeated refreshes after force-push remain stable
- Re-initialization reuses existing repos (no reclone)
Error handling:
- Upstream tag deletion does not break refresh (local copy persists)
- Failed bare-repo fetch preserves previous working tree
- Refresh on versioned bundle raises AirflowException
- Corrupted working repo triggers cleanup and retry
Edge cases:
- Real submodule fixture with ref change and submodule sync
- Ambiguous ref (branch+tag same name) documents branch preference
(cherry picked from commit 19ae9ee)
Co-authored-by: Dev-iL <6509619+Dev-iL@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Dev-iL@potiuk