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
|
# If true, periodically refresh files and submodules
|
||||||
autoRefresh: true
|
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
|
# If true, pass the --all arg to git fetch
|
||||||
fetchAll: true
|
fetchAll: true
|
||||||
|
|
||||||
|
|
|
@ -285,3 +285,11 @@ func (self *BranchCommands) IsBranchMerged(branch *models.Branch, mainBranches *
|
||||||
|
|
||||||
return stdout == "", nil
|
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"]
|
// outputs args vector e.g. ["git", "commit", "-m", "my message"]
|
||||||
Args() []string
|
Args() []string
|
||||||
|
|
||||||
|
// Set a string to be used as stdin for the command.
|
||||||
|
SetStdin(input string) ICmdObj
|
||||||
|
|
||||||
AddEnvVars(...string) ICmdObj
|
AddEnvVars(...string) ICmdObj
|
||||||
GetEnvVars() []string
|
GetEnvVars() []string
|
||||||
|
|
||||||
|
@ -131,6 +134,12 @@ func (self *CmdObj) Args() []string {
|
||||||
return self.cmd.Args
|
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 {
|
func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
|
||||||
self.cmd.Env = append(self.cmd.Env, vars...)
|
self.cmd.Env = append(self.cmd.Env, vars...)
|
||||||
|
|
||||||
|
|
|
@ -244,6 +244,9 @@ type GitConfig struct {
|
||||||
AutoFetch bool `yaml:"autoFetch"`
|
AutoFetch bool `yaml:"autoFetch"`
|
||||||
// If true, periodically refresh files and submodules
|
// If true, periodically refresh files and submodules
|
||||||
AutoRefresh bool `yaml:"autoRefresh"`
|
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
|
// If true, pass the --all arg to git fetch
|
||||||
FetchAll bool `yaml:"fetchAll"`
|
FetchAll bool `yaml:"fetchAll"`
|
||||||
// If true, lazygit will automatically stage files that used to have merge
|
// If true, lazygit will automatically stage files that used to have merge
|
||||||
|
@ -822,6 +825,7 @@ func GetDefaultConfig() *UserConfig {
|
||||||
MainBranches: []string{"master", "main"},
|
MainBranches: []string{"master", "main"},
|
||||||
AutoFetch: true,
|
AutoFetch: true,
|
||||||
AutoRefresh: true,
|
AutoRefresh: true,
|
||||||
|
AutoForwardBranches: "onlyMainBranches",
|
||||||
FetchAll: true,
|
FetchAll: true,
|
||||||
AutoStageResolvedConflicts: true,
|
AutoStageResolvedConflicts: true,
|
||||||
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
|
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 {
|
[]string{"none", "onlyArrow", "arrowAndNumber"}); err != nil {
|
||||||
return err
|
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 {
|
if err := validateKeybindings(config.Keybinding); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,11 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
|
||||||
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
|
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
|
||||||
err = self.gui.git.Sync.FetchBackground()
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1196,7 +1196,11 @@ func (self *FilesController) fetch() error {
|
||||||
return errors.New(self.c.Tr.PassUnameWrong)
|
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
|
return err
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
@ -263,3 +264,34 @@ func (self *BranchesHelper) deleteRemoteBranches(remoteBranches []*models.Remote
|
||||||
}
|
}
|
||||||
return nil
|
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
|
RenameBranch string
|
||||||
CreateBranch string
|
CreateBranch string
|
||||||
FastForwardBranch string
|
FastForwardBranch string
|
||||||
|
AutoForwardBranches string
|
||||||
CherryPick string
|
CherryPick string
|
||||||
CheckoutFile string
|
CheckoutFile string
|
||||||
DiscardOldFileChange string
|
DiscardOldFileChange string
|
||||||
|
@ -2059,6 +2060,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||||
MixedReset: "Mixed reset",
|
MixedReset: "Mixed reset",
|
||||||
HardReset: "Hard reset",
|
HardReset: "Hard reset",
|
||||||
FastForwardBranch: "Fast forward branch",
|
FastForwardBranch: "Fast forward branch",
|
||||||
|
AutoForwardBranches: "Auto-forward branches",
|
||||||
Undo: "Undo",
|
Undo: "Undo",
|
||||||
Redo: "Redo",
|
Redo: "Redo",
|
||||||
CopyPullRequestURL: "Copy pull request URL",
|
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.RemoveNested,
|
||||||
submodule.Reset,
|
submodule.Reset,
|
||||||
submodule.ResetFolder,
|
submodule.ResetFolder,
|
||||||
|
sync.FetchAndAutoForwardBranchesAllBranches,
|
||||||
|
sync.FetchAndAutoForwardBranchesNone,
|
||||||
|
sync.FetchAndAutoForwardBranchesOnlyMainBranches,
|
||||||
sync.FetchPrune,
|
sync.FetchPrune,
|
||||||
sync.FetchWhenSortedByDate,
|
sync.FetchWhenSortedByDate,
|
||||||
sync.ForcePush,
|
sync.ForcePush,
|
||||||
|
|
|
@ -325,6 +325,16 @@
|
||||||
"description": "If true, periodically refresh files and submodules",
|
"description": "If true, periodically refresh files and submodules",
|
||||||
"default": true
|
"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": {
|
"fetchAll": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "If true, pass the --all arg to git fetch",
|
"description": "If true, pass the --all arg to git fetch",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue