@@ -48,25 +48,82 @@ steps:
4848
4949 print(f"Found {len(branches)} autoloop branch(es) to sync: {branches}")
5050
51+ def rev_count(range_spec):
52+ r = subprocess.run(
53+ ["git", "rev-list", "--count", range_spec],
54+ capture_output=True, text=True
55+ )
56+ if r.returncode != 0:
57+ return None
58+ try:
59+ return int(r.stdout.strip())
60+ except ValueError:
61+ return None
62+
5163 failed = []
5264 for branch in branches:
5365 print(f"\n--- Syncing {branch} with {default_branch} ---")
5466
55- # Fetch both branches
67+ # Fetch both branches so the ahead/behind counts below are computed
68+ # against up-to-date local copies of the remote tips.
5669 subprocess.run(["git", "fetch", "origin", branch], capture_output=True)
5770 subprocess.run(["git", "fetch", "origin", default_branch], capture_output=True)
5871
59- # Check out the program branch
72+ # Compute ahead/behind counts using the remote-tracking refs so we
73+ # make a decision based on commit delta (not content delta).
74+ ahead = rev_count(f"origin/{default_branch}..origin/{branch}")
75+ behind = rev_count(f"origin/{branch}..origin/{default_branch}")
76+ if ahead is None or behind is None:
77+ print(f" Failed to compute ahead/behind for {branch}")
78+ failed.append(branch)
79+ continue
80+ print(f" ahead={ahead} behind={behind}")
81+
82+ if ahead == 0 and behind > 0:
83+ # All of the branch's commits are already in the default branch.
84+ # Merging would produce a noisy "Merge main into branch" commit
85+ # that re-exposes every historical file as a patch touch — the
86+ # failure mode that triggers gh-aw's E003 (>100 files) when a
87+ # new PR is opened. Fast-forward the canonical branch instead.
88+ # This is lossless because ahead=0 proves every commit on the
89+ # branch is already reachable from the default branch.
90+ ff = subprocess.run(
91+ ["git", "checkout", "-B", branch, f"origin/{default_branch}"],
92+ capture_output=True, text=True
93+ )
94+ if ff.returncode != 0:
95+ print(f" Failed to fast-forward {branch}: {ff.stderr}")
96+ failed.append(branch)
97+ continue
98+ # Use --force-with-lease so that if anyone else is simultaneously
99+ # pushing to the branch, the update is rejected rather than
100+ # overwriting their commits.
101+ push = subprocess.run(
102+ ["git", "push", "--force-with-lease", "origin", branch],
103+ capture_output=True, text=True
104+ )
105+ if push.returncode != 0:
106+ print(f" Failed to force-push {branch}: {push.stderr}")
107+ failed.append(branch)
108+ continue
109+ print(f" Fast-forwarded {branch} to origin/{default_branch}")
110+ continue
111+
112+ if ahead == 0 and behind == 0:
113+ # Already at default branch — nothing to do.
114+ print(f" {branch} is already up to date with origin/{default_branch}")
115+ continue
116+
117+ if ahead > 0 and behind == 0:
118+ # Unique work preserved; no upstream drift to merge.
119+ print(f" {branch} is ahead of origin/{default_branch} with no upstream drift; nothing to merge.")
120+ continue
121+
122+ # True divergence (ahead > 0 and behind > 0): check out and merge.
60123 checkout = subprocess.run(
61- ["git", "checkout", branch],
124+ ["git", "checkout", "-B", branch, f"origin/{branch}" ],
62125 capture_output=True, text=True
63126 )
64- if checkout.returncode != 0:
65- # Try creating a local tracking branch
66- checkout = subprocess.run(
67- ["git", "checkout", "-b", branch, f"origin/{branch}"],
68- capture_output=True, text=True
69- )
70127 if checkout.returncode != 0:
71128 print(f" Failed to checkout {branch}: {checkout.stderr}")
72129 failed.append(branch)
0 commit comments