mirror of
https://github.com/github/gh-stack.git
synced 2026-09-14 20:26:28 +08:00
a5cae7bbe0
* Rebase stacks onto the latest remote trunk Fetch the configured trunk explicitly before sync or rebase and use that fetched ref whenever the local trunk cannot be safely updated, while preserving local-only and locally-ahead trunks. Fail instead of reporting success when the fetch or rebase never starts, carry the resolved trunk through conflict recovery, and verify the resulting ancestry before sync pushes or either command reports success. * Restore stacks after incomplete cascade rebases Roll back branches already rewritten when a later rebase cannot start or final ancestry verification fails, preventing retries from replaying stale history. Preserve retryable modify state without repeating completed work, and add regression coverage for remote-qualified trunk normalization.
88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
package git
|
|
|
|
import (
|
|
"errors"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func rebaseTestRepo(t *testing.T) string {
|
|
t.Helper()
|
|
_, clone := setupBareAndClone(t)
|
|
|
|
gitExec(t, clone, "checkout", "-b", "feature")
|
|
writeFile(t, clone, "feature.txt", "feature")
|
|
gitExec(t, clone, "add", ".")
|
|
gitExec(t, clone, "commit", "-m", "feature")
|
|
|
|
gitExec(t, clone, "checkout", "main")
|
|
writeFile(t, clone, "main.txt", "main update")
|
|
gitExec(t, clone, "add", ".")
|
|
gitExec(t, clone, "commit", "-m", "main update")
|
|
gitExec(t, clone, "checkout", "feature")
|
|
|
|
return clone
|
|
}
|
|
|
|
func TestIntegration_RebaseRefusedBeforeStart(t *testing.T) {
|
|
clone := rebaseTestRepo(t)
|
|
restore := withGitDir(t, clone)
|
|
defer restore()
|
|
|
|
writeFile(t, clone, "feature.txt", "uncommitted")
|
|
|
|
err := Rebase("main", RebaseOpts{})
|
|
require.Error(t, err)
|
|
assert.True(t, IsRebaseStartError(err))
|
|
assert.False(t, IsRebaseInProgress())
|
|
}
|
|
|
|
func TestIntegration_RebaseConflictIsNotStartError(t *testing.T) {
|
|
clone := rebaseTestRepo(t)
|
|
restore := withGitDir(t, clone)
|
|
defer restore()
|
|
|
|
writeFile(t, clone, "init.txt", "feature version")
|
|
gitExec(t, clone, "add", "init.txt")
|
|
gitExec(t, clone, "commit", "-m", "feature conflict")
|
|
|
|
gitExec(t, clone, "checkout", "main")
|
|
writeFile(t, clone, "init.txt", "main version")
|
|
gitExec(t, clone, "add", "init.txt")
|
|
gitExec(t, clone, "commit", "-m", "main conflict")
|
|
gitExec(t, clone, "checkout", "feature")
|
|
|
|
err := Rebase("main", RebaseOpts{})
|
|
require.Error(t, err)
|
|
assert.False(t, IsRebaseStartError(err))
|
|
assert.True(t, IsRebaseInProgress())
|
|
gitExec(t, clone, "rebase", "--abort")
|
|
}
|
|
|
|
func TestIntegration_FetchBranchRejectsDeletedRemoteBranchWithStaleTrackingRef(t *testing.T) {
|
|
bare, clone := setupBareAndClone(t)
|
|
|
|
gitExec(t, clone, "checkout", "-b", "feature-trunk")
|
|
writeFile(t, clone, "feature.txt", "feature")
|
|
gitExec(t, clone, "add", ".")
|
|
gitExec(t, clone, "commit", "-m", "feature trunk")
|
|
gitExec(t, clone, "push", "origin", "feature-trunk")
|
|
staleSHA := gitExec(t, clone, "rev-parse", "origin/feature-trunk")
|
|
|
|
other := filepath.Join(t.TempDir(), "other")
|
|
gitExec(t, ".", "clone", bare, other)
|
|
gitExec(t, other, "push", "origin", "--delete", "feature-trunk")
|
|
|
|
restore := withGitDir(t, clone)
|
|
defer restore()
|
|
|
|
err := FetchBranch("origin", "feature-trunk")
|
|
require.Error(t, err)
|
|
assert.True(t, errors.Is(err, ErrRemoteBranchNotFound))
|
|
assert.Equal(t, staleSHA, gitExec(t, clone, "rev-parse", "origin/feature-trunk"),
|
|
"the stale tracking ref still exists, so callers must trust the fetch result")
|
|
}
|