mirror of
https://github.com/aviator-co/av.git
synced 2026-01-06 12:20:26 -06:00
Support specifying a branching point even if the parent is trunk (#623)(#622)(#621)(#620)(#619)(#618)
This allows us to handle a case where a newly adopted remote branch has an already merged branch as a parent. In the next PR, we will specify the branching point for those branches. Check if the default branch is set when opening a repository We can check if the default branch at remote tracking branch (refs/remotes/origin/HEAD) exists when opening a repository. By doing so, we don't have to take context and do not have to do an error handling in many situations. Closes #622 Allow specifying the branching point even for trunk parents This relax the semantics of the branch metadata. In the branch metadata, we track `trunk: bool` and `head: string`, but only one of them could be specified in the current semantics. This update allows them to specify both. That is, even if the parent branch is a trunk branch, a branch metadata can specify the branching point. Accordingly, we update the internal name of that branching point as BranchingPointCommitHash. Newly added av branch --remote motivates this change. In that command, we might see a partially merged stack. In that case, we will have a case where a topic branch would have a trunk branch as a parent (because its original parent has been merged), but at the same time, such topic branches would need to have an explicit branching point in order to avoid already merged commits to be considered as a part of that topic branch. Behavior updates would be necessary in the sequencer, but we will do that in the following PRs. Closes #621 Deprecate reference based metadata store It's been a while since we migrated to a file-base metadata storage. We can drop this refmeta code. Closes #620 Extract MergeCommit for remote branches Closes #619 Handle a case where no branch was chosen Closes #618
This commit is contained in:
@@ -105,16 +105,11 @@ func adoptForceAdoption(
|
||||
return errors.New("cannot adopt the current branch as its parent")
|
||||
}
|
||||
|
||||
if isCurrentBranchTrunk, err := repo.IsTrunkBranch(ctx, currentBranch); err != nil {
|
||||
return errors.Wrap(err, "failed to check if the current branch is trunk")
|
||||
} else if isCurrentBranchTrunk {
|
||||
if repo.IsTrunkBranch(currentBranch) {
|
||||
return errors.New("cannot adopt the default branch")
|
||||
}
|
||||
|
||||
isParentBranchTrunk, err := repo.IsTrunkBranch(ctx, parent)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to check if the parent branch is trunk")
|
||||
}
|
||||
isParentBranchTrunk := repo.IsTrunkBranch(parent)
|
||||
if isParentBranchTrunk {
|
||||
branch.Parent = meta.BranchState{
|
||||
Name: parent,
|
||||
@@ -131,9 +126,9 @@ func adoptForceAdoption(
|
||||
return err
|
||||
}
|
||||
branch.Parent = meta.BranchState{
|
||||
Name: parent,
|
||||
Trunk: false,
|
||||
Head: mergeBase,
|
||||
Name: parent,
|
||||
Trunk: false,
|
||||
BranchingPointCommitHash: mergeBase,
|
||||
}
|
||||
tx.SetBranch(branch)
|
||||
}
|
||||
@@ -242,7 +237,7 @@ func (vm *adoptViewModel) initAdoption(chosenTargets []plumbing.ReferenceName) t
|
||||
},
|
||||
}
|
||||
if !piece.ParentIsTrunk {
|
||||
ab.Parent.Head = piece.ParentMergeBase.String()
|
||||
ab.Parent.BranchingPointCommitHash = piece.ParentMergeBase.String()
|
||||
}
|
||||
branches = append(branches, ab)
|
||||
}
|
||||
@@ -350,6 +345,12 @@ func (vm *remoteAdoptViewModel) initTreeSelector(prs []actions.RemotePRInfo) tea
|
||||
}
|
||||
|
||||
func (vm *remoteAdoptViewModel) initGitFetch(prs []actions.RemotePRInfo, chosenTargets []plumbing.ReferenceName) tea.Cmd {
|
||||
if len(chosenTargets) == 0 {
|
||||
return tea.Batch(
|
||||
vm.AddView(uiutils.SimpleMessageView{Message: colors.SuccessStyle.Render("✓ No branch adopted")}),
|
||||
tea.Quit,
|
||||
)
|
||||
}
|
||||
refspecs := []string{}
|
||||
for _, target := range chosenTargets {
|
||||
// Directly clone as a local branch.
|
||||
|
||||
@@ -268,10 +268,7 @@ func createBranch(
|
||||
) (reterr error) {
|
||||
// Determine important contextual information from Git
|
||||
// or if a parent branch is provided, check it allows as a default branch
|
||||
defaultBranch, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return errors.WrapIf(err, "failed to determine repository default branch")
|
||||
}
|
||||
defaultBranch := repo.DefaultBranch()
|
||||
|
||||
tx := db.WriteTx()
|
||||
cu := cleanup.New(func() {
|
||||
@@ -283,7 +280,7 @@ func createBranch(
|
||||
// Determine the parent branch and make sure it's checked out
|
||||
if parentBranchName == "" {
|
||||
var err error
|
||||
parentBranchName, err = repo.CurrentBranchName(ctx)
|
||||
parentBranchName, err = repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return errors.WrapIff(err, "failed to get current branch name")
|
||||
}
|
||||
@@ -295,10 +292,7 @@ func createBranch(
|
||||
}
|
||||
parentBranchName = strings.TrimPrefix(parentBranchName, remoteName+"/")
|
||||
|
||||
isBranchFromTrunk, err := repo.IsTrunkBranch(ctx, parentBranchName)
|
||||
if err != nil {
|
||||
return errors.WrapIf(err, "failed to determine if branch is a trunk")
|
||||
}
|
||||
isBranchFromTrunk := repo.IsTrunkBranch(parentBranchName)
|
||||
checkoutStartingPoint := parentBranchName
|
||||
var parentHead string
|
||||
if isBranchFromTrunk {
|
||||
@@ -373,9 +367,9 @@ func createBranch(
|
||||
tx.SetBranch(meta.Branch{
|
||||
Name: branchName,
|
||||
Parent: meta.BranchState{
|
||||
Name: parentBranchName,
|
||||
Trunk: isBranchFromTrunk,
|
||||
Head: parentHead,
|
||||
Name: parentBranchName,
|
||||
Trunk: isBranchFromTrunk,
|
||||
BranchingPointCommitHash: parentHead,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -403,7 +397,7 @@ func branchMove(
|
||||
oldBranch, newBranch, _ = strings.Cut(newBranch, ":")
|
||||
} else {
|
||||
var err error
|
||||
oldBranch, err = repo.CurrentBranchName(ctx)
|
||||
oldBranch, err = repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -422,10 +416,7 @@ func branchMove(
|
||||
|
||||
currentMeta, ok := tx.Branch(oldBranch)
|
||||
if !ok {
|
||||
defaultBranch, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return errors.WrapIf(err, "failed to determine repository default branch")
|
||||
}
|
||||
defaultBranch := repo.DefaultBranch()
|
||||
currentMeta.Parent = meta.BranchState{
|
||||
Name: defaultBranch,
|
||||
Trunk: true,
|
||||
|
||||
@@ -108,9 +108,9 @@ var branchMetaSetCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
br.Parent = meta.BranchState{
|
||||
Name: branchMetaFlags.parent,
|
||||
Trunk: branchMetaFlags.trunk,
|
||||
Head: parentHead,
|
||||
Name: branchMetaFlags.parent,
|
||||
Trunk: branchMetaFlags.trunk,
|
||||
BranchingPointCommitHash: parentHead,
|
||||
}
|
||||
}
|
||||
tx.SetBranch(br)
|
||||
|
||||
@@ -103,7 +103,7 @@ var commitCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func runCreate(ctx context.Context, repo *git.Repo, db meta.DB) error {
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return errors.WrapIf(err, "failed to determine current branch")
|
||||
}
|
||||
@@ -182,7 +182,7 @@ func runAmend(
|
||||
edit bool,
|
||||
all bool,
|
||||
) error {
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return errors.WrapIf(err, "failed to determine current branch")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"emperror.dev/errors"
|
||||
@@ -114,7 +113,7 @@ func (vm *postCommitRestackViewModel) writeState(state *sequencerui.RestackState
|
||||
}
|
||||
|
||||
func (vm *postCommitRestackViewModel) createState() (*sequencerui.RestackState, error) {
|
||||
currentBranch, err := vm.repo.CurrentBranchName(context.Background())
|
||||
currentBranch, err := vm.repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ Generates the diff between the working tree and the parent branch
|
||||
return err
|
||||
}
|
||||
|
||||
currentBranchName, err := repo.CurrentBranchName(ctx)
|
||||
currentBranchName, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -41,10 +41,7 @@ Generates the diff between the working tree and the parent branch
|
||||
tx := db.ReadTx()
|
||||
branch, exists := tx.Branch(currentBranchName)
|
||||
if !exists {
|
||||
defaultBranch, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defaultBranch := repo.DefaultBranch()
|
||||
branch.Parent = meta.BranchState{
|
||||
Name: defaultBranch,
|
||||
Trunk: true,
|
||||
@@ -90,7 +87,7 @@ Generates the diff between the working tree and the parent branch
|
||||
// have those changes). Instead, we want to compute the diff between
|
||||
// 1a and 2a. We can't just use merge-base here to account for the
|
||||
// fact that one might be amended (not just advanced).
|
||||
diffArgs = append(diffArgs, branch.Parent.Head)
|
||||
diffArgs = append(diffArgs, branch.Parent.BranchingPointCommitHash)
|
||||
|
||||
// Determine if the branch is up-to-date with the parent and warn if
|
||||
// not.
|
||||
@@ -98,7 +95,7 @@ Generates the diff between the working tree and the parent branch
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
notUpToDate = currentParentHead != branch.Parent.Head
|
||||
notUpToDate = currentParentHead != branch.Parent.BranchingPointCommitHash
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"github.com/aviator-co/av/internal/git"
|
||||
"github.com/aviator-co/av/internal/meta"
|
||||
"github.com/aviator-co/av/internal/meta/jsonfiledb"
|
||||
"github.com/aviator-co/av/internal/meta/refmeta"
|
||||
"github.com/aviator-co/av/internal/utils/colors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -59,7 +57,7 @@ var ErrRepoNotInitialized = errors.Sentinel(
|
||||
)
|
||||
|
||||
func getDB(ctx context.Context, repo *git.Repo) (meta.DB, error) {
|
||||
db, exists, err := getOrCreateDB(ctx, repo)
|
||||
db, exists, err := getOrCreateDB(repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -69,23 +67,8 @@ func getDB(ctx context.Context, repo *git.Repo) (meta.DB, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func getOrCreateDB(ctx context.Context, repo *git.Repo) (meta.DB, bool, error) {
|
||||
func getOrCreateDB(repo *git.Repo) (meta.DB, bool, error) {
|
||||
dbPath := filepath.Join(repo.AvDir(), "av.db")
|
||||
oldDBPathPath := filepath.Join(repo.AvDir(), "repo-metadata.json")
|
||||
dbPathStat, _ := os.Stat(dbPath)
|
||||
oldDBPathStat, _ := os.Stat(oldDBPathPath)
|
||||
|
||||
if dbPathStat == nil && oldDBPathStat != nil {
|
||||
// Migrate old db to new db
|
||||
db, exists, err := jsonfiledb.OpenPath(dbPath)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if err := refmeta.Import(ctx, repo, db); err != nil {
|
||||
return nil, false, errors.WrapIff(err, "failed to import ref metadata into av database")
|
||||
}
|
||||
return db, exists, nil
|
||||
}
|
||||
return jsonfiledb.OpenPath(dbPath)
|
||||
}
|
||||
|
||||
@@ -99,11 +82,7 @@ func allBranches(ctx context.Context) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defaultBranch, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defaultBranch := repo.DefaultBranch()
|
||||
tx := db.ReadTx()
|
||||
|
||||
branches := []string{defaultBranch}
|
||||
|
||||
@@ -21,7 +21,7 @@ var initCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
db, _, err := getOrCreateDB(ctx, repo)
|
||||
db, _, err := getOrCreateDB(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func newNextModel(ctx context.Context, lastInStack bool, nInStack int) (stackNex
|
||||
return stackNextModel{}, err
|
||||
}
|
||||
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return stackNextModel{}, err
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ var orphanCmd = &cobra.Command{
|
||||
tx := db.WriteTx()
|
||||
defer tx.Abort()
|
||||
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return errors.WrapIf(err, "failed to determine current branch")
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ Examples:
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
branchName, err := repo.CurrentBranchName(ctx)
|
||||
branchName, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return errors.WrapIf(err, "failed to determine current branch")
|
||||
}
|
||||
@@ -183,7 +183,7 @@ func submitAll(ctx context.Context, current bool, draft bool) error {
|
||||
cu := cleanup.New(func() { tx.Abort() })
|
||||
defer cu.Cleanup()
|
||||
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,7 +288,7 @@ func queue(ctx context.Context) error {
|
||||
}
|
||||
|
||||
tx := db.ReadTx()
|
||||
currentBranchName, err := repo.CurrentBranchName(ctx)
|
||||
currentBranchName, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ func getQueryVariables(ctx context.Context) (map[string]any, error) {
|
||||
|
||||
tx := db.ReadTx()
|
||||
|
||||
currentBranchName, err := repo.CurrentBranchName(ctx)
|
||||
currentBranchName, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -33,14 +33,11 @@ var prevCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
tx := db.ReadTx()
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isCurrentBranchTrunk, err := repo.IsTrunkBranch(ctx, currentBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if isCurrentBranchTrunk {
|
||||
if repo.IsTrunkBranch(currentBranch) {
|
||||
fmt.Fprint(os.Stderr, "already on trunk branch (", colors.UserInput(currentBranch), ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ squashed, dropped, or moved within the stack.
|
||||
return actions.ErrExitSilently{ExitCode: 127}
|
||||
}
|
||||
tx := db.ReadTx()
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -145,22 +145,18 @@ func (vm *reparentViewModel) writeState(state *sequencerui.RestackState) error {
|
||||
|
||||
func (vm *reparentViewModel) createState() (*sequencerui.RestackState, error) {
|
||||
ctx := context.Background()
|
||||
currentBranch, err := vm.repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := vm.repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isCurrentBranchTrunk, err := vm.repo.IsTrunkBranch(ctx, currentBranch); err != nil {
|
||||
return nil, err
|
||||
} else if isCurrentBranchTrunk {
|
||||
if vm.repo.IsTrunkBranch(currentBranch) {
|
||||
return nil, errors.New("current branch is a trunk branch")
|
||||
}
|
||||
if _, exist := vm.db.ReadTx().Branch(currentBranch); !exist {
|
||||
return nil, errors.New("current branch is not adopted to av")
|
||||
}
|
||||
|
||||
if isParentBranchTrunk, err := vm.repo.IsTrunkBranch(ctx, reparentFlags.Parent); err != nil {
|
||||
return nil, err
|
||||
} else if !isParentBranchTrunk {
|
||||
if !vm.repo.IsTrunkBranch(reparentFlags.Parent) {
|
||||
if _, exist := vm.db.ReadTx().Branch(reparentFlags.Parent); !exist {
|
||||
return nil, errors.New("parent branch is not adopted to av")
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func runSquash(ctx context.Context, repo *git.Repo, db meta.DB) error {
|
||||
)
|
||||
}
|
||||
|
||||
currentBranchName, err := repo.CurrentBranchName(ctx)
|
||||
currentBranchName, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ Examples:
|
||||
return err
|
||||
}
|
||||
tx := db.ReadTx()
|
||||
currentBranch, err := repo.CurrentBranchName(ctx)
|
||||
currentBranch, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func (vm *syncViewModel) initSync() tea.Cmd {
|
||||
return uiutils.ErrCmd(errors.New("no restack in progress"))
|
||||
}
|
||||
|
||||
isTrunkBranch, err := vm.repo.IsCurrentBranchTrunk(context.Background())
|
||||
isTrunkBranch, err := vm.repo.IsCurrentBranchTrunk()
|
||||
if err != nil {
|
||||
return uiutils.ErrCmd(err)
|
||||
}
|
||||
|
||||
@@ -149,16 +149,16 @@ func TestRestack(t *testing.T) {
|
||||
Trunk: true,
|
||||
}, GetStoredParentBranchState(t, repo, "stack-1"))
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-1",
|
||||
Head: stack1Commit.String(),
|
||||
Name: "stack-1",
|
||||
BranchingPointCommitHash: stack1Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-2"))
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-2",
|
||||
Head: stack2Commit.String(),
|
||||
Name: "stack-2",
|
||||
BranchingPointCommitHash: stack2Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-3"))
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-1",
|
||||
Head: stack1Commit.String(),
|
||||
Name: "stack-1",
|
||||
BranchingPointCommitHash: stack1Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-4"))
|
||||
}
|
||||
|
||||
@@ -214,8 +214,8 @@ func TestStackRestackAbort(t *testing.T) {
|
||||
// Because we aborted the sync, the stack-2 parent HEAD must stay at the original stack-1
|
||||
// HEAD.
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-1",
|
||||
Head: origStack1Commit.String(),
|
||||
Name: "stack-1",
|
||||
BranchingPointCommitHash: origStack1Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-2"))
|
||||
}
|
||||
|
||||
|
||||
@@ -155,16 +155,16 @@ func TestSync(t *testing.T) {
|
||||
Trunk: true,
|
||||
}, GetStoredParentBranchState(t, repo, "stack-1"))
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-1",
|
||||
Head: stack1Commit.String(),
|
||||
Name: "stack-1",
|
||||
BranchingPointCommitHash: stack1Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-2"))
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-2",
|
||||
Head: stack2Commit.String(),
|
||||
Name: "stack-2",
|
||||
BranchingPointCommitHash: stack2Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-3"))
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-1",
|
||||
Head: stack1Commit.String(),
|
||||
Name: "stack-1",
|
||||
BranchingPointCommitHash: stack1Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-4"))
|
||||
}
|
||||
|
||||
@@ -222,8 +222,8 @@ func TestStackSyncAbort(t *testing.T) {
|
||||
// Because we aborted the sync, the stack-2 parent HEAD must stay at the original stack-1
|
||||
// HEAD.
|
||||
require.Equal(t, meta.BranchState{
|
||||
Name: "stack-1",
|
||||
Head: origStack1Commit.String(),
|
||||
Name: "stack-1",
|
||||
BranchingPointCommitHash: origStack1Commit.String(),
|
||||
}, GetStoredParentBranchState(t, repo, "stack-2"))
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ type RemotePRInfo struct {
|
||||
Name string
|
||||
Parent meta.BranchState
|
||||
PullRequest meta.PullRequest
|
||||
MergeCommit string
|
||||
Title string
|
||||
}
|
||||
|
||||
@@ -94,9 +95,9 @@ func (m *GetRemoteStackedPRModel) Init() tea.Cmd {
|
||||
remotePRInfo := RemotePRInfo{
|
||||
Name: strings.TrimPrefix(pr.HeadRefName, "refs/heads/"),
|
||||
Parent: meta.BranchState{
|
||||
Name: prMeta.Parent,
|
||||
Head: prMeta.ParentHead,
|
||||
Trunk: prMeta.ParentHead == "",
|
||||
Name: prMeta.Parent,
|
||||
Trunk: prMeta.Trunk == prMeta.Parent,
|
||||
BranchingPointCommitHash: prMeta.ParentHead,
|
||||
},
|
||||
PullRequest: meta.PullRequest{
|
||||
ID: pr.ID,
|
||||
@@ -104,7 +105,8 @@ func (m *GetRemoteStackedPRModel) Init() tea.Cmd {
|
||||
Permalink: pr.Permalink,
|
||||
State: pr.State,
|
||||
},
|
||||
Title: pr.Title,
|
||||
MergeCommit: pr.GetMergeCommit(),
|
||||
Title: pr.Title,
|
||||
}
|
||||
m.prs = append(m.prs, remotePRInfo)
|
||||
if remotePRInfo.Parent.Trunk {
|
||||
|
||||
@@ -73,7 +73,7 @@ func getPRMetadata(
|
||||
trunk, _ := meta.Trunk(tx, branch.Name)
|
||||
prMeta := PRMetadata{
|
||||
Parent: branch.Parent.Name,
|
||||
ParentHead: branch.Parent.Head,
|
||||
ParentHead: branch.Parent.BranchingPointCommitHash,
|
||||
Trunk: trunk,
|
||||
}
|
||||
if parent == nil && branch.Parent.Name != "" {
|
||||
@@ -224,10 +224,7 @@ func CreatePullRequest(
|
||||
// figure this out based on whether or not we're on a stacked branch
|
||||
parentState := branchMeta.Parent
|
||||
if parentState.Name == "" {
|
||||
defaultBranch, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.WrapIf(err, "failed to determine default branch")
|
||||
}
|
||||
defaultBranch := repo.DefaultBranch()
|
||||
parentState = meta.BranchState{
|
||||
Name: defaultBranch,
|
||||
Trunk: true,
|
||||
|
||||
@@ -566,7 +566,7 @@ func (vm *GitHubPushModel) createPRMetadata(branch meta.Branch) actions.PRMetada
|
||||
|
||||
metadata := actions.PRMetadata{
|
||||
Parent: branch.Parent.Name,
|
||||
ParentHead: branch.Parent.Head,
|
||||
ParentHead: branch.Parent.BranchingPointCommitHash,
|
||||
Trunk: trunk,
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/aviator-co/av/internal/config"
|
||||
giturls "github.com/chainguard-dev/git-urls"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -25,10 +26,11 @@ var ErrRemoteNotFound = errors.Sentinel("this repository doesn't have a remote o
|
||||
const DEFAULT_REMOTE_NAME = "origin"
|
||||
|
||||
type Repo struct {
|
||||
repoDir string
|
||||
gitDir string
|
||||
gitRepo *git.Repository
|
||||
log logrus.FieldLogger
|
||||
repoDir string
|
||||
gitDir string
|
||||
gitRepo *git.Repository
|
||||
log logrus.FieldLogger
|
||||
defaultBranch plumbing.ReferenceName
|
||||
}
|
||||
|
||||
func OpenRepo(repoDir string, gitDir string) (*Repo, error) {
|
||||
@@ -40,11 +42,29 @@ func OpenRepo(repoDir string, gitDir string) (*Repo, error) {
|
||||
return nil, errors.Errorf("failed to open git repo: %v", err)
|
||||
}
|
||||
r := &Repo{
|
||||
repoDir,
|
||||
gitDir,
|
||||
repo,
|
||||
logrus.WithFields(logrus.Fields{"repo": filepath.Base(repoDir)}),
|
||||
repoDir: repoDir,
|
||||
gitDir: gitDir,
|
||||
gitRepo: repo,
|
||||
log: logrus.WithFields(logrus.Fields{"repo": filepath.Base(repoDir)}),
|
||||
defaultBranch: "",
|
||||
}
|
||||
|
||||
// Fill the default branch now so that we can error early if it can't be
|
||||
// determined.
|
||||
remoteName := r.GetRemoteName()
|
||||
ref, err := r.GoGitRepo().Reference(plumbing.NewRemoteHEADReferenceName(remoteName), false)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Debug("failed to determine remote HEAD")
|
||||
// This `git remote set-head --auto origin` communicates with
|
||||
// the remote, so we probably don't want to run it here inline,
|
||||
// but we suggest it to the user in order to fix this situation.
|
||||
logrus.Warn(
|
||||
"Failed to determine repository default branch. " +
|
||||
"Ensure you have a remote named origin and try running `git remote set-head --auto origin` to fix this.",
|
||||
)
|
||||
return nil, fmt.Errorf("failed to determine remote HEAD: %v", err)
|
||||
}
|
||||
r.defaultBranch = plumbing.NewBranchReferenceName(strings.TrimPrefix(ref.Target().String(), fmt.Sprintf("refs/remotes/%s/", remoteName)))
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@@ -72,51 +92,24 @@ func (r *Repo) AvTmpDir() string {
|
||||
return dir
|
||||
}
|
||||
|
||||
func (r *Repo) DefaultBranch(ctx context.Context) (string, error) {
|
||||
remoteName := r.GetRemoteName()
|
||||
ref, err := r.Git(ctx, "symbolic-ref", fmt.Sprintf("refs/remotes/%s/HEAD", remoteName))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Debug("failed to determine remote HEAD")
|
||||
// this communicates with the remote, so we probably don't want to run
|
||||
// it by default, but we helpfully suggest it to the user. :shrug:
|
||||
logrus.Warn(
|
||||
"Failed to determine repository default branch. " +
|
||||
"Ensure you have a remote named origin and try running `git remote set-head --auto origin` to fix this.",
|
||||
)
|
||||
return "", errors.New("failed to determine remote HEAD")
|
||||
}
|
||||
return strings.TrimPrefix(ref, fmt.Sprintf("refs/remotes/%s/", remoteName)), nil
|
||||
func (r *Repo) DefaultBranch() string {
|
||||
return r.defaultBranch.Short()
|
||||
}
|
||||
|
||||
func (r *Repo) IsTrunkBranch(ctx context.Context, name string) (bool, error) {
|
||||
branches, err := r.TrunkBranches(ctx)
|
||||
func (r *Repo) IsTrunkBranch(name string) bool {
|
||||
return slices.Contains(r.TrunkBranches(), name)
|
||||
}
|
||||
|
||||
func (r *Repo) IsCurrentBranchTrunk() (bool, error) {
|
||||
currentBranch, err := r.CurrentBranchName()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if slices.Contains(branches, name) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
return r.IsTrunkBranch(currentBranch), nil
|
||||
}
|
||||
|
||||
func (r *Repo) IsCurrentBranchTrunk(ctx context.Context) (bool, error) {
|
||||
currentBranch, err := r.CurrentBranchName(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return r.IsTrunkBranch(ctx, currentBranch)
|
||||
}
|
||||
|
||||
func (r *Repo) TrunkBranches(ctx context.Context) ([]string, error) {
|
||||
defaultBranch, err := r.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
branches := []string{defaultBranch}
|
||||
branches = append(branches, config.Av.AdditionalTrunkBranches...)
|
||||
|
||||
return branches, nil
|
||||
func (r *Repo) TrunkBranches() []string {
|
||||
return append([]string{r.DefaultBranch()}, config.Av.AdditionalTrunkBranches...)
|
||||
}
|
||||
|
||||
func (r *Repo) GetRemoteName() string {
|
||||
@@ -228,15 +221,19 @@ func (r *Repo) Run(ctx context.Context, opts *RunOpts) (*Output, error) {
|
||||
// The name is return in "short" format -- i.e., without the "refs/heads/" prefix.
|
||||
// IMPORTANT: This function will return an error if the repository is currently
|
||||
// in a detached-head state (e.g., during a rebase conflict).
|
||||
func (r *Repo) CurrentBranchName(ctx context.Context) (string, error) {
|
||||
branch, err := r.Git(ctx, "symbolic-ref", "--short", "HEAD")
|
||||
func (r *Repo) CurrentBranchName() (string, error) {
|
||||
ref, err := r.GoGitRepo().Reference(plumbing.HEAD, false)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(
|
||||
err,
|
||||
"failed to determine current branch (are you in detached HEAD or is a rebase in progress?)",
|
||||
)
|
||||
}
|
||||
return branch, nil
|
||||
if ref.Type() == plumbing.SymbolicReference {
|
||||
return ref.Target().Short(), nil
|
||||
}
|
||||
// Detached HEAD
|
||||
return "", errors.New("repository is in detached HEAD state")
|
||||
}
|
||||
|
||||
func (r *Repo) DoesBranchExist(ctx context.Context, branch string) (bool, error) {
|
||||
@@ -295,7 +292,7 @@ type CheckoutBranch struct {
|
||||
// branch if necessary). The returned previous branch name may be empty if the
|
||||
// repo is currently not checked out to a branch (i.e., in detached HEAD state).
|
||||
func (r *Repo) CheckoutBranch(ctx context.Context, opts *CheckoutBranch) (string, error) {
|
||||
previousBranchName, err := r.CurrentBranchName(ctx)
|
||||
previousBranchName, err := r.CurrentBranchName()
|
||||
if err != nil {
|
||||
r.log.WithError(err).
|
||||
Debug("failed to get current branch name, repo is probably in detached HEAD")
|
||||
|
||||
@@ -26,21 +26,23 @@ func TestOrigin(t *testing.T) {
|
||||
func TestTrunkBranches(t *testing.T) {
|
||||
repo := gittest.NewTempRepo(t)
|
||||
|
||||
branches, err := repo.AsAvGitRepo().TrunkBranches(t.Context())
|
||||
require.NoError(t, err)
|
||||
branches := repo.AsAvGitRepo().TrunkBranches()
|
||||
require.Equal(t, branches, []string{"main"})
|
||||
|
||||
// add some branches to AdditionalTrunkBranches
|
||||
config.Av.AdditionalTrunkBranches = []string{"develop", "staging"}
|
||||
branches, err = repo.AsAvGitRepo().TrunkBranches(t.Context())
|
||||
require.NoError(t, err)
|
||||
branches = repo.AsAvGitRepo().TrunkBranches()
|
||||
require.Equal(t, branches, []string{"main", "develop", "staging"})
|
||||
}
|
||||
|
||||
func TestGetRemoteName(t *testing.T) {
|
||||
repo := gittest.NewTempRepo(t)
|
||||
require.Equal(t, repo.AsAvGitRepo().GetRemoteName(), git.DEFAULT_REMOTE_NAME)
|
||||
avGitRepo := repo.AsAvGitRepo()
|
||||
require.Equal(t, avGitRepo.GetRemoteName(), git.DEFAULT_REMOTE_NAME)
|
||||
|
||||
// This is a global config, so changing it here affects other tests. Be
|
||||
// sure to reset it.
|
||||
config.Av.Remote = "new-remote"
|
||||
require.Equal(t, repo.AsAvGitRepo().GetRemoteName(), "new-remote")
|
||||
require.Equal(t, avGitRepo.GetRemoteName(), "new-remote")
|
||||
config.Av.Remote = ""
|
||||
}
|
||||
|
||||
@@ -80,6 +80,9 @@ func NewTempRepoWithGitHubServer(t *testing.T, serverURL string) *GitTestRepo {
|
||||
repo.Git(t, "commit", "-m", "Initial commit")
|
||||
repo.Git(t, "push", "origin", "main")
|
||||
|
||||
repo.Git(t, "remote", "set-head", "--auto", "origin")
|
||||
repo.Git(t, "branch", "--all")
|
||||
|
||||
// Write metadata because some commands expect it to be there.
|
||||
// This repository obviously doesn't exist so tests still need to be careful
|
||||
// not to invoke operations that would communicate with GitHub (e.g.,
|
||||
@@ -117,7 +120,10 @@ type GitTestRepo struct {
|
||||
}
|
||||
|
||||
func (r *GitTestRepo) AsAvGitRepo() *avgit.Repo {
|
||||
repo, _ := avgit.OpenRepo(r.RepoDir, r.GitDir)
|
||||
repo, err := avgit.OpenRepo(r.RepoDir, r.GitDir)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to open av git repo: %v", err))
|
||||
}
|
||||
return repo
|
||||
}
|
||||
|
||||
|
||||
@@ -254,10 +254,7 @@ func (vm *PruneBranchModel) CheckoutInitialState() error {
|
||||
}
|
||||
|
||||
// The branch is deleted. Let's checkout the default branch.
|
||||
defaultBranch, err := vm.repo.DefaultBranch(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defaultBranch := vm.repo.DefaultBranch()
|
||||
defaultBranchRef := plumbing.NewBranchReferenceName(defaultBranch)
|
||||
ref, err := vm.repo.GoGitRepo().Reference(defaultBranchRef, true)
|
||||
if err == nil {
|
||||
|
||||
@@ -18,11 +18,24 @@ type BranchState struct {
|
||||
// master.
|
||||
Trunk bool `json:"trunk,omitempty"`
|
||||
|
||||
// The commit SHA of the parent's latest commit. This is used when syncing
|
||||
// the branch with the parent to identify the commits that belong to the
|
||||
// child branch (since the HEAD of the parent branch may change).
|
||||
// This will be unset if Trunk is true.
|
||||
Head string `json:"head,omitempty"`
|
||||
// The branching point commit hash.
|
||||
//
|
||||
// When we start a new branch off of a parent branch, we record the
|
||||
// commit SHA of the parent's latest commit at that time as the
|
||||
// branching point commit hash. This allows us to later identify which
|
||||
// commits belong to the child branch when syncing with the parent
|
||||
// branch.
|
||||
//
|
||||
// NOTE: This field is named "head" in the JSON for historical reasons.
|
||||
//
|
||||
// This field may be empty if the branching off from a trunk branch. In
|
||||
// that case, we will find the branching point commit hash based on `git
|
||||
// merge-base`. Note that this will only work if the trunk branch has
|
||||
// not been rebased since the branch was created, which typically
|
||||
// stands. On the other hand, when branching off of a non-trunk branch,
|
||||
// we should almost always set this field as the non-trunk branches are
|
||||
// tend to be rebased.
|
||||
BranchingPointCommitHash string `json:"head,omitempty"`
|
||||
}
|
||||
|
||||
// unmarshalBranchState unmarshals a BranchState from JSON (which can either be
|
||||
@@ -42,7 +55,7 @@ func unmarshalBranchState(data []byte) (BranchState, error) {
|
||||
if s == "" {
|
||||
return BranchState{}, nil
|
||||
}
|
||||
return BranchState{Name: s, Head: ""}, nil
|
||||
return BranchState{Name: s, BranchingPointCommitHash: ""}, nil
|
||||
}
|
||||
|
||||
// Define a type alias here so that it doesn't have the UnmarshalJSON
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package refmeta
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/aviator-co/av/internal/git"
|
||||
"github.com/aviator-co/av/internal/meta"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ReadAllBranches fetches all branch metadata stored in the git repository.
|
||||
// It returns a map where the key is the name of the branch.
|
||||
func ReadAllBranches(ctx context.Context, repo *git.Repo) (map[string]meta.Branch, error) {
|
||||
// Find all branch metadata ref names
|
||||
// Note: need `**` here (not just `*`) because Git seems to only match one
|
||||
// level of nesting in the ref pattern with just a single `*` (even though
|
||||
// the docs seem to suggest this to not be the case). With a single star,
|
||||
// we won't match branch names like `feature/add-xyz` or `travis/fix-123`.
|
||||
refs, err := repo.ListRefs(ctx, &git.ListRefs{
|
||||
Patterns: []string{branchMetaRefPrefix + "**"},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.WithField("refs", refs).Debug("found branch metadata refs")
|
||||
|
||||
// Read the contents of each ref to get the associated metadata blob...
|
||||
refNames := make([]string, len(refs))
|
||||
for i, ref := range refs {
|
||||
refNames[i] = ref.Name
|
||||
}
|
||||
refContents, err := repo.GetRefs(ctx, &git.GetRefs{
|
||||
Revisions: refNames,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ...and for each metadata blob, parse it from JSON into a Branch
|
||||
branches := make(map[string]meta.Branch, len(refs))
|
||||
for _, ref := range refContents {
|
||||
name := strings.TrimPrefix(ref.Revision, branchMetaRefPrefix)
|
||||
branch, _ := unmarshalBranch(ctx, repo, name, ref.Revision, string(ref.Contents))
|
||||
branches[name] = branch
|
||||
}
|
||||
return branches, nil
|
||||
}
|
||||
|
||||
const branchMetaRefPrefix = "refs/av/branch-metadata/"
|
||||
|
||||
func unmarshalBranch(
|
||||
ctx context.Context,
|
||||
repo *git.Repo,
|
||||
name string,
|
||||
refName string,
|
||||
blob string,
|
||||
) (meta.Branch, bool) {
|
||||
branch := meta.Branch{Name: name}
|
||||
if err := json.Unmarshal([]byte(blob), &branch); err != nil {
|
||||
logrus.WithError(err).WithField("ref", refName).Error("corrupt stack metadata, deleting...")
|
||||
_ = repo.UpdateRef(ctx, &git.UpdateRef{Ref: refName, New: git.Missing})
|
||||
return branch, false
|
||||
}
|
||||
if branch.Parent.Name == "" {
|
||||
// COMPAT: assume parent branch is the default/mainline branch
|
||||
defaultBranch, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
// panic isn't great, but plumbing through the error is more effort
|
||||
// that it's worth here
|
||||
panic(errors.Wrap(err, "failed to determine repository default branch"))
|
||||
}
|
||||
branch.Parent.Name = defaultBranch
|
||||
branch.Parent.Trunk = true
|
||||
}
|
||||
return branch, true
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package refmeta
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aviator-co/av/internal/git"
|
||||
"github.com/aviator-co/av/internal/meta"
|
||||
"github.com/aviator-co/av/internal/utils/cleanup"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Import imports all ref metadata from the git repo into the database.
|
||||
func Import(ctx context.Context, repo *git.Repo, db meta.DB) error {
|
||||
tx := db.WriteTx()
|
||||
cu := cleanup.New(func() { tx.Abort() })
|
||||
defer cu.Cleanup()
|
||||
|
||||
repoMeta, err := ReadRepository(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.SetRepository(repoMeta)
|
||||
|
||||
allBranchMetas, err := ReadAllBranches(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, branchMeta := range allBranchMetas {
|
||||
tx.SetBranch(branchMeta)
|
||||
}
|
||||
logrus.
|
||||
WithField("branches", len(allBranchMetas)).
|
||||
Debug("Imported branches into av database from ref metadata")
|
||||
|
||||
cu.Cancel()
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package refmeta
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/aviator-co/av/internal/git"
|
||||
"github.com/aviator-co/av/internal/meta"
|
||||
)
|
||||
|
||||
// ReadRepository reads repository metadata from the git repo.
|
||||
// Returns the metadata and a boolean indicating if the metadata was found.
|
||||
func ReadRepository(repo *git.Repo) (meta.Repository, error) {
|
||||
metaPath := filepath.Join(repo.Dir(), ".git", "av", "repo-metadata.json")
|
||||
data, err := os.ReadFile(metaPath)
|
||||
if err != nil {
|
||||
return meta.Repository{}, errors.WrapIf(err, "failed to read the repository metadata")
|
||||
}
|
||||
var repository meta.Repository
|
||||
if err := json.Unmarshal(data, &repository); err != nil {
|
||||
return meta.Repository{}, errors.WrapIf(err, "failed to unmarshal the repository metadata")
|
||||
}
|
||||
return repository, nil
|
||||
}
|
||||
@@ -31,9 +31,9 @@ func CreatePlan(
|
||||
var upstreamCommit string
|
||||
// TODO: would be nice to show the user whether or not the branch is
|
||||
// already up-to-date with the parent.
|
||||
if branch.Parent.Head != "" {
|
||||
if branch.Parent.BranchingPointCommitHash != "" {
|
||||
branchCmd.Parent = branch.Parent.Name
|
||||
upstreamCommit = branch.Parent.Head
|
||||
upstreamCommit = branch.Parent.BranchingPointCommitHash
|
||||
} else {
|
||||
trunkCommit, err := repo.MergeBase(ctx, branchName, "origin/"+branch.Parent.Name)
|
||||
if err != nil {
|
||||
|
||||
@@ -38,9 +38,9 @@ func TestCreatePlan(t *testing.T) {
|
||||
tx.SetBranch(meta.Branch{
|
||||
Name: "two",
|
||||
Parent: meta.BranchState{
|
||||
Name: "one",
|
||||
Trunk: false,
|
||||
Head: c1b.String(),
|
||||
Name: "one",
|
||||
Trunk: false,
|
||||
BranchingPointCommitHash: c1b.String(),
|
||||
},
|
||||
})
|
||||
require.NoError(t, tx.Commit())
|
||||
|
||||
@@ -64,7 +64,7 @@ func (b StackBranchCmd) Execute(ctx *Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentState.Head = headCommit
|
||||
parentState.BranchingPointCommitHash = headCommit
|
||||
}
|
||||
branch.Parent = parentState
|
||||
tx.SetBranch(branch)
|
||||
|
||||
@@ -120,10 +120,7 @@ func PlanForReparent(
|
||||
if slices.Contains(children, newParentBranch.Short()) {
|
||||
return nil, errors.New("cannot re-parent to a child branch")
|
||||
}
|
||||
isParentTrunk, err := repo.IsTrunkBranch(ctx, newParentBranch.Short())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isParentTrunk := repo.IsTrunkBranch(newParentBranch.Short())
|
||||
var ret []sequencer.RestackOp
|
||||
ret = append(ret, sequencer.RestackOp{
|
||||
Name: currentBranch,
|
||||
|
||||
@@ -48,7 +48,7 @@ func GetTargetBranches(
|
||||
return ret, nil
|
||||
}
|
||||
if mode == CurrentAndParents {
|
||||
curr, err := repo.CurrentBranchName(ctx)
|
||||
curr, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func GetTargetBranches(
|
||||
return ret, nil
|
||||
}
|
||||
if mode == CurrentAndChildren {
|
||||
curr, err := repo.CurrentBranchName(ctx)
|
||||
curr, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func GetTargetBranches(
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
curr, err := repo.CurrentBranchName(ctx)
|
||||
curr, err := repo.CurrentBranchName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -36,9 +36,10 @@ type branchSnapshot struct {
|
||||
ParentBranch plumbing.ReferenceName
|
||||
// True if the parent branch is the trunk branch (refs/heads/master etc.).
|
||||
IsParentTrunk bool
|
||||
// Commit hash that the parent branch was previously at last time this was synced.
|
||||
// This is plumbing.ZeroHash if the parent branch is a trunk.
|
||||
PreviouslySyncedParentBranchHash plumbing.Hash
|
||||
// Commit hash that the branch was branched off from the parent. This
|
||||
// can be empty when the parent branch is trunk, but this can be
|
||||
// specified in some circumstances even if the parent is trunk.
|
||||
BranchingPointCommitHash plumbing.Hash
|
||||
}
|
||||
|
||||
// Sequencer re-stacks the specified branches.
|
||||
@@ -80,10 +81,9 @@ func getBranchSnapshots(db meta.DB) map[plumbing.ReferenceName]*branchSnapshot {
|
||||
ParentBranch: plumbing.ReferenceName("refs/heads/" + avbr.Parent.Name),
|
||||
}
|
||||
ret[snapshot.Name] = snapshot
|
||||
if avbr.Parent.Trunk {
|
||||
snapshot.IsParentTrunk = true
|
||||
} else {
|
||||
snapshot.PreviouslySyncedParentBranchHash = plumbing.NewHash(avbr.Parent.Head)
|
||||
snapshot.IsParentTrunk = avbr.Parent.Trunk
|
||||
if avbr.Parent.BranchingPointCommitHash != "" {
|
||||
snapshot.BranchingPointCommitHash = plumbing.NewHash(avbr.Parent.BranchingPointCommitHash)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
@@ -171,16 +171,20 @@ func (seq *Sequencer) rebaseBranch(
|
||||
panic(fmt.Sprintf("branch %q not found in original branch infos", op.Name))
|
||||
}
|
||||
|
||||
var previousParentHash plumbing.Hash
|
||||
if snapshot.IsParentTrunk {
|
||||
// Use the current remote tracking branch hash as the previous parent hash.
|
||||
var err error
|
||||
previousParentHash, err = seq.getRemoteTrackingBranchCommit(repo, snapshot.ParentBranch)
|
||||
var branchingPoint plumbing.Hash
|
||||
if snapshot.BranchingPointCommitHash.IsZero() {
|
||||
// If the branching point is not specified, find the merge-base with the parent's remote-tracking branch.
|
||||
rtb, err := seq.getRemoteTrackingBranch(repo, snapshot.ParentBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mb, err := repo.MergeBase(ctx, rtb.String(), op.Name.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
branchingPoint = plumbing.NewHash(mb)
|
||||
} else {
|
||||
previousParentHash = snapshot.PreviouslySyncedParentBranchHash
|
||||
branchingPoint = snapshot.BranchingPointCommitHash
|
||||
}
|
||||
|
||||
var newParentHash plumbing.Hash
|
||||
@@ -213,7 +217,7 @@ func (seq *Sequencer) rebaseBranch(
|
||||
// conflicts.
|
||||
skipGitRebase := false
|
||||
if b1, err := repo.IsAncestor(ctx, newParentHash.String(), op.Name.String()); err == nil && b1 {
|
||||
if b2, err := repo.IsAncestor(ctx, previousParentHash.String(), newParentHash.String()); err == nil &&
|
||||
if b2, err := repo.IsAncestor(ctx, branchingPoint.String(), newParentHash.String()); err == nil &&
|
||||
b2 {
|
||||
logrus.Debug("Skipping rebase since branch is already based on new parent")
|
||||
skipGitRebase = true
|
||||
@@ -225,7 +229,7 @@ func (seq *Sequencer) rebaseBranch(
|
||||
// The commits from `rebaseFrom` to `snapshot.Name` should be rebased onto `rebaseOnto`.
|
||||
opts := git.RebaseOpts{
|
||||
Branch: op.Name.Short(),
|
||||
Upstream: previousParentHash.String(),
|
||||
Upstream: branchingPoint.String(),
|
||||
Onto: newParentHash.String(),
|
||||
}
|
||||
var err error
|
||||
@@ -238,7 +242,7 @@ func (seq *Sequencer) rebaseBranch(
|
||||
"Failed to rebase %q onto %q (merge base is %q)\n",
|
||||
op.Name,
|
||||
op.NewParent,
|
||||
previousParentHash.String()[:7],
|
||||
branchingPoint.String()[:7],
|
||||
) + result.ErrorHeadline
|
||||
seq.SequenceInterruptedNewParentHash = newParentHash
|
||||
return result, nil
|
||||
@@ -274,7 +278,7 @@ func (seq *Sequencer) postRebaseBranchUpdate(db meta.DB, newParentHash plumbing.
|
||||
Trunk: op.NewParentIsTrunk,
|
||||
}
|
||||
if !op.NewParentIsTrunk {
|
||||
newParentBranchState.Head = newParentHash.String()
|
||||
newParentBranchState.BranchingPointCommitHash = newParentHash.String()
|
||||
}
|
||||
|
||||
tx := db.WriteTx()
|
||||
@@ -311,19 +315,27 @@ func (seq *Sequencer) getRemoteTrackingBranchCommit(
|
||||
repo *git.Repo,
|
||||
ref plumbing.ReferenceName,
|
||||
) (plumbing.Hash, error) {
|
||||
rtb, err := seq.getRemoteTrackingBranch(repo, ref)
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
}
|
||||
return seq.getBranchCommit(repo, *rtb)
|
||||
}
|
||||
|
||||
func (seq *Sequencer) getRemoteTrackingBranch(repo *git.Repo, ref plumbing.ReferenceName) (*plumbing.ReferenceName, error) {
|
||||
remote, err := repo.GoGitRepo().Remote(seq.RemoteName)
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, errors.Errorf("failed to get remote %q: %v", seq.RemoteName, err)
|
||||
return nil, errors.Errorf("failed to get remote %q: %v", seq.RemoteName, err)
|
||||
}
|
||||
rtb := mapToRemoteTrackingBranch(remote.Config(), ref)
|
||||
if rtb == nil {
|
||||
return plumbing.ZeroHash, errors.Errorf(
|
||||
return nil, errors.Errorf(
|
||||
"failed to get remote tracking branch in %q for %q",
|
||||
seq.RemoteName,
|
||||
ref,
|
||||
)
|
||||
}
|
||||
return seq.getBranchCommit(repo, *rtb)
|
||||
return rtb, nil
|
||||
}
|
||||
|
||||
func (seq *Sequencer) getBranchCommit(
|
||||
|
||||
@@ -49,7 +49,7 @@ func DetectBranches(
|
||||
// don't have to adopt it.
|
||||
continue
|
||||
}
|
||||
bp, err := traverseUntilTrunk(ctx, repo, bn, nearestTrunkCommit, hashToRefMap, refToHashMap)
|
||||
bp, err := traverseUntilTrunk(repo, bn, nearestTrunkCommit, hashToRefMap, refToHashMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,7 +59,6 @@ func DetectBranches(
|
||||
}
|
||||
|
||||
func traverseUntilTrunk(
|
||||
ctx context.Context,
|
||||
repo *avgit.Repo,
|
||||
branch plumbing.ReferenceName,
|
||||
nearestTrunkCommit plumbing.Hash,
|
||||
@@ -77,10 +76,7 @@ func traverseUntilTrunk(
|
||||
// commit that has multiple parents.
|
||||
err = object.NewCommitPreorderIter(commit, nil, nil).ForEach(func(c *object.Commit) error {
|
||||
if c.Hash == nearestTrunkCommit {
|
||||
trunk, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trunk := repo.DefaultBranch()
|
||||
ret.Parent = plumbing.NewBranchReferenceName(trunk)
|
||||
ret.ParentIsTrunk = true
|
||||
ret.ParentMergeBase = c.Hash
|
||||
@@ -116,10 +112,7 @@ func getNearestTrunkCommit(
|
||||
repo *avgit.Repo,
|
||||
ref plumbing.ReferenceName,
|
||||
) (plumbing.Hash, error) {
|
||||
trunk, err := repo.DefaultBranch(ctx)
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
}
|
||||
trunk := repo.DefaultBranch()
|
||||
rtb := fmt.Sprintf("refs/remotes/%s/%s", repo.GetRemoteName(), trunk)
|
||||
|
||||
mbArgs := []string{rtb, ref.String()}
|
||||
|
||||
Reference in New Issue
Block a user