@@ -82,7 +82,68 @@ steps:
8282 failed.append(branch)
8383 continue
8484
85- # Merge the default branch into the program branch
85+ # Determine whether the branch can be fast-forwarded to the default
86+ # branch (ahead=0) or has truly diverged and needs a merge.
87+ ahead_proc = subprocess.run(
88+ ["git", "rev-list", "--count", f"origin/{default_branch}..origin/{branch}"],
89+ capture_output=True, text=True,
90+ )
91+ behind_proc = subprocess.run(
92+ ["git", "rev-list", "--count", f"origin/{branch}..origin/{default_branch}"],
93+ capture_output=True, text=True,
94+ )
95+ if ahead_proc.returncode != 0 or behind_proc.returncode != 0:
96+ # Don't guess — a failed rev-list with empty stdout would
97+ # otherwise be parsed as 0 below and could trigger an
98+ # incorrect fast-forward that loses commits.
99+ print(f" Failed to compute ahead/behind for {branch}: "
100+ f"ahead.rc={ahead_proc.returncode} stderr={ahead_proc.stderr.strip()!r} "
101+ f"behind.rc={behind_proc.returncode} stderr={behind_proc.stderr.strip()!r}")
102+ failed.append(branch)
103+ continue
104+ try:
105+ ahead = int(ahead_proc.stdout.strip())
106+ behind = int(behind_proc.stdout.strip())
107+ except ValueError:
108+ print(f" Failed to parse ahead/behind counts for {branch}: "
109+ f"ahead={ahead_proc.stdout!r} behind={behind_proc.stdout!r}")
110+ failed.append(branch)
111+ continue
112+
113+ if behind == 0:
114+ # Branch already contains every commit on the default branch.
115+ print(f" {branch} is already up to date with {default_branch} (ahead={ahead}, behind=0)")
116+ continue
117+
118+ if ahead == 0:
119+ # Lossless fast-forward: every commit on the branch is already
120+ # reachable from the default branch (typical case once the
121+ # previous iteration's PR has been merged). A real merge here
122+ # would create a "Merge default into branch" commit that re-
123+ # exposes every historical file as a patch touch — the noise
124+ # that trips gh-aw's MAX_FILES limit when the next iteration
125+ # opens a new PR. Reset the branch to the default branch's HEAD
126+ # and force-push (with lease) instead.
127+ reset = subprocess.run(
128+ ["git", "reset", "--hard", f"origin/{default_branch}"],
129+ capture_output=True, text=True,
130+ )
131+ if reset.returncode != 0:
132+ print(f" Failed to fast-forward {branch}: {reset.stderr}")
133+ failed.append(branch)
134+ continue
135+ push = subprocess.run(
136+ ["git", "push", "--force-with-lease", "origin", branch],
137+ capture_output=True, text=True,
138+ )
139+ if push.returncode != 0:
140+ print(f" Failed to push fast-forward of {branch}: {push.stderr}")
141+ failed.append(branch)
142+ continue
143+ print(f" Fast-forwarded {branch} to {default_branch} (was behind by {behind})")
144+ continue
145+
146+ # True divergence (ahead>0 and behind>0): merge the default branch in.
86147 merge = subprocess.run(
87148 ["git", "merge", f"origin/{default_branch}", "--no-edit",
88149 "-m", f"Merge {default_branch} into {branch}"],
0 commit comments