mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-10 20:05:50 +02:00
Add config for auto-forwarding branches after fetching
This commit is contained in:
parent
2174762315
commit
eaaa937315
14 changed files with 248 additions and 2 deletions
|
@ -337,6 +337,10 @@ git:
|
|||
# If true, periodically refresh files and submodules
|
||||
autoRefresh: true
|
||||
|
||||
# If not "none", lazygit will automatically forward branches to their upstream after fetching. Applies to branches that are not the currently checked out branch, and only to those that are strictly behind their upstream (as opposed to diverged).
|
||||
# Possible values: 'none' | 'onlyMainBranches' | 'allBranches'
|
||||
autoForwardBranches: onlyMainBranches
|
||||
|
||||
# If true, pass the --all arg to git fetch
|
||||
fetchAll: true
|
||||
|
||||
|
|
|
@ -285,3 +285,11 @@ func (self *BranchCommands) IsBranchMerged(branch *models.Branch, mainBranches *
|
|||
|
||||
return stdout == "", nil
|
||||
}
|
||||
|
||||
func (self *BranchCommands) UpdateBranchRefs(updateCommands string) error {
|
||||
cmdArgs := NewGitCmd("update-ref").
|
||||
Arg("--stdin").
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).SetStdin(updateCommands).Run()
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ type ICmdObj interface {
|
|||
// outputs args vector e.g. ["git", "commit", "-m", "my message"]
|
||||
Args() []string
|
||||
|
||||
// Set a string to be used as stdin for the command.
|
||||
SetStdin(input string) ICmdObj
|
||||
|
||||
AddEnvVars(...string) ICmdObj
|
||||
GetEnvVars() []string
|
||||
|
||||
|
@ -131,6 +134,12 @@ func (self *CmdObj) Args() []string {
|
|||
return self.cmd.Args
|
||||
}
|
||||
|
||||
func (self *CmdObj) SetStdin(input string) ICmdObj {
|
||||
self.cmd.Stdin = strings.NewReader(input)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
|
||||
self.cmd.Env = append(self.cmd.Env, vars...)
|
||||
|
||||
|
|
|
@ -244,6 +244,9 @@ type GitConfig struct {
|
|||
AutoFetch bool `yaml:"autoFetch"`
|
||||
// If true, periodically refresh files and submodules
|
||||
AutoRefresh bool `yaml:"autoRefresh"`
|
||||
// If not "none", lazygit will automatically forward branches to their upstream after fetching. Applies to branches that are not the currently checked out branch, and only to those that are strictly behind their upstream (as opposed to diverged).
|
||||
// Possible values: 'none' | 'onlyMainBranches' | 'allBranches'
|
||||
AutoForwardBranches string `yaml:"autoForwardBranches" jsonschema:"enum=none,enum=onlyMainBranches,enum=allBranches"`
|
||||
// If true, pass the --all arg to git fetch
|
||||
FetchAll bool `yaml:"fetchAll"`
|
||||
// If true, lazygit will automatically stage files that used to have merge
|
||||
|
@ -822,6 +825,7 @@ func GetDefaultConfig() *UserConfig {
|
|||
MainBranches: []string{"master", "main"},
|
||||
AutoFetch: true,
|
||||
AutoRefresh: true,
|
||||
AutoForwardBranches: "onlyMainBranches",
|
||||
FetchAll: true,
|
||||
AutoStageResolvedConflicts: true,
|
||||
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
|
||||
|
|
|
@ -19,6 +19,10 @@ func (config *UserConfig) Validate() error {
|
|||
[]string{"none", "onlyArrow", "arrowAndNumber"}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateEnum("git.autoForwardBranches", config.Git.AutoForwardBranches,
|
||||
[]string{"none", "onlyMainBranches", "allBranches"}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateKeybindings(config.Keybinding); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -125,7 +125,11 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
|
|||
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
|
||||
err = self.gui.git.Sync.FetchBackground()
|
||||
|
||||
_ = self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})
|
||||
_ = self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.SYNC})
|
||||
|
||||
if err == nil {
|
||||
err = self.gui.helpers.BranchesHelper.AutoForwardBranches()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1196,7 +1196,11 @@ func (self *FilesController) fetch() error {
|
|||
return errors.New(self.c.Tr.PassUnameWrong)
|
||||
}
|
||||
|
||||
_ = self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})
|
||||
_ = self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.SYNC})
|
||||
|
||||
if err == nil {
|
||||
err = self.c.Helpers().BranchesHelper.AutoForwardBranches()
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@ package helpers
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
|
@ -263,3 +264,34 @@ func (self *BranchesHelper) deleteRemoteBranches(remoteBranches []*models.Remote
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BranchesHelper) AutoForwardBranches() error {
|
||||
if self.c.UserConfig().Git.AutoForwardBranches == "none" {
|
||||
return nil
|
||||
}
|
||||
|
||||
allBranches := self.c.UserConfig().Git.AutoForwardBranches == "allBranches"
|
||||
branches := self.c.Model().Branches
|
||||
updateCommands := ""
|
||||
// The first branch is the currently checked out branch; skip it
|
||||
for _, branch := range branches[1:] {
|
||||
if branch.RemoteBranchStoredLocally() && (allBranches || lo.Contains(self.c.UserConfig().Git.MainBranches, branch.Name)) {
|
||||
isStrictlyBehind := branch.IsBehindForPull() && !branch.IsAheadForPull()
|
||||
if isStrictlyBehind {
|
||||
updateCommands += fmt.Sprintf("update %s %s %s\n", branch.FullRefName(), branch.FullUpstreamRefName(), branch.CommitHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if updateCommands == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.AutoForwardBranches)
|
||||
self.c.LogCommand(strings.TrimRight(updateCommands, "\n"), false)
|
||||
err := self.c.Git().Branch.UpdateBranchRefs(updateCommands)
|
||||
|
||||
_ = self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES}, Mode: types.SYNC})
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -931,6 +931,7 @@ type Actions struct {
|
|||
RenameBranch string
|
||||
CreateBranch string
|
||||
FastForwardBranch string
|
||||
AutoForwardBranches string
|
||||
CherryPick string
|
||||
CheckoutFile string
|
||||
DiscardOldFileChange string
|
||||
|
@ -2059,6 +2060,7 @@ func EnglishTranslationSet() *TranslationSet {
|
|||
MixedReset: "Mixed reset",
|
||||
HardReset: "Hard reset",
|
||||
FastForwardBranch: "Fast forward branch",
|
||||
AutoForwardBranches: "Auto-forward branches",
|
||||
Undo: "Undo",
|
||||
Redo: "Redo",
|
||||
CopyPullRequestURL: "Copy pull request URL",
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var FetchAndAutoForwardBranchesAllBranches = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Fetch from remote and auto-forward branches with config set to 'allBranches'",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.AutoForwardBranches = "allBranches"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateNCommits(3)
|
||||
shell.NewBranch("feature")
|
||||
shell.NewBranch("diverged")
|
||||
shell.CloneIntoRemote("origin")
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
shell.SetBranchUpstream("feature", "origin/feature")
|
||||
shell.SetBranchUpstream("diverged", "origin/diverged")
|
||||
shell.Checkout("master")
|
||||
shell.HardReset("HEAD^")
|
||||
shell.Checkout("feature")
|
||||
shell.HardReset("HEAD~2")
|
||||
shell.Checkout("diverged")
|
||||
shell.HardReset("HEAD~2")
|
||||
shell.EmptyCommit("local")
|
||||
shell.NewBranch("checked-out")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ↓2").DoesNotContain("↑"),
|
||||
Contains("master ↓1").DoesNotContain("↑"),
|
||||
)
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press(keys.Files.Fetch)
|
||||
|
||||
// AutoForwardBranches is "allBranches": both master and feature get forwarded
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ✓"),
|
||||
Contains("master ✓"),
|
||||
)
|
||||
},
|
||||
})
|
|
@ -0,0 +1,54 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var FetchAndAutoForwardBranchesNone = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Fetch from remote and auto-forward branches with config set to 'none'",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.AutoForwardBranches = "none"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateNCommits(3)
|
||||
shell.NewBranch("feature")
|
||||
shell.NewBranch("diverged")
|
||||
shell.CloneIntoRemote("origin")
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
shell.SetBranchUpstream("feature", "origin/feature")
|
||||
shell.SetBranchUpstream("diverged", "origin/diverged")
|
||||
shell.Checkout("master")
|
||||
shell.HardReset("HEAD^")
|
||||
shell.Checkout("feature")
|
||||
shell.HardReset("HEAD~2")
|
||||
shell.Checkout("diverged")
|
||||
shell.HardReset("HEAD~2")
|
||||
shell.EmptyCommit("local")
|
||||
shell.NewBranch("checked-out")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ↓2").DoesNotContain("↑"),
|
||||
Contains("master ↓1").DoesNotContain("↑"),
|
||||
)
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press(keys.Files.Fetch)
|
||||
|
||||
// AutoForwardBranches is "none": nothing should happen
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ↓2").DoesNotContain("↑"),
|
||||
Contains("master ↓1").DoesNotContain("↑"),
|
||||
)
|
||||
},
|
||||
})
|
|
@ -0,0 +1,54 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var FetchAndAutoForwardBranchesOnlyMainBranches = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Fetch from remote and auto-forward branches with config set to 'onlyMainBranches'",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.AutoForwardBranches = "onlyMainBranches"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateNCommits(3)
|
||||
shell.NewBranch("feature")
|
||||
shell.NewBranch("diverged")
|
||||
shell.CloneIntoRemote("origin")
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
shell.SetBranchUpstream("feature", "origin/feature")
|
||||
shell.SetBranchUpstream("diverged", "origin/diverged")
|
||||
shell.Checkout("master")
|
||||
shell.HardReset("HEAD^")
|
||||
shell.Checkout("feature")
|
||||
shell.HardReset("HEAD~2")
|
||||
shell.Checkout("diverged")
|
||||
shell.HardReset("HEAD~2")
|
||||
shell.EmptyCommit("local")
|
||||
shell.NewBranch("checked-out")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ↓2").DoesNotContain("↑"),
|
||||
Contains("master ↓1").DoesNotContain("↑"),
|
||||
)
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press(keys.Files.Fetch)
|
||||
|
||||
// AutoForwardBranches is "onlyMainBranches": master gets forwarded, but feature doesn't
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ↓2").DoesNotContain("↑"),
|
||||
Contains("master ✓"),
|
||||
)
|
||||
},
|
||||
})
|
|
@ -360,6 +360,9 @@ var tests = []*components.IntegrationTest{
|
|||
submodule.RemoveNested,
|
||||
submodule.Reset,
|
||||
submodule.ResetFolder,
|
||||
sync.FetchAndAutoForwardBranchesAllBranches,
|
||||
sync.FetchAndAutoForwardBranchesNone,
|
||||
sync.FetchAndAutoForwardBranchesOnlyMainBranches,
|
||||
sync.FetchPrune,
|
||||
sync.FetchWhenSortedByDate,
|
||||
sync.ForcePush,
|
||||
|
|
|
@ -325,6 +325,16 @@
|
|||
"description": "If true, periodically refresh files and submodules",
|
||||
"default": true
|
||||
},
|
||||
"autoForwardBranches": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"onlyMainBranches",
|
||||
"allBranches"
|
||||
],
|
||||
"description": "If not \"none\", lazygit will automatically forward branches to their upstream after fetching. Applies to branches that are not the currently checked out branch, and only to those that are strictly behind their upstream (as opposed to diverged).\nPossible values: 'none' | 'onlyMainBranches' | 'allBranches'",
|
||||
"default": "onlyMainBranches"
|
||||
},
|
||||
"fetchAll": {
|
||||
"type": "boolean",
|
||||
"description": "If true, pass the --all arg to git fetch",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue