gitgrip 1.5.0__py3-none-any.whl
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.
- gitgrip-1.5.0.dist-info/METADATA +13 -0
- gitgrip-1.5.0.dist-info/RECORD +80 -0
- gitgrip-1.5.0.dist-info/WHEEL +5 -0
- gitgrip-1.5.0.dist-info/entry_points.txt +2 -0
- gitgrip-1.5.0.dist-info/top_level.txt +2 -0
- gr2/__init__.py +0 -0
- gr2/overlay/__init__.py +6 -0
- gr2/overlay/activate.py +196 -0
- gr2/overlay/agent_manifest.py +138 -0
- gr2/overlay/cli.py +181 -0
- gr2/overlay/cross_repo.py +124 -0
- gr2/overlay/drivers.py +113 -0
- gr2/overlay/introspection.py +155 -0
- gr2/overlay/language_drivers.py +115 -0
- gr2/overlay/objects.py +412 -0
- gr2/overlay/perf.py +251 -0
- gr2/overlay/refs.py +36 -0
- gr2/overlay/trust.py +150 -0
- gr2/overlay/types.py +69 -0
- gr2/overlay/units.py +313 -0
- gr2/overlay/workspace_spec.py +59 -0
- gr2/prototypes/__init__.py +0 -0
- gr2/prototypes/cache_materialization_probe.py +190 -0
- gr2/prototypes/concurrent_event_stress.py +199 -0
- gr2/prototypes/concurrent_lease_stress.py +240 -0
- gr2/prototypes/concurrent_workspace_cap_stress.py +231 -0
- gr2/prototypes/contribution_protocol.py +665 -0
- gr2/prototypes/cross_mode_lane_stress.py +986 -0
- gr2/prototypes/jsonl_store.py +158 -0
- gr2/prototypes/lane_workspace_prototype.py +2088 -0
- gr2/prototypes/layout_model_probe.py +139 -0
- gr2/prototypes/propagation_daemon.py +546 -0
- gr2/prototypes/propagation_state_machine.py +1478 -0
- gr2/prototypes/python_exec_playground.py +194 -0
- gr2/prototypes/python_hook_runtime_playground.py +240 -0
- gr2/prototypes/python_migration_playground.py +144 -0
- gr2/prototypes/python_review_checkout_playground.py +242 -0
- gr2/prototypes/python_spec_apply_playground.py +282 -0
- gr2/prototypes/real_git_lane_materialization.py +248 -0
- gr2/prototypes/real_git_playground.py +334 -0
- gr2/prototypes/recall_lane_history.py +274 -0
- gr2/prototypes/repo_maintenance_prototype.py +659 -0
- gr2/prototypes/repo_transport_probe.py +147 -0
- gr2/python_cli/__init__.py +2 -0
- gr2/python_cli/__main__.py +6 -0
- gr2/python_cli/add.py +51 -0
- gr2/python_cli/app.py +2516 -0
- gr2/python_cli/branch.py +67 -0
- gr2/python_cli/channel_bridge.py +131 -0
- gr2/python_cli/clone_exec.py +1019 -0
- gr2/python_cli/commit.py +199 -0
- gr2/python_cli/config.py +291 -0
- gr2/python_cli/env_exec.py +419 -0
- gr2/python_cli/events.py +529 -0
- gr2/python_cli/execops.py +372 -0
- gr2/python_cli/failures.py +98 -0
- gr2/python_cli/file_exec.py +256 -0
- gr2/python_cli/gitops.py +226 -0
- gr2/python_cli/grip.py +1337 -0
- gr2/python_cli/grip_cli.py +493 -0
- gr2/python_cli/hooks.py +450 -0
- gr2/python_cli/launch_exec.py +786 -0
- gr2/python_cli/merge_verification.py +274 -0
- gr2/python_cli/migration.py +985 -0
- gr2/python_cli/open_gr_review.py +699 -0
- gr2/python_cli/platform.py +441 -0
- gr2/python_cli/pr.py +487 -0
- gr2/python_cli/project_review.py +314 -0
- gr2/python_cli/prune.py +365 -0
- gr2/python_cli/push.py +172 -0
- gr2/python_cli/review.py +462 -0
- gr2/python_cli/review_ephemeral.py +143 -0
- gr2/python_cli/review_run.py +621 -0
- gr2/python_cli/spec_apply.py +1285 -0
- gr2/python_cli/staging_cleanup.py +205 -0
- gr2/python_cli/syncops.py +920 -0
- gr2/python_cli/target.py +100 -0
- gr2/python_cli/workspace_snapshot.py +105 -0
- gr2/schemas/gr2-materialization-plan-v1.schema.json +191 -0
- gr2_overlay/__init__.py +37 -0
gr2/python_cli/branch.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Native `branch` command (grip#752 slice 1, D3).
|
|
2
|
+
|
|
3
|
+
Create/switch, --base support (grip#746: gr1's `gr branch` has no base-ref
|
|
4
|
+
argument, forcing raw-git fallbacks), existing-branch-switches-not-errors
|
|
5
|
+
(mirrors gr1's grip#401 fix), worktree-conflict detection (mirrors gr1's
|
|
6
|
+
real, cited pain -- grip#750).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from .gitops import git
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BranchError(Exception):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _branch_exists(repo: Path, name: str) -> bool:
|
|
21
|
+
proc = git(repo, "rev-parse", "--verify", f"refs/heads/{name}")
|
|
22
|
+
return proc.returncode == 0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _ref_exists(repo: Path, ref: str) -> bool:
|
|
26
|
+
proc = git(repo, "rev-parse", "--verify", ref)
|
|
27
|
+
return proc.returncode == 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _raise_for_checkout_failure(name: str, stderr: str) -> None:
|
|
31
|
+
if "is already used by worktree at" in stderr:
|
|
32
|
+
marker = "worktree at '"
|
|
33
|
+
start = stderr.find(marker)
|
|
34
|
+
if start != -1:
|
|
35
|
+
start += len(marker)
|
|
36
|
+
end = stderr.find("'", start)
|
|
37
|
+
worktree_path = stderr[start:end] if end != -1 else "another worktree"
|
|
38
|
+
raise BranchError(
|
|
39
|
+
f"Branch '{name}' is checked out in another worktree at '{worktree_path}'. "
|
|
40
|
+
f"Use that worktree or choose a different branch name."
|
|
41
|
+
)
|
|
42
|
+
raise BranchError(f"Branch '{name}' is already checked out in another worktree.")
|
|
43
|
+
raise BranchError(stderr.strip())
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def create_branch(repo: Path, name: str, base: str | None = None) -> None:
|
|
47
|
+
"""Create and switch to branch `name` in `repo`.
|
|
48
|
+
|
|
49
|
+
If `name` already exists, switch to it instead of erroring (grip#401).
|
|
50
|
+
If `base` is given, the branch is created (or reset, if it already
|
|
51
|
+
exists) to point at `base` -- resolved per-repo, matching `git checkout
|
|
52
|
+
-B`'s own create-or-reset semantics. A missing `base` ref raises
|
|
53
|
+
immediately, before any git mutation -- never a silent fallback to HEAD
|
|
54
|
+
(grip#746's acceptance criteria).
|
|
55
|
+
"""
|
|
56
|
+
if base is not None and not _ref_exists(repo, base):
|
|
57
|
+
raise BranchError(f"base ref '{base}' does not exist in {repo}")
|
|
58
|
+
|
|
59
|
+
if base is not None:
|
|
60
|
+
proc = git(repo, "checkout", "-B", name, base)
|
|
61
|
+
elif _branch_exists(repo, name):
|
|
62
|
+
proc = git(repo, "checkout", name)
|
|
63
|
+
else:
|
|
64
|
+
proc = git(repo, "checkout", "-b", name)
|
|
65
|
+
|
|
66
|
+
if proc.returncode != 0:
|
|
67
|
+
_raise_for_checkout_failure(name, proc.stderr)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""gr2 channel bridge consumer.
|
|
2
|
+
|
|
3
|
+
Translates outbox events into channel messages per the mapping table in
|
|
4
|
+
HOOK-EVENT-CONTRACT.md section 8. Uses cursor-based consumption from
|
|
5
|
+
events.read_events_detailed(), which also reports lines it could not read.
|
|
6
|
+
|
|
7
|
+
The bridge is a pure function layer: format_event() maps an event dict to
|
|
8
|
+
a message string (or None), and run_bridge() orchestrates cursor reads and
|
|
9
|
+
posts via a caller-provided post_fn. This keeps the MCP/recall_channel
|
|
10
|
+
dependency out of the module and makes it fully testable.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Callable
|
|
16
|
+
|
|
17
|
+
from .events import read_events_detailed, warn_unreadable
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_CONSUMER_NAME = "channel_bridge"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def format_event(event: dict[str, object]) -> str | None:
|
|
24
|
+
"""Apply the section 8 mapping table to produce a channel message.
|
|
25
|
+
|
|
26
|
+
Returns None if the event type is not mapped (silently dropped).
|
|
27
|
+
"""
|
|
28
|
+
etype = event.get("type", "")
|
|
29
|
+
|
|
30
|
+
if etype == "lane.created":
|
|
31
|
+
return (
|
|
32
|
+
f"{event['actor']} created lane {event['lane_name']}"
|
|
33
|
+
f" [{event.get('lane_type', 'unknown')}]"
|
|
34
|
+
f" repos={event.get('repos', [])}"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if etype == "lane.entered":
|
|
38
|
+
return f"{event['actor']} entered {event['owner_unit']}/{event['lane_name']}"
|
|
39
|
+
|
|
40
|
+
if etype == "lane.exited":
|
|
41
|
+
return f"{event['actor']} exited {event['owner_unit']}/{event['lane_name']}"
|
|
42
|
+
|
|
43
|
+
if etype == "pr.created":
|
|
44
|
+
repos = event.get("repos", [])
|
|
45
|
+
if isinstance(repos, list) and repos and isinstance(repos[0], dict):
|
|
46
|
+
repo_names = [r.get("repo", "") for r in repos]
|
|
47
|
+
else:
|
|
48
|
+
repo_names = repos
|
|
49
|
+
return (
|
|
50
|
+
f"{event['actor']} opened PR group {event['pr_group_id']}: {repo_names}"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if etype == "pr.merged":
|
|
54
|
+
return f"{event['actor']} merged PR group {event['pr_group_id']}"
|
|
55
|
+
|
|
56
|
+
if etype == "pr.checks_failed":
|
|
57
|
+
failed = event.get("failed_checks", [])
|
|
58
|
+
return f"CI failed on {event['repo']}#{event['pr_number']}: {failed}"
|
|
59
|
+
|
|
60
|
+
if etype == "hook.failed":
|
|
61
|
+
# Only blocking hook failures produce channel messages.
|
|
62
|
+
if event.get("on_failure") != "block":
|
|
63
|
+
return None
|
|
64
|
+
return (
|
|
65
|
+
f"Hook {event['hook_name']} failed in {event['repo']}"
|
|
66
|
+
f" (blocking): {event.get('stderr_tail', '')}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if etype == "sync.conflict":
|
|
70
|
+
files = event.get("conflicting_files", [])
|
|
71
|
+
return f"Sync conflict in {event['repo']}: {files}"
|
|
72
|
+
|
|
73
|
+
if etype == "lease.force_broken":
|
|
74
|
+
return (
|
|
75
|
+
f"Lease on {event['lane_name']} force-broken"
|
|
76
|
+
f" by {event['broken_by']}: {event.get('reason', '')}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if etype == "failure.resolved":
|
|
80
|
+
return (
|
|
81
|
+
f"{event['resolved_by']} resolved failure"
|
|
82
|
+
f" {event['operation_id']} on {event['lane_name']}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if etype == "lease.reclaimed":
|
|
86
|
+
return (
|
|
87
|
+
f"Stale lease on {event['lane_name']} reclaimed"
|
|
88
|
+
f" (was held by {event['previous_holder']})"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Unmapped event type: silently dropped.
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def run_bridge(
|
|
96
|
+
workspace_root: Path,
|
|
97
|
+
*,
|
|
98
|
+
post_fn: Callable[[str], object],
|
|
99
|
+
) -> int:
|
|
100
|
+
"""Read new events from the outbox and post mapped messages.
|
|
101
|
+
|
|
102
|
+
Uses the 'channel_bridge' cursor. Returns the number of messages posted.
|
|
103
|
+
The post_fn receives formatted message strings; the caller decides how to
|
|
104
|
+
deliver them (recall_channel, print, log, etc.).
|
|
105
|
+
"""
|
|
106
|
+
read = read_events_detailed(workspace_root, _CONSUMER_NAME)
|
|
107
|
+
# An unreadable outbox line is a message that never reaches a channel, and
|
|
108
|
+
# the bridge is the only place that says so.
|
|
109
|
+
#
|
|
110
|
+
# It is reported on EVERY read, not once: the cursor filter compares seq
|
|
111
|
+
# against the cursor and only lines that PARSE have a usable seq, so an
|
|
112
|
+
# unreadable line can never be advanced past, wherever it sits in the file
|
|
113
|
+
# (witnessed terminal AND mid-file). An UNTERMINATED record never reaches
|
|
114
|
+
# this report at all: the reader splits on b"\n", so a COMPLETE record whose
|
|
115
|
+
# only loss was its terminator is the final chunk and parses on the spot --
|
|
116
|
+
# before any emit, repair or no repair (W11, and its truncated control).
|
|
117
|
+
# What seam repair protects is the NEXT append, which would otherwise be
|
|
118
|
+
# glued onto it. So everything reported here is a TRUNCATED record, and for
|
|
119
|
+
# those the report is permanent: a bridge polling in a loop warns every
|
|
120
|
+
# cycle until someone repairs the file. That cost is real and is recorded
|
|
121
|
+
# as a residual.
|
|
122
|
+
#
|
|
123
|
+
# stderr keeps stdout parseable for callers that consume the posted count.
|
|
124
|
+
warn_unreadable(read)
|
|
125
|
+
posted = 0
|
|
126
|
+
for event in read.events:
|
|
127
|
+
msg = format_event(event)
|
|
128
|
+
if msg is not None:
|
|
129
|
+
post_fn(msg)
|
|
130
|
+
posted += 1
|
|
131
|
+
return posted
|