Fetch before Merge and push the squash commit to origin
This commit is contained in:
+32
-8
@@ -147,7 +147,7 @@ func UnmergedChanges(worktree, target string) (Unmerged, error) {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func SquashMerge(repo, worktree, target, message string, env []string) (string, error) {
|
||||
func SquashCommit(repo, worktree, target, message string, env []string) (string, error) {
|
||||
checkedOut, err := run(repo, "symbolic-ref", "--short", "HEAD")
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -196,16 +196,40 @@ func SquashMerge(repo, worktree, target, message string, env []string) (string,
|
||||
return "", nil
|
||||
}
|
||||
|
||||
commit, err := runEnv(repo, env, "commit-tree", lines[0], "-p", head, "-m", message)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return runEnv(repo, env, "commit-tree", lines[0], "-p", head, "-m", message)
|
||||
}
|
||||
|
||||
_, err = run(repo, "merge", "--ff-only", commit)
|
||||
const Remote = "origin"
|
||||
|
||||
var ErrRemoteMoved = errors.New("the remote's branch has moved")
|
||||
|
||||
func Push(repo, commit, branch string) error {
|
||||
out, stderr, code, err := runCode(repo, nil, "push", "--porcelain", Remote, commit+":refs/heads/"+branch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
return commit, nil
|
||||
if code == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
if strings.HasPrefix(line, "!") && strings.Contains(line, "[rejected]") && (strings.Contains(line, "(fetch first)") || strings.Contains(line, "(non-fast-forward)")) {
|
||||
return fmt.Errorf("pushing %s to %s: %w", branch, Remote, ErrRemoteMoved)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("pushing %s to %s: exit status %d: %s", branch, Remote, code, strings.TrimSpace(out+"\n"+stderr))
|
||||
}
|
||||
|
||||
func FastForward(repo, rev string) error {
|
||||
_, err := run(repo, "merge", "--ff-only", rev)
|
||||
return err
|
||||
}
|
||||
|
||||
func FetchFastForward(repo, branch string) error {
|
||||
_, err := run(repo, "fetch", Remote, branch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return FastForward(repo, Remote+"/"+branch)
|
||||
}
|
||||
|
||||
func Head(dir string) (string, error) {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testEnv = []string{
|
||||
"GIT_AUTHOR_NAME=Test",
|
||||
"GIT_AUTHOR_EMAIL=test@byre.invalid",
|
||||
"GIT_COMMITTER_NAME=Test",
|
||||
"GIT_COMMITTER_EMAIL=test@byre.invalid",
|
||||
}
|
||||
|
||||
func mustGit(t *testing.T, dir string, args ...string) string {
|
||||
t.Helper()
|
||||
out, err := runEnv(dir, testEnv, args...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func pushFixture(t *testing.T) (string, string) {
|
||||
t.Helper()
|
||||
bare := filepath.Join(t.TempDir(), "remote.git")
|
||||
mustGit(t, t.TempDir(), "init", "--quiet", "--bare", "--initial-branch=main", bare)
|
||||
|
||||
repo := t.TempDir()
|
||||
mustGit(t, repo, "init", "--quiet", "--initial-branch=main")
|
||||
mustGit(t, repo, "commit", "--quiet", "--allow-empty", "-m", "root")
|
||||
mustGit(t, repo, "remote", "add", Remote, bare)
|
||||
mustGit(t, repo, "push", "--quiet", Remote, "main")
|
||||
return repo, bare
|
||||
}
|
||||
|
||||
func moveRemote(t *testing.T, bare string) string {
|
||||
t.Helper()
|
||||
other := t.TempDir()
|
||||
mustGit(t, other, "clone", "--quiet", bare, ".")
|
||||
mustGit(t, other, "commit", "--quiet", "--allow-empty", "-m", "elsewhere")
|
||||
mustGit(t, other, "push", "--quiet", Remote, "main")
|
||||
return mustGit(t, other, "rev-parse", "HEAD")
|
||||
}
|
||||
|
||||
func squash(t *testing.T, repo, message string) string {
|
||||
t.Helper()
|
||||
tree := mustGit(t, repo, "rev-parse", "main^{tree}")
|
||||
return mustGit(t, repo, "commit-tree", tree, "-p", "main", "-m", message)
|
||||
}
|
||||
|
||||
func TestPushClean(t *testing.T) {
|
||||
repo, bare := pushFixture(t)
|
||||
commit := squash(t, repo, "task")
|
||||
|
||||
err := Push(repo, commit, "main")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := mustGit(t, bare, "rev-parse", "main"); got != commit {
|
||||
t.Fatalf("remote main at %s, want %s", got, commit)
|
||||
}
|
||||
err = FastForward(repo, commit)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := mustGit(t, repo, "rev-parse", "main"); got != commit {
|
||||
t.Fatalf("local main at %s, want %s", got, commit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushRemoteMoved(t *testing.T) {
|
||||
repo, bare := pushFixture(t)
|
||||
moved := moveRemote(t, bare)
|
||||
commit := squash(t, repo, "task")
|
||||
|
||||
err := Push(repo, commit, "main")
|
||||
if !errors.Is(err, ErrRemoteMoved) {
|
||||
t.Fatalf("push onto a moved remote: %v, want ErrRemoteMoved", err)
|
||||
}
|
||||
if got := mustGit(t, bare, "rev-parse", "main"); got != moved {
|
||||
t.Fatalf("remote main at %s after a refused push, want %s", got, moved)
|
||||
}
|
||||
|
||||
err = FetchFastForward(repo, "main")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := mustGit(t, repo, "rev-parse", "main"); got != moved {
|
||||
t.Fatalf("local main at %s after fetch, want %s", got, moved)
|
||||
}
|
||||
|
||||
commit = squash(t, repo, "task again")
|
||||
err = Push(repo, commit, "main")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := mustGit(t, bare, "rev-parse", "main"); got != commit {
|
||||
t.Fatalf("remote main at %s, want %s", got, commit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushOtherFailure(t *testing.T) {
|
||||
repo, _ := pushFixture(t)
|
||||
mustGit(t, repo, "remote", "set-url", Remote, filepath.Join(t.TempDir(), "missing.git"))
|
||||
|
||||
err := Push(repo, squash(t, repo, "task"), "main")
|
||||
if err == nil || errors.Is(err, ErrRemoteMoved) {
|
||||
t.Fatalf("push to a missing remote: %v, want an error other than ErrRemoteMoved", err)
|
||||
}
|
||||
t.Logf("push error: %v", err)
|
||||
}
|
||||
Reference in New Issue
Block a user