Skip pushing merges when the repo has no remote
This commit is contained in:
@@ -96,6 +96,7 @@ Coordination of merges, discards, reviews, ready-for-merge prompts, workflow sta
|
||||
|
||||
## git
|
||||
|
||||
- A project's remote is `origin`, checked each time byre would fetch or push. A repo with no `origin` still works: merges land on the local default branch and byre skips the fetch, the push and the notes refspec, so a fresh `git init` is enough to start. Adding `origin` later takes effect on the next merge; the notes refspec is added at the next server start.
|
||||
- Merge notes live under `refs/notes/byre`. byre adds the fetch refspec `refs/notes/byre*:refs/notes/byre*` to each checkout; it's a pattern because git refuses a fetch whose exact refspec names a ref the remote doesn't have yet, as it doesn't before the first note is pushed. Another clone adds it with `git config --add remote.origin.fetch 'refs/notes/byre*:refs/notes/byre*'`; `git log --notes=byre` shows the notes.
|
||||
- `git fetch` and `git push` run non-interactively (`GIT_TERMINAL_PROMPT=0`, ssh `BatchMode=yes`, overriding any `core.sshCommand`), so a remote needing a password or an unknown host key fails at once instead of waiting for input. Every git command runs in its own process group, killed whole on timeout, so an `ssh` it started can't finish a fetch or push after byre has moved on.
|
||||
- A push error other than a non-fast-forward rejection is ambiguous, since the remote may have taken the push before the connection dropped; byre then fetches and checks whether the commit landed before deciding.
|
||||
|
||||
@@ -27,8 +27,7 @@ func (m *Manager) prepareMainCommit(ctx context.Context, task api.Task, project
|
||||
return commit, notes, err
|
||||
}
|
||||
|
||||
func advanceMainCheckout(project api.Project, commit, notes string) error {
|
||||
pushed := git.Remote + "/" + project.DefaultBranch
|
||||
func advanceMainCheckout(project api.Project, commit, notes, pushed string) error {
|
||||
err := git.AdvanceNotes(context.Background(), project.RepoPath, notes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pushed %s to %s, but updating %s in the project checkout failed: %w", commit, pushed, git.NotesRef, err)
|
||||
|
||||
@@ -218,8 +218,11 @@ func (m *Manager) deliverMerge(ctx context.Context, sess api.AgentSession, task
|
||||
}
|
||||
|
||||
func pushLanded(ctx context.Context, project api.Project, commit string) (bool, error) {
|
||||
target := git.Remote + "/" + project.DefaultBranch
|
||||
err := git.Fetch(ctx, project.RepoPath)
|
||||
target, err := git.Upstream(ctx, project.RepoPath, project.DefaultBranch)
|
||||
if err != nil {
|
||||
return false, &unfinishedMergeError{fmt.Errorf("checking whether %s reached %s failed: %w", commit, project.DefaultBranch, err)}
|
||||
}
|
||||
err = git.Fetch(ctx, project.RepoPath)
|
||||
if err != nil {
|
||||
return false, &unfinishedMergeError{fmt.Errorf("checking whether %s reached %s failed: %w", commit, target, err)}
|
||||
}
|
||||
@@ -341,7 +344,11 @@ func (m *Manager) landMerge(ctx context.Context, f store.FinalizingMerge) error
|
||||
}
|
||||
return m.closeUnchanged(ctx, task, project, f.By)
|
||||
}
|
||||
err = m.store.SetFinalizingCommit(ctx, task.Id, commit, notes, git.Remote+"/"+project.DefaultBranch)
|
||||
pushed, err := git.Upstream(ctx, project.RepoPath, project.DefaultBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.store.SetFinalizingCommit(ctx, task.Id, commit, notes, pushed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -376,8 +383,11 @@ func (m *Manager) closeUnchanged(ctx context.Context, task api.Task, project api
|
||||
}
|
||||
|
||||
func (m *Manager) closePushed(ctx context.Context, task api.Task, project api.Project, commit, notes, login string) error {
|
||||
pushed := git.Remote + "/" + project.DefaultBranch
|
||||
err := advanceMainCheckout(project, commit, notes)
|
||||
pushed, err := git.Upstream(ctx, project.RepoPath, project.DefaultBranch)
|
||||
if err != nil {
|
||||
return &unfinishedMergeError{err}
|
||||
}
|
||||
err = advanceMainCheckout(project, commit, notes, pushed)
|
||||
if err != nil {
|
||||
return &unfinishedMergeError{err}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,10 @@ func (m *Manager) mergeToMain(ctx context.Context, l *live, raw json.RawMessage)
|
||||
return fmt.Sprintf("Nothing to push: %s already has all of this branch's work, so byre merged %s into this branch. Keep working; the task stays open.", project.DefaultBranch, project.DefaultBranch), nil
|
||||
}
|
||||
|
||||
pushed := git.Remote + "/" + project.DefaultBranch
|
||||
pushed, err := git.Upstream(ctx, project.RepoPath, project.DefaultBranch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = m.emit(ctx, l, agentActor, "task.merged_to_main", map[string]any{"id": task.Id, "commit": commit, "pushed": pushed, "merge_title": title})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -142,8 +145,11 @@ func (m *Manager) pushToMain(ctx context.Context, l *live, task api.Task, projec
|
||||
return "", err
|
||||
}
|
||||
|
||||
pushed := git.Remote + "/" + project.DefaultBranch
|
||||
err = advanceMainCheckout(project, commit, notes)
|
||||
pushed, err := git.Upstream(ctx, project.RepoPath, project.DefaultBranch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = advanceMainCheckout(project, commit, notes, pushed)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+39
-5
@@ -53,14 +53,30 @@ func Inspect(ctx context.Context, path string) (RepoInfo, error) {
|
||||
return RepoInfo{}, err
|
||||
}
|
||||
|
||||
remote, err := run(ctx, root, "remote", "get-url", "origin")
|
||||
remote, _, code, err := runCode(ctx, root, nil, "remote", "get-url", Remote)
|
||||
if err != nil {
|
||||
return RepoInfo{}, err
|
||||
}
|
||||
if code != 0 {
|
||||
remote = ""
|
||||
}
|
||||
|
||||
return RepoInfo{Root: root, DefaultBranch: branch, Remote: remote}, nil
|
||||
}
|
||||
|
||||
func HasRemote(ctx context.Context, repo string) (bool, error) {
|
||||
_, _, code, err := runCode(ctx, repo, nil, "remote", "get-url", Remote)
|
||||
return err == nil && code == 0, err
|
||||
}
|
||||
|
||||
func Upstream(ctx context.Context, repo, branch string) (string, error) {
|
||||
has, err := HasRemote(ctx, repo)
|
||||
if err != nil || !has {
|
||||
return branch, err
|
||||
}
|
||||
return Remote + "/" + branch, nil
|
||||
}
|
||||
|
||||
func AddWorktree(ctx context.Context, repo, path, branch, base string) (string, error) {
|
||||
sha, err := run(ctx, repo, "rev-parse", "--verify", base+"^{commit}")
|
||||
if err != nil {
|
||||
@@ -92,12 +108,18 @@ func EnsureBranchCommit(ctx context.Context, repo, branch string, env []string)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, stderr, code, err := runNetwork(ctx, repo, "push", Remote, commit+":refs/heads/"+branch)
|
||||
has, err := HasRemote(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if code != 0 {
|
||||
return fmt.Errorf("pushing %s to %s: exit status %d: %s", branch, Remote, code, stderr)
|
||||
if has {
|
||||
_, stderr, code, err := runNetwork(ctx, repo, "push", Remote, commit+":refs/heads/"+branch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if code != 0 {
|
||||
return fmt.Errorf("pushing %s to %s: exit status %d: %s", branch, Remote, code, stderr)
|
||||
}
|
||||
}
|
||||
_, err = run(ctx, repo, "update-ref", "refs/heads/"+branch, commit, "")
|
||||
return err
|
||||
@@ -258,6 +280,10 @@ var networkEnv = []string{
|
||||
var ErrRemoteMoved = errors.New("the remote's branch has moved")
|
||||
|
||||
func Push(ctx context.Context, repo, commit, branch, notes string) error {
|
||||
has, err := HasRemote(ctx, repo)
|
||||
if err != nil || !has {
|
||||
return err
|
||||
}
|
||||
out, stderr, code, err := runNetwork(ctx, repo, "push", "--atomic", "--porcelain", Remote, commit+":refs/heads/"+branch, notes+":"+NotesRef)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -279,6 +305,10 @@ func FastForward(ctx context.Context, repo, rev string) error {
|
||||
}
|
||||
|
||||
func Fetch(ctx context.Context, repo string) error {
|
||||
has, err := HasRemote(ctx, repo)
|
||||
if err != nil || !has {
|
||||
return err
|
||||
}
|
||||
_, stderr, code, err := runNetwork(ctx, repo, "fetch", Remote)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -290,7 +320,11 @@ func Fetch(ctx context.Context, repo string) error {
|
||||
}
|
||||
|
||||
func FetchFastForward(ctx context.Context, repo, branch string) error {
|
||||
err := Fetch(ctx, repo)
|
||||
has, err := HasRemote(ctx, repo)
|
||||
if err != nil || !has {
|
||||
return err
|
||||
}
|
||||
err = Fetch(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ const NotesRef = "refs/notes/byre"
|
||||
const notesFetch = NotesRef + "*:" + NotesRef + "*"
|
||||
|
||||
func ConfigureNotesFetch(ctx context.Context, repo string) error {
|
||||
has, err := HasRemote(ctx, repo)
|
||||
if err != nil || !has {
|
||||
return err
|
||||
}
|
||||
key := "remote." + Remote + ".fetch"
|
||||
out, stderr, code, err := runCode(ctx, repo, nil, "config", "--get-all", key)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user