mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-04-23 15:39:22 +00:00
feat: use git-replay for rebasing
This is better for performance, because it can do more work in-memory. It also preserves unknown headers, which can be important for some clients. For example, Jujutsu uses a non-standard "change-id" header to track commits across rebase and amend, but regular git-rebase drops such unknown headers.
This commit is contained in:
parent
eb3feaad45
commit
472bd4f97a
1 changed files with 57 additions and 1 deletions
|
@ -234,8 +234,64 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
|
||||||
}
|
}
|
||||||
|
|
||||||
// rebaseTrackingOnToBase checks out the tracking branch as staging and rebases it on to the base branch
|
// rebaseTrackingOnToBase checks out the tracking branch as staging and rebases it on to the base branch
|
||||||
// if there is a conflict it will return a models.ErrRebaseConflicts
|
|
||||||
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
|
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
|
||||||
|
// Check git version for availability of git-replay. If it is available, we use
|
||||||
|
// it for performance and to preserve unknown commit headers like the
|
||||||
|
// "change-id" header used by Jujutsu and GitButler to track changes across
|
||||||
|
// rebase, amend etc.
|
||||||
|
if err := git.CheckGitVersionAtLeast("2.44"); err == nil {
|
||||||
|
// Create staging branch
|
||||||
|
if err := git.NewCommand(ctx, "branch").AddDynamicArguments(stagingBranch, trackingBranch).
|
||||||
|
Run(ctx.RunOpts()); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"unable to git branch tracking as staging in temp repo for %v: %w\n%s\n%s",
|
||||||
|
ctx.pr, err,
|
||||||
|
ctx.outbuf.String(),
|
||||||
|
ctx.errbuf.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ctx.outbuf.Reset()
|
||||||
|
ctx.errbuf.Reset()
|
||||||
|
|
||||||
|
// Use git-replay for performance and to preserve unknown headers,
|
||||||
|
// like the "change-id" header used by Jujutsu and GitButler.
|
||||||
|
if err := git.NewCommand(ctx, "replay", "--onto").AddDynamicArguments(baseBranch).
|
||||||
|
AddDynamicArguments(fmt.Sprintf("%s..%s", baseBranch, stagingBranch)).
|
||||||
|
Run(ctx.RunOpts()); err != nil {
|
||||||
|
return fmt.Errorf("Failed to replay commits on base branch")
|
||||||
|
}
|
||||||
|
// git-replay worked, stdout contains the instructions for update-ref
|
||||||
|
updateRefInstructions := ctx.outbuf.String()
|
||||||
|
opts := ctx.RunOpts()
|
||||||
|
opts.Stdin = strings.NewReader(updateRefInstructions)
|
||||||
|
if err := git.NewCommand(ctx, "update-ref", "--stdin").Run(opts); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Failed to update ref for %v: %w\n%s\n%s",
|
||||||
|
ctx.pr,
|
||||||
|
err,
|
||||||
|
ctx.outbuf.String(),
|
||||||
|
ctx.errbuf.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Checkout staging branch
|
||||||
|
if err := git.NewCommand(ctx, "checkout").AddDynamicArguments(stagingBranch).
|
||||||
|
Run(ctx.RunOpts()); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"unable to git checkout staging in temp repo for %v: %w\n%s\n%s",
|
||||||
|
ctx.pr,
|
||||||
|
err,
|
||||||
|
ctx.outbuf.String(),
|
||||||
|
ctx.errbuf.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ctx.outbuf.Reset()
|
||||||
|
ctx.errbuf.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The available git version is too old to support git-replay.
|
||||||
|
// Fall back to regular rebase.
|
||||||
|
|
||||||
// Checkout head branch
|
// Checkout head branch
|
||||||
if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
|
if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
|
||||||
Run(ctx.RunOpts()); err != nil {
|
Run(ctx.RunOpts()); err != nil {
|
||||||
|
|
Loading…
Add table
Reference in a new issue