ctrlrelay 0.11.2__tar.gz → 0.11.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/CHANGELOG.md +16 -1
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/PKG-INFO +1 -1
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/pyproject.toml +1 -1
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/worktree.py +29 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_worktree.py +49 -3
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/dependabot.yml +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/workflows/build.yml +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/workflows/cla.yml +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/workflows/pages.yml +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/workflows/publish.yml +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.github/workflows/test.yml +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/.gitignore +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/CODE_OF_CONDUCT.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/CONTRIBUTING.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/LICENSE +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/README.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/SECURITY.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/config/orchestrator.yaml.example +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/Gemfile +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/_config.yml +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/architecture.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/bridge.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/cli.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/configuration.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/development.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/feedback-loop.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/getting-started.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/index.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/operations.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/docs/personalization.md +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/bridge/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/bridge/__main__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/bridge/protocol.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/bridge/server.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/bridge/telegram_handler.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/cli.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/archived.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/audit.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/checkpoint.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/code_review.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/config.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/dispatcher.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/github.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/network.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/obs.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/poller.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/pr_verifier.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/pr_watcher.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/question_expiry.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/scheduler.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/core/state.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/dashboard/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/dashboard/client.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/gh_protocol.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/install.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/personalization/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/personalization/manager.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/personalization/paths.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/pipelines/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/pipelines/base.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/pipelines/dev.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/pipelines/post_merge.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/pipelines/secops.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/pipelines/task.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/setup.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/global-CLAUDE.md.snippet +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/launchd/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/launchd/bridge.plist.template +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/launchd/poller.plist.template +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/systemd/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/systemd/bridge.service.template +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/systemd/poller.service.template +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/transports/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/transports/base.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/transports/file_mock.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/transports/socket_client.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/__init__.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/conftest.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_archived.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_audit.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_bridge_protocol.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_bridge_server.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_checkpoint.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_cli_ci_wait.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_cli_dev.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_cli_repos.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_cli_secops.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_cli_start.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_cli_version.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_code_review.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_config.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_dashboard_client.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_dev_integration.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_dev_pipeline.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_dispatcher.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_docs_site.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_gh_protocol.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_github.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_install.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_network.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_obs.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_personalization.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_pipeline_base.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_poller.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_post_merge.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_pr_verifier.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_pr_watcher.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_question_expiry.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_scheduler.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_secops_integration.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_secops_pipeline.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_setup.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_state.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_task_pipeline.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_telegram_handler.py +0 -0
- {ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/tests/test_transport.py +0 -0
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.11.3] - 2026-09-12
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **A force-pushed default branch fails the fetch again.** v0.11.2
|
|
15
|
+
tolerated every non-fast-forward rejection so a rebased Dependabot
|
|
16
|
+
branch would stop aborting sweeps. That also covered the default
|
|
17
|
+
branch, which sessions are cut from: after a force-push to `main` on
|
|
18
|
+
GitHub, the bare repo would keep its old `main` and every session
|
|
19
|
+
would run on a tree origin no longer has, with only a log line to show
|
|
20
|
+
for it. A rejection of the default branch now raises
|
|
21
|
+
`default branch diverged from origin`; other branches are still
|
|
22
|
+
tolerated. Closes #120.
|
|
23
|
+
|
|
10
24
|
## [0.11.2] - 2026-09-10
|
|
11
25
|
|
|
12
26
|
### Fixed
|
|
@@ -1644,7 +1658,8 @@ pipeline).
|
|
|
1644
1658
|
per-phase implementation plans (Phase 0 through Phase 4).
|
|
1645
1659
|
- `docs/Claude_Code_Project_Guide.md` — project development guide.
|
|
1646
1660
|
|
|
1647
|
-
[Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.11.
|
|
1661
|
+
[Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.11.3...HEAD
|
|
1662
|
+
[0.11.3]: https://github.com/AInvirion/ctrlrelay/compare/v0.11.2...v0.11.3
|
|
1648
1663
|
[0.11.2]: https://github.com/AInvirion/ctrlrelay/compare/v0.11.1...v0.11.2
|
|
1649
1664
|
[0.11.1]: https://github.com/AInvirion/ctrlrelay/compare/v0.11.0...v0.11.1
|
|
1650
1665
|
[0.11.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.10.0...v0.11.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ctrlrelay
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.3
|
|
4
4
|
Summary: Local-first orchestrator for headless coding agents across multiple GitHub repos
|
|
5
5
|
Project-URL: Homepage, https://github.com/AInvirion/ctrlrelay
|
|
6
6
|
Project-URL: Documentation, https://ainvirion.github.io/ctrlrelay/
|
|
@@ -122,6 +122,7 @@ class WorktreeManager:
|
|
|
122
122
|
cwd: Path | None = None,
|
|
123
123
|
timeout: int | None = None,
|
|
124
124
|
tolerate_non_fast_forward: bool = False,
|
|
125
|
+
protected_refs: frozenset[str] = frozenset(),
|
|
125
126
|
) -> str:
|
|
126
127
|
"""Run git command and return stdout. `timeout` overrides self.timeout
|
|
127
128
|
for one call — useful for cheap probes that shouldn't inherit the full
|
|
@@ -131,6 +132,11 @@ class WorktreeManager:
|
|
|
131
132
|
refspec: git exits 1 when it declines to rewind a diverged local
|
|
132
133
|
branch, even though every other ref updated. See
|
|
133
134
|
:func:`_rejections_are_only_non_fast_forward`.
|
|
135
|
+
|
|
136
|
+
``protected_refs`` names branches whose rejection is never
|
|
137
|
+
tolerated. A declined rewind of the default branch means local
|
|
138
|
+
and origin have diverged there, and every worktree cut from it
|
|
139
|
+
would run against a tree origin no longer has.
|
|
134
140
|
"""
|
|
135
141
|
cmd = ["git", *args]
|
|
136
142
|
effective_timeout = timeout if timeout is not None else self.timeout
|
|
@@ -186,6 +192,13 @@ class WorktreeManager:
|
|
|
186
192
|
)
|
|
187
193
|
if not declined:
|
|
188
194
|
raise WorktreeError(f"git failed: {err}")
|
|
195
|
+
stuck = sorted(set(declined) & protected_refs)
|
|
196
|
+
if stuck:
|
|
197
|
+
raise WorktreeError(
|
|
198
|
+
f"git failed: default branch diverged from origin "
|
|
199
|
+
f"({', '.join(stuck)}); refusing to use a stale tree: "
|
|
200
|
+
f"{err}"
|
|
201
|
+
)
|
|
189
202
|
log_event(
|
|
190
203
|
_logger,
|
|
191
204
|
"worktree.fetch.non_fast_forward",
|
|
@@ -864,10 +877,22 @@ class WorktreeManager:
|
|
|
864
877
|
Git exits 1 on a declined rewind even when every other ref
|
|
865
878
|
applied, and that exit code was aborting the whole session
|
|
866
879
|
for a branch it had correctly protected.
|
|
880
|
+
|
|
881
|
+
The default branch is the exception: a rejection there still
|
|
882
|
+
raises. Sessions are cut from it, so tolerating that one would
|
|
883
|
+
swap a loud failure for work done quietly on a stale tree —
|
|
884
|
+
e.g. after a force-push to ``main`` on GitHub.
|
|
867
885
|
"""
|
|
868
886
|
bare_path = self._get_bare_repo_path(repo)
|
|
869
887
|
|
|
870
888
|
if bare_path.exists():
|
|
889
|
+
try:
|
|
890
|
+
default_branch = await self.get_default_branch(repo)
|
|
891
|
+
except WorktreeError:
|
|
892
|
+
# A bare repo with no symbolic HEAD cannot name a default
|
|
893
|
+
# branch to protect. create_worktree fails on the same
|
|
894
|
+
# lookup, so the stale-tree risk never reaches a session.
|
|
895
|
+
default_branch = ""
|
|
871
896
|
await self._run_git(
|
|
872
897
|
"fetch", "--prune", "origin",
|
|
873
898
|
_BOT_BRANCH_REFSPEC,
|
|
@@ -875,6 +900,10 @@ class WorktreeManager:
|
|
|
875
900
|
cwd=bare_path,
|
|
876
901
|
timeout=_GIT_TRANSFER_TIMEOUT_SECONDS,
|
|
877
902
|
tolerate_non_fast_forward=True,
|
|
903
|
+
protected_refs=(
|
|
904
|
+
frozenset({default_branch}) if default_branch
|
|
905
|
+
else frozenset()
|
|
906
|
+
),
|
|
878
907
|
)
|
|
879
908
|
else:
|
|
880
909
|
await self._run_git(
|
|
@@ -79,12 +79,14 @@ class TestWorktreeManager:
|
|
|
79
79
|
mock_git.return_value = ""
|
|
80
80
|
await manager.ensure_bare_repo("owner/repo")
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
# symbolic-ref (default-branch lookup), then the fetch.
|
|
83
|
+
assert mock_git.await_count == 2
|
|
84
|
+
assert mock_git.await_args_list[0].args[0] == "symbolic-ref"
|
|
83
85
|
args = mock_git.await_args.args
|
|
84
86
|
# Must be a fetch, not --all, with the explicit refspec.
|
|
85
87
|
assert args[0] == "fetch"
|
|
86
88
|
assert "origin" in args
|
|
87
|
-
# Non-force refspec on purpose:
|
|
89
|
+
# Non-force refspec on purpose: a force fetch
|
|
88
90
|
# would destroy unpushed local commits in the dev pipeline's
|
|
89
91
|
# branch-reuse flow. Fast-forward-only fixes the stale-
|
|
90
92
|
# default-branch case while leaving divergent dev branches
|
|
@@ -1597,7 +1599,10 @@ class TestGitTimeoutIsDiagnosable:
|
|
|
1597
1599
|
wt._get_bare_repo_path("owner/repo").mkdir(parents=True, exist_ok=True)
|
|
1598
1600
|
wt._run_git.reset_mock()
|
|
1599
1601
|
await wt.ensure_bare_repo("owner/repo")
|
|
1600
|
-
fetch_kwargs =
|
|
1602
|
+
fetch_kwargs = next(
|
|
1603
|
+
c.kwargs for c in wt._run_git.await_args_list
|
|
1604
|
+
if c.args and c.args[0] == "fetch"
|
|
1605
|
+
)
|
|
1601
1606
|
assert fetch_kwargs["timeout"] == _GIT_TRANSFER_TIMEOUT_SECONDS
|
|
1602
1607
|
|
|
1603
1608
|
|
|
@@ -1726,6 +1731,47 @@ class TestFetchSurvivesRewrittenBranches:
|
|
|
1726
1731
|
await wt.ensure_bare_repo("owner/repo")
|
|
1727
1732
|
|
|
1728
1733
|
|
|
1734
|
+
@pytest.mark.asyncio
|
|
1735
|
+
async def test_force_pushed_default_branch_still_raises(
|
|
1736
|
+
self, tmp_path: Path
|
|
1737
|
+
) -> None:
|
|
1738
|
+
"""Sessions are cut from the default branch. If origin rewrote it,
|
|
1739
|
+
tolerating the rejection would run every session on a tree origin
|
|
1740
|
+
no longer has — quietly, instead of loudly."""
|
|
1741
|
+
import subprocess
|
|
1742
|
+
|
|
1743
|
+
from ctrlrelay.core.worktree import WorktreeError, WorktreeManager
|
|
1744
|
+
|
|
1745
|
+
origin, bare = self._build_repos(tmp_path)
|
|
1746
|
+
|
|
1747
|
+
# Rewrite main on origin, and give the bare clone its own commit
|
|
1748
|
+
# on main so the two have diverged.
|
|
1749
|
+
self._git("checkout", "-q", "main", cwd=origin)
|
|
1750
|
+
(origin / "f").write_text("rewritten\n")
|
|
1751
|
+
self._git("commit", "-qam", "x", cwd=origin)
|
|
1752
|
+
self._git("commit", "-q", "--amend", "-m", "force-pushed main",
|
|
1753
|
+
cwd=origin)
|
|
1754
|
+
tree = self._git("rev-parse", "refs/heads/main^{tree}",
|
|
1755
|
+
cwd=bare).strip()
|
|
1756
|
+
sha = self._git("commit-tree", tree, "-p", "refs/heads/main",
|
|
1757
|
+
"-m", "local main", cwd=bare).strip()
|
|
1758
|
+
self._git("update-ref", "refs/heads/main", sha, cwd=bare)
|
|
1759
|
+
|
|
1760
|
+
wt = WorktreeManager(
|
|
1761
|
+
worktrees_dir=tmp_path / "wt", bare_repos_dir=tmp_path / "repos",
|
|
1762
|
+
)
|
|
1763
|
+
target = wt._get_bare_repo_path("owner/repo")
|
|
1764
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
1765
|
+
bare.rename(target)
|
|
1766
|
+
subprocess.run(
|
|
1767
|
+
["git", "remote", "set-url", "origin", str(origin)],
|
|
1768
|
+
cwd=target, check=True,
|
|
1769
|
+
)
|
|
1770
|
+
|
|
1771
|
+
with pytest.raises(WorktreeError, match="default branch diverged"):
|
|
1772
|
+
await wt.ensure_bare_repo("owner/repo")
|
|
1773
|
+
|
|
1774
|
+
|
|
1729
1775
|
class TestNonFastForwardClassifier:
|
|
1730
1776
|
"""`_rejections_are_only_non_fast_forward` decides whether a
|
|
1731
1777
|
non-zero `git fetch` is the refspec doing its job or a real
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/systemd/bridge.service.template
RENAMED
|
File without changes
|
{ctrlrelay-0.11.2 → ctrlrelay-0.11.3}/src/ctrlrelay/templates/systemd/poller.service.template
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|