fierj
PublicTiny personal git forge
9fc2523d4cde5e05199725b2293df13dd08f3899
diff --git a/patch.go b/patch.go
index 4d4956c..254ada4 100644
--- a/patch.go
+++ b/patch.go
@@ -186,13 +186,10 @@ func mergePatch(repoPath string, p *Patch) error {
return fmt.Errorf("patch is not open")
}
if current.Branch != "" {
- cmd := exec.Command("git", "merge", "--ff-only", current.Branch)
- cmd.Dir = repoPath
- var stderr bytes.Buffer
- cmd.Stderr = &stderr
- if err := cmd.Run(); err != nil {
- slog.Error("patch merge failed", "repo", filepath.Base(repoPath), "branch", current.Branch, "error", err, "stderr", stderr.String())
- return fmt.Errorf("cannot fast-forward merge %q: %s", current.Branch, strings.TrimSpace(stderr.String()))
+ target := defaultBranch(repoPath)
+ if err := fastForwardMerge(repoPath, target, current.Branch); err != nil {
+ slog.Error("patch merge failed", "repo", filepath.Base(repoPath), "branch", current.Branch, "error", err)
+ return err
}
} else if current.PatchFile != "" {
patchFilePath := filepath.Join(patchDataDir(repoPath), current.PatchFile)
@@ -238,6 +235,26 @@ func writePatch(repoPath string, p *Patch) error {
return os.Rename(tmp, target)
}
+// fastForwardMerge updates target to point to branch if target is an ancestor
+// of branch (pure fast-forward). Works in bare repositories.
+func fastForwardMerge(repoPath, target, branch string) error {
+ // Check that target is an ancestor of branch.
+ cmd := exec.Command("git", "merge-base", "--is-ancestor", "refs/heads/"+target, "refs/heads/"+branch)
+ cmd.Dir = repoPath
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("%q is not ahead of %q — rebase the branch first", branch, target)
+ }
+ // Fast-forward: update the target ref to point to the branch.
+ cmd = exec.Command("git", "update-ref", "refs/heads/"+target, "refs/heads/"+branch)
+ cmd.Dir = repoPath
+ var stderr bytes.Buffer
+ cmd.Stderr = &stderr
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("update-ref failed: %s", strings.TrimSpace(stderr.String()))
+ }
+ return nil
+}
+
// nonDefaultBranches returns branches that aren't the default (potential patches).
func nonDefaultBranches(repoPath string) []string {
def := defaultBranch(repoPath)