mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 05:26:53 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
954a4d2fbb |
@@ -2,7 +2,7 @@
|
||||
|
||||
- **Please check if the PR fulfills these requirements**
|
||||
|
||||
* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
|
||||
* [ ] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go generate`)
|
||||
* [ ] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
|
||||
* [ ] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide)
|
||||
* [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
|
||||
|
||||
@@ -127,14 +127,17 @@ jobs:
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
- name: Check Cheatsheet
|
||||
run: |
|
||||
go run scripts/cheatsheet/main.go check
|
||||
- name: Check Vendor Directory
|
||||
# ensure our vendor directory matches up with our go modules
|
||||
run: |
|
||||
go mod vendor && git diff --exit-code || (echo "Unexpected change to vendor directory. Run 'go mod vendor' locally and commit the changes" && exit 1)
|
||||
- name: Check All Auto-Generated Files
|
||||
# ensure all our auto-generated files are up to date
|
||||
- name: Check Integration Test List
|
||||
# ensure our integration test list is up to date
|
||||
run: |
|
||||
go generate ./... && git diff --quiet || (git status -s; echo "Auto-generated files not up to date. Run 'go generate ./...' locally and commit the changes" && exit 1)
|
||||
go generate pkg/integration/tests/tests.go && git diff --exit-code || (echo "Integration test list not up to date. Run 'go generate pkg/integration/tests/tests.go' locally and commit the changes" && exit 1)
|
||||
shell: bash # needed so that we get "-o pipefail"
|
||||
- name: Check Filenames
|
||||
run: scripts/check_filenames.sh
|
||||
|
||||
+1
-5
@@ -20,11 +20,7 @@ linters:
|
||||
linters-settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
staticcheck:
|
||||
# SA1019 is for checking that we're not using fields marked as deprecated
|
||||
# in a comment. It decides this in a loose way so I'm silencing it. Also because
|
||||
# it's tripping on our own structs.
|
||||
checks: ["all", "-SA1019"]
|
||||
|
||||
nakedret:
|
||||
# the gods will judge me but I just don't like naked returns at all
|
||||
max-func-lines: 0
|
||||
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"gopls": {
|
||||
"formatting.gofumpt": true,
|
||||
},
|
||||
}
|
||||
@@ -3,7 +3,7 @@ all: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -gcflags='all=-N -l'
|
||||
go build
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
@@ -29,7 +29,6 @@ unit-test:
|
||||
.PHONY: test
|
||||
test: unit-test integration-test-all
|
||||
|
||||
# Generate all our auto-generated files (test list, cheatsheets, maybe other things in the future)
|
||||
.PHONY: generate
|
||||
generate:
|
||||
go generate ./...
|
||||
@@ -38,6 +37,10 @@ generate:
|
||||
format:
|
||||
gofumpt -l -w .
|
||||
|
||||
.PHONY: update-cheatsheet
|
||||
update-cheatsheet:
|
||||
go run scripts/cheatsheet/main.go generate
|
||||
|
||||
# For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md.
|
||||
.PHONY: integration-test-tui
|
||||
integration-test-tui:
|
||||
|
||||
+4
-5
@@ -85,7 +85,6 @@ gui:
|
||||
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
||||
border: 'rounded' # one of 'single' | 'double' | 'rounded' | 'hidden'
|
||||
animateExplosion: true # shows an explosion animation when nuking the working tree
|
||||
portraitMode: 'auto' # one of 'auto' | 'never' | 'always'
|
||||
git:
|
||||
paging:
|
||||
colorArg: always
|
||||
@@ -163,7 +162,7 @@ keybinding:
|
||||
jumpToBlock: ['1', '2', '3', '4', '5'] # goto the Nth block / panel
|
||||
nextMatch: 'n'
|
||||
prevMatch: 'N'
|
||||
optionMenu: <disabled> # show help menu
|
||||
optionMenu: null # show help menu
|
||||
optionMenu-alt1: '?' # show help menu
|
||||
select: '<space>'
|
||||
goInto: '<enter>'
|
||||
@@ -319,7 +318,7 @@ os:
|
||||
editPreset: 'vscode'
|
||||
```
|
||||
|
||||
Supported presets are `vim`, `nvim`, `nvim-remote`, `emacs`, `nano`, `micro`, `vscode`, `sublime`, `bbedit`, `kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
Supported presets are `vim`, `nvim`, `nvim-remote`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`, `kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
|
||||
`nvim-remote` is an experimental preset for when you have invoked lazygit from within a neovim process, allowing lazygit to open the file from within the parent process rather than spawning a new one.
|
||||
|
||||
@@ -462,12 +461,12 @@ Supported versions are "2" and "3". The deprecated config `showIcons` sets the v
|
||||
|
||||
For all possible keybinding options, check [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md)
|
||||
|
||||
You can disable certain key bindings by specifying `<disabled>`.
|
||||
You can disable certain key bindings by specifying `null`.
|
||||
|
||||
```yaml
|
||||
keybinding:
|
||||
universal:
|
||||
edit: <disabled> # disable 'edit file'
|
||||
edit: null # disable 'edit file'
|
||||
```
|
||||
|
||||
### Example Keybindings For Colemak Users
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit キーバインド
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit 키 바인딩
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit Sneltoetsen
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit Связки клавиш
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit 按键绑定
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazygit 鍵盤快捷鍵
|
||||
|
||||
|
||||
@@ -180,10 +180,10 @@ func parseCliArgsAndEnvVars() *cliArgs {
|
||||
useConfigDir := ""
|
||||
flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory")
|
||||
|
||||
workTree := os.Getenv("GIT_WORK_TREE")
|
||||
workTree := ""
|
||||
flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
|
||||
|
||||
gitDir := os.Getenv("GIT_DIR")
|
||||
gitDir := ""
|
||||
flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
|
||||
|
||||
customConfigFile := ""
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package cheatsheet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
)
|
||||
|
||||
func Check() {
|
||||
dir := GetKeybindingsDir()
|
||||
tmpDir := filepath.Join(os.TempDir(), "lazygit_cheatsheet")
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
err = os.Mkdir(tmpDir, 0o700)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
|
||||
generateAtDir(tmpDir)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
actualContent := obtainContent(dir)
|
||||
expectedContent := obtainContent(tmpDir)
|
||||
|
||||
if expectedContent == "" {
|
||||
log.Fatal("empty expected content")
|
||||
}
|
||||
|
||||
if actualContent != expectedContent {
|
||||
err := difflib.WriteUnifiedDiff(os.Stdout, difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(expectedContent),
|
||||
B: difflib.SplitLines(actualContent),
|
||||
FromFile: "Expected",
|
||||
FromDate: "",
|
||||
ToFile: "Actual",
|
||||
ToDate: "",
|
||||
Context: 1,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
fmt.Printf("\nCheatsheets are out of date. Please run `%s` at the project root and commit the changes. If you run the script and no keybindings files are updated as a result, try rebasing onto master and trying again.\n", CommandToRun())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("\nCheatsheets are up to date")
|
||||
}
|
||||
|
||||
func obtainContent(dir string) string {
|
||||
re := regexp.MustCompile(`Keybindings_\w+\.md$`)
|
||||
|
||||
content := ""
|
||||
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if re.MatchString(path) {
|
||||
bytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
content += fmt.Sprintf("\n%s\n\n", filepath.Base(path))
|
||||
content += string(bytes)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
//go:generate go run generator.go
|
||||
|
||||
// This "script" generates files called Keybindings_{{.LANG}}.md
|
||||
// in the docs/keybindings directory.
|
||||
// This "script" generates a file called Keybindings_{{.LANG}}.md
|
||||
// in current working directory.
|
||||
//
|
||||
// The content of these generated files is a keybindings cheatsheet.
|
||||
// The content of this generated file is a keybindings cheatsheet.
|
||||
//
|
||||
// To generate the cheatsheets, run:
|
||||
// go generate pkg/cheatsheet/generate.go
|
||||
// To generate cheatsheet in english run:
|
||||
// go run scripts/generate_cheatsheet.go
|
||||
|
||||
package cheatsheet
|
||||
|
||||
@@ -44,7 +42,7 @@ type headerWithBindings struct {
|
||||
}
|
||||
|
||||
func CommandToRun() string {
|
||||
return "go generate ./..."
|
||||
return "go run scripts/cheatsheet/main.go generate"
|
||||
}
|
||||
|
||||
func GetKeybindingsDir() string {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/cheatsheet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Generating cheatsheets in %s...\n", cheatsheet.GetKeybindingsDir())
|
||||
cheatsheet.Generate()
|
||||
}
|
||||
+5
-4
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
@@ -47,7 +48,6 @@ type GitCommand struct {
|
||||
|
||||
type Loaders struct {
|
||||
BranchLoader *git_commands.BranchLoader
|
||||
MergedBranchLoader *git_commands.MergedBranchLoader
|
||||
CommitFileLoader *git_commands.CommitFileLoader
|
||||
CommitLoader *git_commands.CommitLoader
|
||||
FileLoader *git_commands.FileLoader
|
||||
@@ -63,6 +63,7 @@ func NewGitCommand(
|
||||
version *git_commands.GitVersion,
|
||||
osCommand *oscommands.OSCommand,
|
||||
gitConfig git_config.IGitConfig,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) (*GitCommand, error) {
|
||||
currentPath, err := os.Getwd()
|
||||
if err != nil {
|
||||
@@ -117,6 +118,7 @@ func NewGitCommand(
|
||||
gitConfig,
|
||||
repoPaths,
|
||||
repository,
|
||||
syncMutex,
|
||||
), nil
|
||||
}
|
||||
|
||||
@@ -127,6 +129,7 @@ func NewGitCommandAux(
|
||||
gitConfig git_config.IGitConfig,
|
||||
repoPaths *git_commands.RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) *GitCommand {
|
||||
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd)
|
||||
|
||||
@@ -137,7 +140,7 @@ func NewGitCommandAux(
|
||||
// common ones are: cmn, osCommand, dotGitDir, configCommands
|
||||
configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo)
|
||||
|
||||
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands)
|
||||
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands, syncMutex)
|
||||
|
||||
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
|
||||
statusCommands := git_commands.NewStatusCommands(gitCommon)
|
||||
@@ -163,7 +166,6 @@ func NewGitCommandAux(
|
||||
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||
mergedBranchLoader := git_commands.NewMergedBranchLoader(gitCommon)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon)
|
||||
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
||||
@@ -202,7 +204,6 @@ func NewGitCommandAux(
|
||||
Worktrees: worktreeLoader,
|
||||
StashLoader: stashLoader,
|
||||
TagLoader: tagLoader,
|
||||
MergedBranchLoader: mergedBranchLoader,
|
||||
},
|
||||
RepoPaths: repoPaths,
|
||||
}
|
||||
|
||||
@@ -568,26 +568,50 @@ func (self *CommitLoader) getMergeBase(refName string) string {
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getExistingMainBranches() []string {
|
||||
var existingBranches []string
|
||||
var wg sync.WaitGroup
|
||||
|
||||
mainBranches := self.UserConfig.Git.MainBranches
|
||||
existingBranches = make([]string, len(mainBranches))
|
||||
|
||||
existingBranches := utils.ConcurrentMap(mainBranches, func(mainBranch string) string {
|
||||
// Try to determine upstream of local main branch
|
||||
if ref, err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--symbolic-full-name", mainBranch+"@{u}").ToArgv(),
|
||||
).DontLog().RunWithOutput(); err == nil {
|
||||
return strings.TrimSpace(ref)
|
||||
}
|
||||
for i, branchName := range mainBranches {
|
||||
wg.Add(1)
|
||||
i := i
|
||||
branchName := branchName
|
||||
go utils.Safe(func() {
|
||||
defer wg.Done()
|
||||
|
||||
// If this failed, fallback to the local branch
|
||||
ref := "refs/heads/" + mainBranch
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
return ref
|
||||
}
|
||||
// Try to determine upstream of local main branch
|
||||
if ref, err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
|
||||
).DontLog().RunWithOutput(); err == nil {
|
||||
existingBranches[i] = strings.TrimSpace(ref)
|
||||
return
|
||||
}
|
||||
|
||||
return ""
|
||||
})
|
||||
// If this failed, a local branch for this main branch doesn't exist or it
|
||||
// has no upstream configured. Try looking for one in the "origin" remote.
|
||||
ref := "refs/remotes/origin/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
return
|
||||
}
|
||||
|
||||
// If this failed as well, try if we have the main branch as a local
|
||||
// branch. This covers the case where somebody is using git locally
|
||||
// for something, but never pushing anywhere.
|
||||
ref = "refs/heads/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
|
||||
return branch != ""
|
||||
|
||||
@@ -74,10 +74,12 @@ func TestGetCommits(t *testing.T) {
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
||||
// here it's testing which of the configured main branches have an upstream
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "refs/remotes/origin/main", nil). // this one does too
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "", errors.New("error")). // this one doesn't, so check for a local branch
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/develop"}, "", errors.New("error")). // no local branch either
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", nil). // yep, origin/main exists
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/develop"}, "", errors.New("error")). // doesn't exist there, either, so it checks for a local branch
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/develop"}, "", errors.New("error")). // no local branch either
|
||||
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/remotes/origin/master", "refs/remotes/origin/main"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||
|
||||
@@ -210,8 +212,10 @@ func TestGetCommits(t *testing.T) {
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||
// here it's testing which of the configured main branches exist; neither does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")),
|
||||
|
||||
expectedCommits: []*models.Commit{
|
||||
@@ -246,6 +250,7 @@ func TestGetCommits(t *testing.T) {
|
||||
// here it's testing which of the configured main branches exist
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "refs/remotes/origin/develop", nil).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "1.0-hotfixes@{u}"}, "refs/remotes/origin/1.0-hotfixes", nil).
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
type GitCommon struct {
|
||||
@@ -14,6 +15,8 @@ type GitCommon struct {
|
||||
repoPaths *RepoPaths
|
||||
repo *gogit.Repository
|
||||
config *ConfigCommands
|
||||
// mutex for doing things like push/pull/fetch
|
||||
syncMutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
func NewGitCommon(
|
||||
@@ -24,6 +27,7 @@ func NewGitCommon(
|
||||
repoPaths *RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
config *ConfigCommands,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) *GitCommon {
|
||||
return &GitCommon{
|
||||
Common: cmn,
|
||||
@@ -33,5 +37,6 @@ func NewGitCommon(
|
||||
repoPaths: repoPaths,
|
||||
repo: repo,
|
||||
config: config,
|
||||
syncMutex: syncMutex,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// Returns a set of branch names which are merged into a main branch.
|
||||
// This
|
||||
type MergedBranchLoader struct {
|
||||
c *GitCommon
|
||||
}
|
||||
|
||||
func NewMergedBranchLoader(c *GitCommon) *MergedBranchLoader {
|
||||
return &MergedBranchLoader{c: c}
|
||||
}
|
||||
|
||||
// TODO: check against upstreams, and share code with commit loader that determines main branches
|
||||
func (self *MergedBranchLoader) Load() *set.Set[string] {
|
||||
set := set.New[string]()
|
||||
|
||||
mainBranches := self.c.UserConfig.Git.MainBranches
|
||||
results := utils.ConcurrentMap(mainBranches, func(mainBranch string) []string {
|
||||
mergedBranches, err := self.GetMergedBranches(mainBranch)
|
||||
if err != nil {
|
||||
self.c.Log.Warnf("Failed to get merged branches for %s: %s", mainBranch, err)
|
||||
return nil
|
||||
}
|
||||
return mergedBranches
|
||||
})
|
||||
|
||||
for i := 0; i < len(mainBranches); i++ {
|
||||
for _, mergedBranch := range results[i] {
|
||||
set.Add(mergedBranch)
|
||||
}
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (self *MergedBranchLoader) GetMergedBranches(mainBranch string) ([]string, error) {
|
||||
// git for-each-ref --merged master --format '%(refname)' refs/heads/
|
||||
cmdArgs := NewGitCmd("for-each-ref").Arg(
|
||||
"--merged",
|
||||
mainBranch,
|
||||
"--format",
|
||||
"%(refname)",
|
||||
"refs/heads/",
|
||||
).ToArgv()
|
||||
|
||||
output, err := self.c.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
branches := utils.SplitLines(output)
|
||||
|
||||
return branches, nil
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName strin
|
||||
Arg(remoteName, "--delete", branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) DeleteRemoteTag(task gocui.Task, remoteName string, tagName string) error {
|
||||
@@ -61,7 +61,7 @@ func (self *RemoteCommands) DeleteRemoteTag(task gocui.Task, remoteName string,
|
||||
Arg(remoteName, "--delete", tagName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
// CheckRemoteBranchExists Returns remote branch
|
||||
|
||||
@@ -36,7 +36,7 @@ func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (oscommands
|
||||
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
|
||||
ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task)
|
||||
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex)
|
||||
return cmdObj, nil
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ func (self *SyncCommands) FetchBackgroundCmdObj() oscommands.ICmdObj {
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs)
|
||||
cmdObj.DontLog().FailOnCredentialRequest()
|
||||
cmdObj.WithMutex(self.syncMutex)
|
||||
return cmdObj
|
||||
}
|
||||
|
||||
@@ -95,7 +96,7 @@ func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
||||
|
||||
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
|
||||
// has 'pull.rebase = interactive' configured.
|
||||
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).Run()
|
||||
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FastForward(
|
||||
@@ -109,7 +110,7 @@ func (self *SyncCommands) FastForward(
|
||||
Arg(remoteBranchName + ":" + branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchRemote(task gocui.Task, remoteName string) error {
|
||||
@@ -117,5 +118,5 @@ func (self *SyncCommands) FetchRemote(task gocui.Task, remoteName string) error
|
||||
Arg(remoteName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
@@ -52,5 +52,5 @@ func (self *TagCommands) Push(task gocui.Task, remoteName string, tagName string
|
||||
cmdArgs := NewGitCmd("push").Arg(remoteName, "tag", tagName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
@@ -99,30 +99,32 @@ func (self *HostingServiceMgr) getCandidateServiceDomains() []ServiceDomain {
|
||||
|
||||
serviceDomains := slices.Clone(defaultServiceDomains)
|
||||
|
||||
for gitDomain, typeAndDomain := range self.configServiceDomains {
|
||||
provider, webDomain, success := strings.Cut(typeAndDomain, ":")
|
||||
if len(self.configServiceDomains) > 0 {
|
||||
for gitDomain, typeAndDomain := range self.configServiceDomains {
|
||||
provider, webDomain, success := strings.Cut(typeAndDomain, ":")
|
||||
|
||||
// we allow for one ':' for specifying the TCP port
|
||||
if !success || strings.Count(webDomain, ":") > 1 {
|
||||
self.log.Errorf("Unexpected format for git service: '%s'. Expected something like 'github.com:github.com'", typeAndDomain)
|
||||
continue
|
||||
}
|
||||
// we allow for one ':' for specifying the TCP port
|
||||
if !success || strings.Count(webDomain, ":") > 1 {
|
||||
self.log.Errorf("Unexpected format for git service: '%s'. Expected something like 'github.com:github.com'", typeAndDomain)
|
||||
continue
|
||||
}
|
||||
|
||||
serviceDefinition, ok := serviceDefinitionByProvider[provider]
|
||||
if !ok {
|
||||
providerNames := lo.Map(serviceDefinitions, func(serviceDefinition ServiceDefinition, _ int) string {
|
||||
return serviceDefinition.provider
|
||||
serviceDefinition, ok := serviceDefinitionByProvider[provider]
|
||||
if !ok {
|
||||
providerNames := lo.Map(serviceDefinitions, func(serviceDefinition ServiceDefinition, _ int) string {
|
||||
return serviceDefinition.provider
|
||||
})
|
||||
|
||||
self.log.Errorf("Unknown git service type: '%s'. Expected one of %s", provider, strings.Join(providerNames, ", "))
|
||||
continue
|
||||
}
|
||||
|
||||
serviceDomains = append(serviceDomains, ServiceDomain{
|
||||
gitDomain: gitDomain,
|
||||
webDomain: webDomain,
|
||||
serviceDefinition: serviceDefinition,
|
||||
})
|
||||
|
||||
self.log.Errorf("Unknown git service type: '%s'. Expected one of %s", provider, strings.Join(providerNames, ", "))
|
||||
continue
|
||||
}
|
||||
|
||||
serviceDomains = append(serviceDomains, ServiceDomain{
|
||||
gitDomain: gitDomain,
|
||||
webDomain: webDomain,
|
||||
serviceDefinition: serviceDefinition,
|
||||
})
|
||||
}
|
||||
|
||||
return serviceDomains
|
||||
|
||||
@@ -65,10 +65,6 @@ func (b *Branch) ID() string {
|
||||
return b.RefName()
|
||||
}
|
||||
|
||||
func (b *Branch) URN() string {
|
||||
return "branch-" + b.ID()
|
||||
}
|
||||
|
||||
func (b *Branch) Description() string {
|
||||
return b.RefName()
|
||||
}
|
||||
|
||||
@@ -24,10 +24,6 @@ func (t *Tag) ID() string {
|
||||
return t.RefName()
|
||||
}
|
||||
|
||||
func (t *Tag) URN() string {
|
||||
return "tag-" + t.ID()
|
||||
}
|
||||
|
||||
func (t *Tag) Description() string {
|
||||
return t.Message
|
||||
}
|
||||
|
||||
@@ -74,6 +74,11 @@ func CopyFile(src, dst string) (err error) {
|
||||
return //nolint: nakedret
|
||||
}
|
||||
|
||||
// If I was less lazy
|
||||
// I would actually think
|
||||
// of a way to add some real code
|
||||
// for the sake of an example
|
||||
|
||||
// CopyDir recursively copies a directory tree, attempting to preserve permissions.
|
||||
// Source directory must exist. If destination already exists we'll clobber it.
|
||||
// Symlinks are ignored and skipped.
|
||||
|
||||
@@ -60,7 +60,6 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
||||
suspend: false,
|
||||
},
|
||||
"emacs": standardTerminalEditorPreset("emacs"),
|
||||
"micro": standardTerminalEditorPreset("micro"),
|
||||
"nano": standardTerminalEditorPreset("nano"),
|
||||
"kakoune": standardTerminalEditorPreset("kak"),
|
||||
"helix": {
|
||||
|
||||
+110
-265
@@ -5,255 +5,131 @@ import (
|
||||
)
|
||||
|
||||
type UserConfig struct {
|
||||
// Config relating to the Lazygit UI
|
||||
Gui GuiConfig `yaml:"gui"`
|
||||
// Config relating to git
|
||||
Git GitConfig `yaml:"git"`
|
||||
// Periodic update checks
|
||||
Update UpdateConfig `yaml:"update"`
|
||||
// Background refreshes
|
||||
Refresher RefresherConfig `yaml:"refresher"`
|
||||
// If true, show a confirmation popup before quitting Lazygit
|
||||
ConfirmOnQuit bool `yaml:"confirmOnQuit"`
|
||||
// If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close
|
||||
QuitOnTopLevelReturn bool `yaml:"quitOnTopLevelReturn"`
|
||||
// Keybindings
|
||||
Keybinding KeybindingConfig `yaml:"keybinding"`
|
||||
// Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc
|
||||
OS OSConfig `yaml:"os,omitempty"`
|
||||
// If true, don't display introductory popups upon opening Lazygit.
|
||||
// Lazygit sets this to true upon first runninng the program so that you don't see introductory popups every time you open the program.
|
||||
DisableStartupPopups bool `yaml:"disableStartupPopups"`
|
||||
// User-configured commands that can be invoked from within Lazygit
|
||||
CustomCommands []CustomCommand `yaml:"customCommands"`
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-pull-request-urls
|
||||
Services map[string]string `yaml:"services"`
|
||||
// What to do when opening Lazygit outside of a git repo.
|
||||
// - 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo
|
||||
// - 'create': initialize a new repo
|
||||
// - 'skip': open most recent repo
|
||||
// - 'quit': exit Lazygit
|
||||
NotARepository string `yaml:"notARepository"`
|
||||
// If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.
|
||||
PromptToReturnFromSubprocess bool `yaml:"promptToReturnFromSubprocess"`
|
||||
Gui GuiConfig `yaml:"gui"`
|
||||
Git GitConfig `yaml:"git"`
|
||||
Update UpdateConfig `yaml:"update"`
|
||||
Refresher RefresherConfig `yaml:"refresher"`
|
||||
ConfirmOnQuit bool `yaml:"confirmOnQuit"`
|
||||
QuitOnTopLevelReturn bool `yaml:"quitOnTopLevelReturn"`
|
||||
Keybinding KeybindingConfig `yaml:"keybinding"`
|
||||
// OS determines what defaults are set for opening files and links
|
||||
OS OSConfig `yaml:"os,omitempty"`
|
||||
DisableStartupPopups bool `yaml:"disableStartupPopups"`
|
||||
CustomCommands []CustomCommand `yaml:"customCommands"`
|
||||
Services map[string]string `yaml:"services"`
|
||||
NotARepository string `yaml:"notARepository"`
|
||||
PromptToReturnFromSubprocess bool `yaml:"promptToReturnFromSubprocess"`
|
||||
}
|
||||
|
||||
type RefresherConfig struct {
|
||||
// File/submodule refresh interval in seconds.
|
||||
// Auto-refresh can be disabled via option 'git.autoRefresh'.
|
||||
RefreshInterval int `yaml:"refreshInterval"`
|
||||
// Re-fetch interval in seconds.
|
||||
// Auto-fetch can be disabled via option 'git.autoFetch'.
|
||||
FetchInterval int `yaml:"fetchInterval"`
|
||||
FetchInterval int `yaml:"fetchInterval"`
|
||||
}
|
||||
|
||||
type GuiConfig struct {
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-author-color
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-branch-color
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
// The number of lines you scroll by when scrolling the main window
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
// If true, allow scrolling past the bottom of the content in the main window
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#scroll-off-margin
|
||||
ScrollOffMargin int `yaml:"scrollOffMargin"`
|
||||
// One of: 'margin' (default) | 'jump'
|
||||
ScrollOffBehavior string `yaml:"scrollOffBehavior"`
|
||||
// If true, capture mouse events.
|
||||
// When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
// If true, do not show a warning when discarding changes in the staging view.
|
||||
SkipDiscardChangeWarning bool `yaml:"skipDiscardChangeWarning"`
|
||||
// If true, do not show warning when applying/popping the stash
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
// If true, do not show a warning when attempting to commit without any staged files; instead stage all unstaged files.
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
// If true, do not show a warning when rewording a commit via an external editor
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
// Fraction of the total screen width to use for the left side section. You may want to pick a small number (e.g. 0.2) if you're using a narrow screen, so that you can see more of the main section.
|
||||
// Number from 0 to 1.0.
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
// If true, increase the height of the focused side window; creating an accordion effect.
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
// Sometimes the main window is split in two (e.g. when the selected file has both staged and unstaged changes). This setting controls how the two sections are split.
|
||||
// Options are:
|
||||
// - 'horizontal': split the window horizontally
|
||||
// - 'vertical': split the window vertically
|
||||
// - 'flexible': (default) split the window horizontally if the window is wide enough, otherwise split vertically
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
Language string `yaml:"language"`
|
||||
// Format used when displaying time e.g. commit time.
|
||||
// Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
// Format used when displaying time if the time is less than 24 hours ago.
|
||||
// Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
|
||||
ShortTimeFormat string `yaml:"shortTimeFormat"`
|
||||
// Config relating to colors and styles.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
// Config relating to the commit length indicator
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
// If true, show the '5 of 20' footer at the bottom of list views
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
// If true, display the files in the file views as a tree. If false, display the files as a flat list.
|
||||
// This can be toggled from within Lazygit with the '~' key, but that will not change the default.
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
// If true, show a random tip in the command log when Lazygit starts
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
// If true, show the command log
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
// If true, show the bottom line that contains keybinding info and useful buttons. If false, this line will be hidden except to display a loader for an in-progress action.
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
// If true, show jump-to-window keybindings in window titles.
|
||||
ShowPanelJumps bool `yaml:"showPanelJumps"`
|
||||
// Deprecated: use nerdFontsVersion instead
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
// Nerd fonts version to use.
|
||||
// One of: '2' | '3' | empty string (default)
|
||||
// If empty, do not show icons.
|
||||
NerdFontsVersion string `yaml:"nerdFontsVersion"`
|
||||
// If true, show commit hashes alongside branch names in the branches view.
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
// Height of the command log view
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
// Whether to split the main window when viewing file changes.
|
||||
// One of: 'auto' | 'always'
|
||||
// If 'auto', only split the main window when a file has both staged and unstaged changes
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
// Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).
|
||||
// One of: 'normal' (default) | 'half' | 'full'
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
// Window border style.
|
||||
// One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
||||
Border string `yaml:"border"`
|
||||
// If true, show a seriously epic explosion animation when nuking the working tree.
|
||||
AnimateExplosion bool `yaml:"animateExplosion"`
|
||||
// Whether to stack UI components on top of each other.
|
||||
// One of 'auto' (default) | 'always' | 'never'
|
||||
PortraitMode string `yaml:"portraitMode"`
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
ScrollOffMargin int `yaml:"scrollOffMargin"`
|
||||
ScrollOffBehavior string `yaml:"scrollOffBehavior"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipDiscardChangeWarning bool `yaml:"skipDiscardChangeWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
Language string `yaml:"language"`
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
ShortTimeFormat string `yaml:"shortTimeFormat"`
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
ShowPanelJumps bool `yaml:"showPanelJumps"`
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
NerdFontsVersion string `yaml:"nerdFontsVersion"`
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
Border string `yaml:"border"`
|
||||
AnimateExplosion bool `yaml:"animateExplosion"`
|
||||
}
|
||||
|
||||
type ThemeConfig struct {
|
||||
// Border color of focused window
|
||||
ActiveBorderColor []string `yaml:"activeBorderColor"`
|
||||
// Border color of non-focused windows
|
||||
InactiveBorderColor []string `yaml:"inactiveBorderColor"`
|
||||
// Border color of focused window when searching in that window
|
||||
ActiveBorderColor []string `yaml:"activeBorderColor"`
|
||||
InactiveBorderColor []string `yaml:"inactiveBorderColor"`
|
||||
SearchingActiveBorderColor []string `yaml:"searchingActiveBorderColor"`
|
||||
// Color of keybindings help text in the bottom line
|
||||
OptionsTextColor []string `yaml:"optionsTextColor"`
|
||||
// Background color of selected line.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
|
||||
SelectedLineBgColor []string `yaml:"selectedLineBgColor"`
|
||||
// Background color of selected range
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
|
||||
SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"`
|
||||
// Foreground color of copied commit
|
||||
CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"`
|
||||
// Background color of copied commit
|
||||
CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"`
|
||||
// Foreground color of marked base commit (for rebase)
|
||||
MarkedBaseCommitFgColor []string `yaml:"markedBaseCommitFgColor"`
|
||||
// Background color of marked base commit (for rebase)
|
||||
MarkedBaseCommitBgColor []string `yaml:"markedBaseCommitBgColor"`
|
||||
// Color for file with unstaged changes
|
||||
UnstagedChangesColor []string `yaml:"unstagedChangesColor"`
|
||||
// Default text color
|
||||
DefaultFgColor []string `yaml:"defaultFgColor"`
|
||||
OptionsTextColor []string `yaml:"optionsTextColor"`
|
||||
SelectedLineBgColor []string `yaml:"selectedLineBgColor"`
|
||||
SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"`
|
||||
CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"`
|
||||
CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"`
|
||||
MarkedBaseCommitBgColor []string `yaml:"markedBaseCommitBgColor"`
|
||||
MarkedBaseCommitFgColor []string `yaml:"markedBaseCommitFgColor"`
|
||||
UnstagedChangesColor []string `yaml:"unstagedChangesColor"`
|
||||
DefaultFgColor []string `yaml:"defaultFgColor"`
|
||||
}
|
||||
|
||||
type CommitLengthConfig struct {
|
||||
// If true, show an indicator of commit message length
|
||||
Show bool `yaml:"show"`
|
||||
}
|
||||
|
||||
type GitConfig struct {
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
|
||||
Paging PagingConfig `yaml:"paging"`
|
||||
// Config relating to committing
|
||||
Commit CommitConfig `yaml:"commit"`
|
||||
// Config relating to merging
|
||||
Merging MergingConfig `yaml:"merging"`
|
||||
// list of branches that are considered 'main' branches, used when displaying commits
|
||||
MainBranches []string `yaml:"mainBranches"`
|
||||
// Prefix to use when skipping hooks. E.g. if set to 'WIP', then pre-commit hooks will be skipped when the commit message starts with 'WIP'
|
||||
SkipHookPrefix string `yaml:"skipHookPrefix"`
|
||||
// If true, periodically fetch from remote
|
||||
AutoFetch bool `yaml:"autoFetch"`
|
||||
// If true, periodically refresh files and submodules
|
||||
AutoRefresh bool `yaml:"autoRefresh"`
|
||||
// If true, pass the --all arg to git fetch
|
||||
FetchAll bool `yaml:"fetchAll"`
|
||||
// Command used when displaying the current branch git log in the main window
|
||||
BranchLogCmd string `yaml:"branchLogCmd"`
|
||||
// Command used to display git log of all branches in the main window
|
||||
AllBranchesLogCmd string `yaml:"allBranchesLogCmd"`
|
||||
// If true, do not spawn a separate process when using GPG
|
||||
OverrideGpg bool `yaml:"overrideGpg"`
|
||||
// If true, do not allow force pushes
|
||||
DisableForcePushing bool `yaml:"disableForcePushing"`
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix
|
||||
CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"`
|
||||
// If true, parse emoji strings in commit messages e.g. render :rocket: as 🚀
|
||||
// (This should really be under 'gui', not 'git')
|
||||
ParseEmoji bool `yaml:"parseEmoji"`
|
||||
// Config for showing the log in the commits view
|
||||
Log LogConfig `yaml:"log"`
|
||||
Paging PagingConfig `yaml:"paging"`
|
||||
Commit CommitConfig `yaml:"commit"`
|
||||
Merging MergingConfig `yaml:"merging"`
|
||||
MainBranches []string `yaml:"mainBranches"`
|
||||
SkipHookPrefix string `yaml:"skipHookPrefix"`
|
||||
AutoFetch bool `yaml:"autoFetch"`
|
||||
AutoRefresh bool `yaml:"autoRefresh"`
|
||||
FetchAll bool `yaml:"fetchAll"`
|
||||
BranchLogCmd string `yaml:"branchLogCmd"`
|
||||
AllBranchesLogCmd string `yaml:"allBranchesLogCmd"`
|
||||
OverrideGpg bool `yaml:"overrideGpg"`
|
||||
DisableForcePushing bool `yaml:"disableForcePushing"`
|
||||
CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"`
|
||||
// this should really be under 'gui', not 'git'
|
||||
ParseEmoji bool `yaml:"parseEmoji"`
|
||||
Log LogConfig `yaml:"log"`
|
||||
}
|
||||
|
||||
type PagingConfig struct {
|
||||
// Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never'
|
||||
ColorArg string `yaml:"colorArg"`
|
||||
// e.g.
|
||||
// diff-so-fancy
|
||||
// delta --dark --paging=never
|
||||
// ydiff -p cat -s --wrap --width={{columnWidth}}
|
||||
Pager string `yaml:"pager"`
|
||||
// If true, Lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
|
||||
UseConfig bool `yaml:"useConfig"`
|
||||
// e.g. 'difft --color=always'
|
||||
ColorArg string `yaml:"colorArg"`
|
||||
Pager string `yaml:"pager"`
|
||||
UseConfig bool `yaml:"useConfig"`
|
||||
ExternalDiffCommand string `yaml:"externalDiffCommand"`
|
||||
}
|
||||
|
||||
type CommitConfig struct {
|
||||
// If true, pass '--signoff' flag when committing
|
||||
SignOff bool `yaml:"signOff"`
|
||||
}
|
||||
|
||||
type MergingConfig struct {
|
||||
// If true, run merges in a subprocess so that if a commit message is required, Lazygit will not hang
|
||||
// Only applicable to unix users.
|
||||
ManualCommit bool `yaml:"manualCommit"`
|
||||
// Extra args passed to `git merge`, e.g. --no-ff
|
||||
Args string `yaml:"args"`
|
||||
ManualCommit bool `yaml:"manualCommit"`
|
||||
Args string `yaml:"args"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
// One of: 'date-order' | 'author-date-order' | 'topo-order'
|
||||
// 'topo-order' makes it easier to read the git log graph, but commits may not
|
||||
// appear chronologically. See https://git-scm.com/docs/
|
||||
Order string `yaml:"order"`
|
||||
// This determines whether the git graph is rendered in the commits panel
|
||||
// One of 'always' | 'never' 'when-maximised'
|
||||
ShowGraph string `yaml:"showGraph"`
|
||||
// displays the whole git graph by default in the commits view (equivalent to passing the `--all` argument to `git log`)
|
||||
ShowWholeGraph bool `yaml:"showWholeGraph"`
|
||||
Order string `yaml:"order"` // one of date-order, author-date-order, topo-order
|
||||
ShowGraph string `yaml:"showGraph"` // one of always, never, when-maximised
|
||||
ShowWholeGraph bool `yaml:"showWholeGraph"`
|
||||
}
|
||||
|
||||
type CommitPrefixConfig struct {
|
||||
// pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*"
|
||||
Pattern string `yaml:"pattern"`
|
||||
// Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] "
|
||||
Replace string `yaml:"replace"`
|
||||
}
|
||||
|
||||
type UpdateConfig struct {
|
||||
// One of: 'prompt' (default) | 'background' | 'never'
|
||||
Method string `yaml:"method"`
|
||||
// Period in days between update checks
|
||||
Days int64 `yaml:"days"`
|
||||
Days int64 `yaml:"days"`
|
||||
}
|
||||
|
||||
type KeybindingConfig struct {
|
||||
@@ -491,8 +367,7 @@ type OSConfig struct {
|
||||
// Deprecated: use OpenLink instead.
|
||||
OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
|
||||
|
||||
// CopyToClipboardCmd is the command for copying to clipboard.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard
|
||||
// CopyToClipboardCmd is the command for copying to clipboard
|
||||
CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"`
|
||||
}
|
||||
|
||||
@@ -501,79 +376,50 @@ type CustomCommandAfterHook struct {
|
||||
}
|
||||
|
||||
type CustomCommand struct {
|
||||
// The key to trigger the command. Use a single letter or one of the values from https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md
|
||||
Key string `yaml:"key"`
|
||||
// The context in which to listen for the key
|
||||
Context string `yaml:"context"`
|
||||
// The command to run (using Go template syntax for placeholder values)
|
||||
Command string `yaml:"command"`
|
||||
// If true, run the command in a subprocess (e.g. if the command requires user input)
|
||||
Subprocess bool `yaml:"subprocess"`
|
||||
// A list of prompts that will request user input before running the final command
|
||||
Prompts []CustomCommandPrompt `yaml:"prompts"`
|
||||
// Text to display while waiting for command to finish
|
||||
LoadingText string `yaml:"loadingText"`
|
||||
// Label for the custom command when displayed in the keybindings menu
|
||||
Description string `yaml:"description"`
|
||||
// If true, stream the command's output to the Command Log panel
|
||||
Stream bool `yaml:"stream"`
|
||||
// If true, show the command's output in a popup within Lazygit
|
||||
ShowOutput bool `yaml:"showOutput"`
|
||||
// Actions to take after the command has completed
|
||||
After CustomCommandAfterHook `yaml:"after"`
|
||||
Key string `yaml:"key"`
|
||||
Context string `yaml:"context"`
|
||||
Command string `yaml:"command"`
|
||||
Subprocess bool `yaml:"subprocess"`
|
||||
Prompts []CustomCommandPrompt `yaml:"prompts"`
|
||||
LoadingText string `yaml:"loadingText"`
|
||||
Description string `yaml:"description"`
|
||||
Stream bool `yaml:"stream"`
|
||||
ShowOutput bool `yaml:"showOutput"`
|
||||
After CustomCommandAfterHook `yaml:"after"`
|
||||
}
|
||||
|
||||
type CustomCommandPrompt struct {
|
||||
// One of: 'input' | 'menu' | 'confirm' | 'menuFromCommand'
|
||||
Type string `yaml:"type"`
|
||||
// Used to reference the entered value from within the custom command. E.g. a prompt with `key: 'Branch'` can be referred to as `{{.Form.Branch}}` in the command
|
||||
Key string `yaml:"key"`
|
||||
// The title to display in the popup panel
|
||||
// one of 'input', 'menu', 'confirm', or 'menuFromCommand'
|
||||
Type string `yaml:"type"`
|
||||
Key string `yaml:"key"`
|
||||
Title string `yaml:"title"`
|
||||
|
||||
// The initial value to appear in the text box.
|
||||
// Only for input prompts.
|
||||
InitialValue string `yaml:"initialValue"`
|
||||
// Shows suggestions as the input is entered
|
||||
// Only for input prompts.
|
||||
Suggestions CustomCommandSuggestions `yaml:"suggestions"`
|
||||
// these only apply to input prompts
|
||||
InitialValue string `yaml:"initialValue"`
|
||||
Suggestions CustomCommandSuggestions `yaml:"suggestions"`
|
||||
|
||||
// The message of the confirmation prompt.
|
||||
// Only for confirm prompts.
|
||||
// this only applies to confirm prompts
|
||||
Body string `yaml:"body"`
|
||||
|
||||
// Menu options.
|
||||
// Only for menu prompts.
|
||||
// this only applies to menus
|
||||
Options []CustomCommandMenuOption
|
||||
|
||||
// The command to run to generate menu options
|
||||
// Only for menuFromCommand prompts.
|
||||
Command string `yaml:"command"`
|
||||
// The regexp to run specifying groups which are going to be kept from the command's output.
|
||||
// Only for menuFromCommand prompts.
|
||||
Filter string `yaml:"filter"`
|
||||
// How to format matched groups from the filter to construct a menu item's value.
|
||||
// Only for menuFromCommand prompts.
|
||||
// this only applies to menuFromCommand
|
||||
Command string `yaml:"command"`
|
||||
Filter string `yaml:"filter"`
|
||||
ValueFormat string `yaml:"valueFormat"`
|
||||
// Like valueFormat but for the labels. If `labelFormat` is not specified, `valueFormat` is shown instead.
|
||||
// Only for menuFromCommand prompts.
|
||||
LabelFormat string `yaml:"labelFormat"`
|
||||
}
|
||||
|
||||
type CustomCommandSuggestions struct {
|
||||
// Uses built-in logic to obtain the suggestions. One of 'authors' | 'branches' | 'files' | 'refs' | 'remotes' | 'remoteBranches' | 'tags'
|
||||
Preset string `yaml:"preset"`
|
||||
// Command to run such that each line in the output becomes a suggestion. Mutually exclusive with 'preset' field.
|
||||
Preset string `yaml:"preset"`
|
||||
Command string `yaml:"command"`
|
||||
}
|
||||
|
||||
type CustomCommandMenuOption struct {
|
||||
// The first part of the label
|
||||
Name string `yaml:"name"`
|
||||
// The second part of the label
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
// The value that will be used in the command
|
||||
Value string `yaml:"value"`
|
||||
Value string `yaml:"value"`
|
||||
}
|
||||
|
||||
func GetDefaultConfig() *UserConfig {
|
||||
@@ -622,7 +468,6 @@ func GetDefaultConfig() *UserConfig {
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "rounded",
|
||||
AnimateExplosion: true,
|
||||
PortraitMode: "auto",
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
@@ -691,7 +536,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
NextMatch: "n",
|
||||
PrevMatch: "N",
|
||||
StartSearch: "/",
|
||||
OptionMenu: "<disabled>",
|
||||
OptionMenu: "",
|
||||
OptionMenuAlt1: "?",
|
||||
Select: "<space>",
|
||||
GoInto: "<enter>",
|
||||
|
||||
@@ -27,13 +27,11 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
return presentation.GetBranchListDisplayStrings(
|
||||
viewModel.GetItems(),
|
||||
c.State().GetItemOperation,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().Diffing.Ref,
|
||||
c.Tr,
|
||||
c.UserConfig,
|
||||
c.Model().Worktrees,
|
||||
c.Model().MergedBranches,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,6 @@ func (self *FilteredList[T]) ClearFilter() {
|
||||
self.SetFilter("")
|
||||
}
|
||||
|
||||
func (self *FilteredList[T]) ReApplyFilter() {
|
||||
self.applyFilter()
|
||||
}
|
||||
|
||||
func (self *FilteredList[T]) IsFiltering() bool {
|
||||
return self.filter != ""
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
type RemotesContext struct {
|
||||
*FilteredListViewModel[*models.Remote]
|
||||
*ListContextTrait
|
||||
*SearchHistory
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -27,10 +27,7 @@ func NewTagsContext(
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
return presentation.GetTagListDisplayStrings(
|
||||
viewModel.GetItems(),
|
||||
c.State().GetItemOperation,
|
||||
c.Modes().Diffing.Ref, c.Tr)
|
||||
return presentation.GetTagListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref)
|
||||
}
|
||||
|
||||
return &TagsContext{
|
||||
|
||||
@@ -47,7 +47,6 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
patchBuildingHelper := helpers.NewPatchBuildingHelper(helperCommon)
|
||||
stagingHelper := helpers.NewStagingHelper(helperCommon)
|
||||
mergeConflictsHelper := helpers.NewMergeConflictsHelper(helperCommon)
|
||||
searchHelper := helpers.NewSearchHelper(helperCommon)
|
||||
|
||||
refreshHelper := helpers.NewRefreshHelper(
|
||||
helperCommon,
|
||||
@@ -57,7 +56,6 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
stagingHelper,
|
||||
mergeConflictsHelper,
|
||||
worktreeHelper,
|
||||
searchHelper,
|
||||
)
|
||||
diffHelper := helpers.NewDiffHelper(helperCommon)
|
||||
cherryPickHelper := helpers.NewCherryPickHelper(
|
||||
@@ -114,14 +112,13 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
Confirmation: helpers.NewConfirmationHelper(helperCommon),
|
||||
Mode: modeHelper,
|
||||
AppStatus: appStatusHelper,
|
||||
InlineStatus: helpers.NewInlineStatusHelper(helperCommon),
|
||||
WindowArrangement: helpers.NewWindowArrangementHelper(
|
||||
gui.c,
|
||||
windowHelper,
|
||||
modeHelper,
|
||||
appStatusHelper,
|
||||
),
|
||||
Search: searchHelper,
|
||||
Search: helpers.NewSearchHelper(helperCommon),
|
||||
Worktree: worktreeHelper,
|
||||
SubCommits: helpers.NewSubCommitsHelper(helperCommon, refreshHelper, setSubCommits),
|
||||
}
|
||||
|
||||
@@ -34,10 +34,9 @@ func NewBranchesController(
|
||||
func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
return []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.checkSelected(self.press),
|
||||
GetDisabledReason: self.getDisabledReasonForPress,
|
||||
Description: self.c.Tr.Checkout,
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.checkSelected(self.press),
|
||||
Description: self.c.Tr.Checkout,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.New),
|
||||
@@ -300,18 +299,6 @@ func (self *BranchesController) press(selectedBranch *models.Branch) error {
|
||||
return self.c.Helpers().Refs.CheckoutRef(selectedBranch.Name, types.CheckoutRefOptions{})
|
||||
}
|
||||
|
||||
func (self *BranchesController) getDisabledReasonForPress() string {
|
||||
currentBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||
if currentBranch != nil {
|
||||
op := self.c.State().GetItemOperation(currentBranch)
|
||||
if op == types.ItemOperationFastForwarding || op == types.ItemOperationPulling {
|
||||
return self.c.Tr.CantCheckoutBranchWhilePulling
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *BranchesController) worktreeForBranch(branch *models.Branch) (*models.Worktree, bool) {
|
||||
return git_commands.WorktreeForBranch(branch, self.c.Model().Worktrees)
|
||||
}
|
||||
@@ -576,7 +563,14 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
|
||||
action := self.c.Tr.Actions.FastForwardBranch
|
||||
|
||||
return self.c.WithInlineStatus(branch, types.ItemOperationFastForwarding, context.LOCAL_BRANCHES_CONTEXT_KEY, func(task gocui.Task) error {
|
||||
message := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.FastForwarding,
|
||||
map[string]string{
|
||||
"branch": branch.Name,
|
||||
},
|
||||
)
|
||||
|
||||
return self.c.WithWaitingStatus(message, func(task gocui.Task) error {
|
||||
worktree, ok := self.worktreeForBranch(branch)
|
||||
if ok {
|
||||
self.c.LogAction(action)
|
||||
@@ -596,17 +590,24 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
WorktreeGitDir: worktreeGitDir,
|
||||
},
|
||||
)
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
return err
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
} else {
|
||||
self.c.LogAction(action)
|
||||
|
||||
err := self.c.Git().Sync.FastForward(
|
||||
task, branch.Name, branch.UpstreamRemote, branch.UpstreamBranch,
|
||||
)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.BRANCHES}})
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ type Helpers struct {
|
||||
Confirmation *ConfirmationHelper
|
||||
Mode *ModeHelper
|
||||
AppStatus *AppStatusHelper
|
||||
InlineStatus *InlineStatusHelper
|
||||
WindowArrangement *WindowArrangementHelper
|
||||
Search *SearchHelper
|
||||
Worktree *WorktreeHelper
|
||||
@@ -82,7 +81,6 @@ func NewStubHelpers() *Helpers {
|
||||
Confirmation: &ConfirmationHelper{},
|
||||
Mode: &ModeHelper{},
|
||||
AppStatus: &AppStatusHelper{},
|
||||
InlineStatus: &InlineStatusHelper{},
|
||||
WindowArrangement: &WindowArrangementHelper{},
|
||||
Search: &SearchHelper{},
|
||||
Worktree: &WorktreeHelper{},
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
type InlineStatusHelper struct {
|
||||
c *HelperCommon
|
||||
|
||||
contextsWithInlineStatus map[types.ContextKey]*inlineStatusInfo
|
||||
mutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
func NewInlineStatusHelper(c *HelperCommon) *InlineStatusHelper {
|
||||
return &InlineStatusHelper{
|
||||
c: c,
|
||||
contextsWithInlineStatus: make(map[types.ContextKey]*inlineStatusInfo),
|
||||
mutex: &deadlock.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
type InlineStatusOpts struct {
|
||||
Item types.HasUrn
|
||||
Operation types.ItemOperation
|
||||
ContextKey types.ContextKey
|
||||
}
|
||||
|
||||
type inlineStatusInfo struct {
|
||||
refCount int
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
// A custom task for WithInlineStatus calls; it wraps the original one and
|
||||
// hides the status whenever the task is paused, and shows it again when
|
||||
// continued.
|
||||
type inlineStatusHelperTask struct {
|
||||
gocui.Task
|
||||
|
||||
inlineStatusHelper *InlineStatusHelper
|
||||
opts InlineStatusOpts
|
||||
}
|
||||
|
||||
// poor man's version of explicitly saying that struct X implements interface Y
|
||||
var _ gocui.Task = inlineStatusHelperTask{}
|
||||
|
||||
func (self inlineStatusHelperTask) Pause() {
|
||||
self.inlineStatusHelper.stop(self.opts)
|
||||
self.Task.Pause()
|
||||
|
||||
self.inlineStatusHelper.renderContext(self.opts.ContextKey)
|
||||
}
|
||||
|
||||
func (self inlineStatusHelperTask) Continue() {
|
||||
self.Task.Continue()
|
||||
self.inlineStatusHelper.start(self.opts)
|
||||
}
|
||||
|
||||
func (self *InlineStatusHelper) WithInlineStatus(opts InlineStatusOpts, f func(gocui.Task) error) {
|
||||
self.c.OnWorker(func(task gocui.Task) {
|
||||
self.start(opts)
|
||||
|
||||
err := f(inlineStatusHelperTask{task, self, opts})
|
||||
if err != nil {
|
||||
self.c.OnUIThread(func() error {
|
||||
return self.c.Error(err)
|
||||
})
|
||||
}
|
||||
|
||||
self.stop(opts)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *InlineStatusHelper) start(opts InlineStatusOpts) {
|
||||
self.c.State().SetItemOperation(opts.Item, opts.Operation)
|
||||
|
||||
self.mutex.Lock()
|
||||
defer self.mutex.Unlock()
|
||||
|
||||
info := self.contextsWithInlineStatus[opts.ContextKey]
|
||||
if info == nil {
|
||||
info = &inlineStatusInfo{refCount: 0, stop: make(chan struct{})}
|
||||
self.contextsWithInlineStatus[opts.ContextKey] = info
|
||||
|
||||
go utils.Safe(func() {
|
||||
ticker := time.NewTicker(time.Millisecond * utils.LoaderAnimationInterval)
|
||||
defer ticker.Stop()
|
||||
outer:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
self.renderContext(opts.ContextKey)
|
||||
case <-info.stop:
|
||||
break outer
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
info.refCount++
|
||||
}
|
||||
|
||||
func (self *InlineStatusHelper) stop(opts InlineStatusOpts) {
|
||||
self.mutex.Lock()
|
||||
|
||||
if info := self.contextsWithInlineStatus[opts.ContextKey]; info != nil {
|
||||
info.refCount--
|
||||
if info.refCount <= 0 {
|
||||
info.stop <- struct{}{}
|
||||
delete(self.contextsWithInlineStatus, opts.ContextKey)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self.mutex.Unlock()
|
||||
|
||||
self.c.State().ClearItemOperation(opts.Item)
|
||||
}
|
||||
|
||||
func (self *InlineStatusHelper) renderContext(contextKey types.ContextKey) {
|
||||
self.c.OnUIThread(func() error {
|
||||
_ = self.c.ContextForKey(contextKey).HandleRender()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -28,7 +28,6 @@ type RefreshHelper struct {
|
||||
stagingHelper *StagingHelper
|
||||
mergeConflictsHelper *MergeConflictsHelper
|
||||
worktreeHelper *WorktreeHelper
|
||||
searchHelper *SearchHelper
|
||||
}
|
||||
|
||||
func NewRefreshHelper(
|
||||
@@ -39,7 +38,6 @@ func NewRefreshHelper(
|
||||
stagingHelper *StagingHelper,
|
||||
mergeConflictsHelper *MergeConflictsHelper,
|
||||
worktreeHelper *WorktreeHelper,
|
||||
searchHelper *SearchHelper,
|
||||
) *RefreshHelper {
|
||||
return &RefreshHelper{
|
||||
c: c,
|
||||
@@ -49,7 +47,6 @@ func NewRefreshHelper(
|
||||
stagingHelper: stagingHelper,
|
||||
mergeConflictsHelper: mergeConflictsHelper,
|
||||
worktreeHelper: worktreeHelper,
|
||||
searchHelper: searchHelper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,15 +115,12 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
includeWorktreesWithBranches := false
|
||||
if scopeSet.Includes(types.COMMITS) || scopeSet.Includes(types.BRANCHES) || scopeSet.Includes(types.REFLOG) || scopeSet.Includes(types.BISECT_INFO) {
|
||||
// whenever we change commits, we should update branches because the upstream/downstream
|
||||
// counts can change. Whenever we change branches we should also change commits
|
||||
// e.g. in the case of switching branches.
|
||||
refresh("commits and commit files", self.refreshCommitsAndCommitFiles)
|
||||
|
||||
includeWorktreesWithBranches = scopeSet.Includes(types.WORKTREES)
|
||||
refresh("reflog and branches", func() { self.refreshReflogAndBranches(includeWorktreesWithBranches) })
|
||||
refresh("reflog and branches", self.refreshReflogAndBranches)
|
||||
} else if scopeSet.Includes(types.REBASE_COMMITS) {
|
||||
// the above block handles rebase commits so we only need to call this one
|
||||
// if we've asked specifically for rebase commits and not those other things
|
||||
@@ -163,7 +157,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {
|
||||
refresh("remotes", func() { _ = self.refreshRemotes() })
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.WORKTREES) && !includeWorktreesWithBranches {
|
||||
if scopeSet.Includes(types.WORKTREES) {
|
||||
refresh("worktrees", func() { _ = self.refreshWorktrees() })
|
||||
}
|
||||
|
||||
@@ -248,7 +242,7 @@ func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() {
|
||||
case types.INITIAL:
|
||||
self.c.OnWorker(func(_ gocui.Task) {
|
||||
_ = self.refreshReflogCommits()
|
||||
self.refreshBranches(false)
|
||||
self.refreshBranches()
|
||||
self.c.State().GetRepoState().SetStartupStage(types.COMPLETE)
|
||||
})
|
||||
|
||||
@@ -257,10 +251,10 @@ func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshReflogAndBranches(refreshWorktrees bool) {
|
||||
func (self *RefreshHelper) refreshReflogAndBranches() {
|
||||
self.refreshReflogCommitsConsideringStartup()
|
||||
|
||||
self.refreshBranches(refreshWorktrees)
|
||||
self.refreshBranches()
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshCommitsAndCommitFiles() {
|
||||
@@ -331,7 +325,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||
self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
|
||||
self.c.Model().CheckedOutBranch = checkedOutBranchName
|
||||
|
||||
return self.refreshView(self.c.Contexts().LocalCommits)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
|
||||
@@ -354,7 +348,7 @@ func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
|
||||
self.c.Model().SubCommits = commits
|
||||
self.RefreshAuthors(commits)
|
||||
|
||||
return self.refreshView(self.c.Contexts().SubCommits)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) RefreshAuthors(commits []*models.Commit) {
|
||||
@@ -384,7 +378,7 @@ func (self *RefreshHelper) refreshCommitFilesContext() error {
|
||||
self.c.Model().CommitFiles = files
|
||||
self.c.Contexts().CommitFiles.CommitFileTreeViewModel.SetTree()
|
||||
|
||||
return self.refreshView(self.c.Contexts().CommitFiles)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().CommitFiles)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshRebaseCommits() error {
|
||||
@@ -398,7 +392,7 @@ func (self *RefreshHelper) refreshRebaseCommits() error {
|
||||
self.c.Model().Commits = updatedCommits
|
||||
self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
|
||||
|
||||
return self.refreshView(self.c.Contexts().LocalCommits)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshTags() error {
|
||||
@@ -409,7 +403,7 @@ func (self *RefreshHelper) refreshTags() error {
|
||||
|
||||
self.c.Model().Tags = tags
|
||||
|
||||
return self.refreshView(self.c.Contexts().Tags)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().Tags)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshStateSubmoduleConfigs() error {
|
||||
@@ -425,66 +419,39 @@ func (self *RefreshHelper) refreshStateSubmoduleConfigs() error {
|
||||
|
||||
// self.refreshStatus is called at the end of this because that's when we can
|
||||
// be sure there is a State.Model.Branches array to pick the current branch from
|
||||
func (self *RefreshHelper) refreshBranches(refreshWorktrees bool) {
|
||||
func (self *RefreshHelper) refreshBranches() {
|
||||
self.c.Mutexes().RefreshingBranchesMutex.Lock()
|
||||
defer self.c.Mutexes().RefreshingBranchesMutex.Unlock()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
reflogCommits := self.c.Model().FilteredReflogCommits
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
// in filter mode we filter our reflog commits to just those containing the path
|
||||
// however we need all the reflog entries to populate the recencies of our branches
|
||||
// which allows us to order them correctly. So if we're filtering we'll just
|
||||
// manually load all the reflog commits here
|
||||
var err error
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
|
||||
reflogCommits := self.c.Model().FilteredReflogCommits
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
// in filter mode we filter our reflog commits to just those containing the path
|
||||
// however we need all the reflog entries to populate the recencies of our branches
|
||||
// which allows us to order them correctly. So if we're filtering we'll just
|
||||
// manually load all the reflog commits here
|
||||
var err error
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.Model().Branches = branches
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
mergedBranches := self.c.Git().Loaders.MergedBranchLoader.Load()
|
||||
|
||||
self.c.Model().MergedBranches = mergedBranches
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if refreshWorktrees {
|
||||
self.loadWorktrees()
|
||||
if err := self.refreshView(self.c.Contexts().Worktrees); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.refreshView(self.c.Contexts().Branches); err != nil {
|
||||
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.Model().Branches = branches
|
||||
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().Branches); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
// Need to re-render the commits view because the visualization of local
|
||||
// branch heads might have changed
|
||||
self.c.Mutexes().LocalCommitsMutex.Lock()
|
||||
if err := self.c.Contexts().LocalCommits.HandleRender(); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
self.c.Mutexes().LocalCommitsMutex.Unlock()
|
||||
|
||||
self.refreshStatus()
|
||||
}
|
||||
@@ -506,11 +473,11 @@ func (self *RefreshHelper) refreshFilesAndSubmodules() error {
|
||||
}
|
||||
|
||||
self.c.OnUIThread(func() error {
|
||||
if err := self.refreshView(self.c.Contexts().Submodules); err != nil {
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().Submodules); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
if err := self.refreshView(self.c.Contexts().Files); err != nil {
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().Files); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
@@ -632,7 +599,7 @@ func (self *RefreshHelper) refreshReflogCommits() error {
|
||||
model.FilteredReflogCommits = model.ReflogCommits
|
||||
}
|
||||
|
||||
return self.refreshView(self.c.Contexts().ReflogCommits)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().ReflogCommits)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshRemotes() error {
|
||||
@@ -656,44 +623,41 @@ func (self *RefreshHelper) refreshRemotes() error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.refreshView(self.c.Contexts().Remotes); err != nil {
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().Remotes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.refreshView(self.c.Contexts().RemoteBranches); err != nil {
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().RemoteBranches); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) loadWorktrees() {
|
||||
func (self *RefreshHelper) refreshWorktrees() error {
|
||||
worktrees, err := self.c.Git().Loaders.Worktrees.GetWorktrees()
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
self.c.Model().Worktrees = []*models.Worktree{}
|
||||
return nil
|
||||
}
|
||||
|
||||
self.c.Model().Worktrees = worktrees
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshWorktrees() error {
|
||||
self.loadWorktrees()
|
||||
|
||||
// need to refresh branches because the branches view shows worktrees against
|
||||
// branches
|
||||
if err := self.refreshView(self.c.Contexts().Branches); err != nil {
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().Branches); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.refreshView(self.c.Contexts().Worktrees)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().Worktrees)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshStashEntries() error {
|
||||
self.c.Model().StashEntries = self.c.Git().Loaders.StashLoader.
|
||||
GetStashEntries(self.c.Modes().Filtering.GetPath())
|
||||
|
||||
return self.refreshView(self.c.Contexts().Stash)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().Stash)
|
||||
}
|
||||
|
||||
// never call this on its own, it should only be called from within refreshCommits()
|
||||
@@ -712,7 +676,7 @@ func (self *RefreshHelper) refreshStatus() {
|
||||
|
||||
repoName := self.c.Git().RepoPaths.RepoName()
|
||||
|
||||
status := presentation.FormatStatus(repoName, currentBranch, types.ItemOperationNone, linkedWorktreeName, workingTreeState, self.c.Tr)
|
||||
status := presentation.FormatStatus(repoName, currentBranch, linkedWorktreeName, workingTreeState, self.c.Tr)
|
||||
|
||||
self.c.SetViewContent(self.c.Views().Status, status)
|
||||
}
|
||||
@@ -732,8 +696,3 @@ func (self *RefreshHelper) refForLog() string {
|
||||
|
||||
return bisectInfo.GetStartSha()
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshView(context types.Context) error {
|
||||
self.searchHelper.ReApplyFilter(context)
|
||||
return self.c.PostRefreshUpdate(context)
|
||||
}
|
||||
|
||||
@@ -173,6 +173,11 @@ func (self *ReposHelper) DispatchSwitchTo(path string, errMsg string, contextKey
|
||||
return err
|
||||
}
|
||||
|
||||
// these two mutexes are used by our background goroutines (triggered via `self.goEvery`. We don't want to
|
||||
// switch to a repo while one of these goroutines is in the process of updating something
|
||||
self.c.Mutexes().SyncMutex.Lock()
|
||||
defer self.c.Mutexes().SyncMutex.Unlock()
|
||||
|
||||
self.c.Mutexes().RefreshingFilesMutex.Lock()
|
||||
defer self.c.Mutexes().RefreshingFilesMutex.Unlock()
|
||||
|
||||
|
||||
@@ -227,18 +227,6 @@ func (self *SearchHelper) OnPromptContentChanged(searchString string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SearchHelper) ReApplyFilter(context types.Context) {
|
||||
state := self.searchState()
|
||||
if context == state.Context {
|
||||
filterableContext, ok := context.(types.IFilterableContext)
|
||||
if ok {
|
||||
filterableContext.SetSelectedLineIdx(0)
|
||||
_ = filterableContext.GetView().SetOriginY(0)
|
||||
filterableContext.ReApplyFilter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SearchHelper) RenderSearchStatus(c types.Context) {
|
||||
if c.GetKey() == context.SEARCH_CONTEXT_KEY {
|
||||
return
|
||||
|
||||
@@ -34,24 +34,14 @@ func NewWindowArrangementHelper(
|
||||
|
||||
const INFO_SECTION_PADDING = " "
|
||||
|
||||
func (self *WindowArrangementHelper) shouldUsePortraitMode(width, height int) bool {
|
||||
switch self.c.UserConfig.Gui.PortraitMode {
|
||||
case "never":
|
||||
return false
|
||||
case "always":
|
||||
return true
|
||||
default: // "auto" or any garbage values in PortraitMode value
|
||||
return width <= 84 && height > 45
|
||||
}
|
||||
}
|
||||
|
||||
func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
|
||||
width, height := self.c.GocuiGui().Size()
|
||||
|
||||
sideSectionWeight, mainSectionWeight := self.getMidSectionWeights()
|
||||
|
||||
sidePanelsDirection := boxlayout.COLUMN
|
||||
if self.shouldUsePortraitMode(width, height) {
|
||||
portraitMode := width <= 84 && height > 45
|
||||
if portraitMode {
|
||||
sidePanelsDirection = boxlayout.ROW
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ func (self *StatusController) onClick() error {
|
||||
}
|
||||
|
||||
cx, _ := self.c.Views().Status.Cursor()
|
||||
upstreamStatus := presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr)
|
||||
upstreamStatus := presentation.BranchStatus(currentBranch, self.c.Tr)
|
||||
repoName := self.c.Git().RepoPaths.RepoName()
|
||||
workingTreeState := self.c.Git().Status.WorkingTreeState()
|
||||
switch workingTreeState {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
@@ -31,16 +30,14 @@ func NewSyncController(
|
||||
func (self *SyncController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Push),
|
||||
Handler: opts.Guards.NoPopupPanel(self.HandlePush),
|
||||
GetDisabledReason: self.getDisabledReasonForPushOrPull,
|
||||
Description: self.c.Tr.Push,
|
||||
Key: opts.GetKey(opts.Config.Universal.Push),
|
||||
Handler: opts.Guards.NoPopupPanel(self.HandlePush),
|
||||
Description: self.c.Tr.Push,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Pull),
|
||||
Handler: opts.Guards.NoPopupPanel(self.HandlePull),
|
||||
GetDisabledReason: self.getDisabledReasonForPushOrPull,
|
||||
Description: self.c.Tr.Pull,
|
||||
Key: opts.GetKey(opts.Config.Universal.Pull),
|
||||
Handler: opts.Guards.NoPopupPanel(self.HandlePull),
|
||||
Description: self.c.Tr.Pull,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -59,18 +56,6 @@ func (self *SyncController) HandlePull() error {
|
||||
return self.branchCheckedOut(self.pull)()
|
||||
}
|
||||
|
||||
func (self *SyncController) getDisabledReasonForPushOrPull() string {
|
||||
currentBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||
if currentBranch != nil {
|
||||
op := self.c.State().GetItemOperation(currentBranch)
|
||||
if op != types.ItemOperationNone {
|
||||
return self.c.Tr.CantPullOrPushSameBranchTwice
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SyncController) branchCheckedOut(f func(*models.Branch) error) func() error {
|
||||
return func() error {
|
||||
currentBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||
@@ -88,13 +73,13 @@ func (self *SyncController) push(currentBranch *models.Branch) error {
|
||||
if currentBranch.IsTrackingRemote() {
|
||||
opts := pushOpts{}
|
||||
if currentBranch.HasCommitsToPull() {
|
||||
return self.requestToForcePush(currentBranch, opts)
|
||||
return self.requestToForcePush(opts)
|
||||
} else {
|
||||
return self.pushAux(currentBranch, opts)
|
||||
return self.pushAux(opts)
|
||||
}
|
||||
} else {
|
||||
if self.c.Git().Config.GetPushToCurrent() {
|
||||
return self.pushAux(currentBranch, pushOpts{setUpstream: true})
|
||||
return self.pushAux(pushOpts{setUpstream: true})
|
||||
} else {
|
||||
return self.c.Helpers().Upstream.PromptForUpstreamWithInitialContent(currentBranch, func(upstream string) error {
|
||||
upstreamRemote, upstreamBranch, err := self.c.Helpers().Upstream.ParseUpstream(upstream)
|
||||
@@ -102,7 +87,7 @@ func (self *SyncController) push(currentBranch *models.Branch) error {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.pushAux(currentBranch, pushOpts{
|
||||
return self.pushAux(pushOpts{
|
||||
setUpstream: true,
|
||||
upstreamRemote: upstreamRemote,
|
||||
upstreamBranch: upstreamBranch,
|
||||
@@ -122,11 +107,11 @@ func (self *SyncController) pull(currentBranch *models.Branch) error {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.PullAux(currentBranch, PullFilesOptions{Action: action})
|
||||
return self.PullAux(PullFilesOptions{Action: action})
|
||||
})
|
||||
}
|
||||
|
||||
return self.PullAux(currentBranch, PullFilesOptions{Action: action})
|
||||
return self.PullAux(PullFilesOptions{Action: action})
|
||||
}
|
||||
|
||||
func (self *SyncController) setCurrentBranchUpstream(upstream string) error {
|
||||
@@ -154,8 +139,8 @@ type PullFilesOptions struct {
|
||||
Action string
|
||||
}
|
||||
|
||||
func (self *SyncController) PullAux(currentBranch *models.Branch, opts PullFilesOptions) error {
|
||||
return self.c.WithInlineStatus(currentBranch, types.ItemOperationPulling, context.LOCAL_BRANCHES_CONTEXT_KEY, func(task gocui.Task) error {
|
||||
func (self *SyncController) PullAux(opts PullFilesOptions) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.PullingStatus, func(task gocui.Task) error {
|
||||
return self.pullWithLock(task, opts)
|
||||
})
|
||||
}
|
||||
@@ -182,8 +167,8 @@ type pushOpts struct {
|
||||
setUpstream bool
|
||||
}
|
||||
|
||||
func (self *SyncController) pushAux(currentBranch *models.Branch, opts pushOpts) error {
|
||||
return self.c.WithInlineStatus(currentBranch, types.ItemOperationPushing, context.LOCAL_BRANCHES_CONTEXT_KEY, func(task gocui.Task) error {
|
||||
func (self *SyncController) pushAux(opts pushOpts) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.PushingStatus, func(task gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.Push)
|
||||
err := self.c.Git().Sync.Push(
|
||||
task,
|
||||
@@ -207,18 +192,18 @@ func (self *SyncController) pushAux(currentBranch *models.Branch, opts pushOpts)
|
||||
newOpts := opts
|
||||
newOpts.force = true
|
||||
|
||||
return self.pushAux(currentBranch, newOpts)
|
||||
return self.pushAux(newOpts)
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SyncController) requestToForcePush(currentBranch *models.Branch, opts pushOpts) error {
|
||||
func (self *SyncController) requestToForcePush(opts pushOpts) error {
|
||||
forcePushDisabled := self.c.UserConfig.Git.DisableForcePushing
|
||||
if forcePushDisabled {
|
||||
return self.c.ErrorMsg(self.c.Tr.ForcePushDisabled)
|
||||
@@ -229,7 +214,7 @@ func (self *SyncController) requestToForcePush(currentBranch *models.Branch, opt
|
||||
Prompt: self.forcePushPrompt(),
|
||||
HandleConfirm: func() error {
|
||||
opts.force = true
|
||||
return self.pushAux(currentBranch, opts)
|
||||
return self.pushAux(opts)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -92,9 +92,10 @@ func (self *TagsController) checkout(tag *models.Tag) error {
|
||||
func (self *TagsController) localDelete(tag *models.Tag) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DeleteLocalTag)
|
||||
err := self.c.Git().Tag.LocalDelete(tag.Name)
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS}})
|
||||
return err
|
||||
if err := self.c.Git().Tag.LocalDelete(tag.Name); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS}})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -129,10 +130,10 @@ func (self *TagsController) remoteDelete(tag *models.Tag) error {
|
||||
Title: confirmTitle,
|
||||
Prompt: confirmPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithInlineStatus(tag, types.ItemOperationDeleting, context.TAGS_CONTEXT_KEY, func(task gocui.Task) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(t gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DeleteRemoteTag)
|
||||
if err := self.c.Git().Remote.DeleteRemoteTag(task, upstream, tag.Name); err != nil {
|
||||
return err
|
||||
if err := self.c.Git().Remote.DeleteRemoteTag(t, upstream, tag.Name); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.c.Toast(self.c.Tr.RemoteTagDeletedMessage)
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS}})
|
||||
@@ -188,17 +189,14 @@ func (self *TagsController) push(tag *models.Tag) error {
|
||||
InitialContent: "origin",
|
||||
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRemoteSuggestionsFunc(),
|
||||
HandleConfirm: func(response string) error {
|
||||
return self.c.WithInlineStatus(tag, types.ItemOperationPushing, context.TAGS_CONTEXT_KEY, func(task gocui.Task) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.PushingTagStatus, func(task gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.PushTag)
|
||||
err := self.c.Git().Tag.Push(task, response, tag.Name)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
// Render again to remove the inline status:
|
||||
self.c.OnUIThread(func() error {
|
||||
_ = self.c.Contexts().Tags.HandleRender()
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
return nil
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
+3
-36
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||
@@ -111,12 +110,6 @@ type Gui struct {
|
||||
// lazygit was opened in, or if we'll retain the one we're currently in.
|
||||
RetainOriginalDir bool
|
||||
|
||||
// stores long-running operations associated with items (e.g. when a branch
|
||||
// is being pushed). At the moment the rule is to use an item operation when
|
||||
// we need to talk to the remote.
|
||||
itemOperations map[string]types.ItemOperation
|
||||
itemOperationsMutex *deadlock.Mutex
|
||||
|
||||
PrevLayout PrevLayout
|
||||
|
||||
// this is the initial dir we are in upon opening lazygit. We hold onto this
|
||||
@@ -187,27 +180,6 @@ func (self *StateAccessor) SetRetainOriginalDir(value bool) {
|
||||
self.gui.RetainOriginalDir = value
|
||||
}
|
||||
|
||||
func (self *StateAccessor) GetItemOperation(item types.HasUrn) types.ItemOperation {
|
||||
self.gui.itemOperationsMutex.Lock()
|
||||
defer self.gui.itemOperationsMutex.Unlock()
|
||||
|
||||
return self.gui.itemOperations[item.URN()]
|
||||
}
|
||||
|
||||
func (self *StateAccessor) SetItemOperation(item types.HasUrn, operation types.ItemOperation) {
|
||||
self.gui.itemOperationsMutex.Lock()
|
||||
defer self.gui.itemOperationsMutex.Unlock()
|
||||
|
||||
self.gui.itemOperations[item.URN()] = operation
|
||||
}
|
||||
|
||||
func (self *StateAccessor) ClearItemOperation(item types.HasUrn) {
|
||||
self.gui.itemOperationsMutex.Lock()
|
||||
defer self.gui.itemOperationsMutex.Unlock()
|
||||
|
||||
delete(self.gui.itemOperations, item.URN())
|
||||
}
|
||||
|
||||
// we keep track of some stuff from one render to the next to see if certain
|
||||
// things have changed
|
||||
type PrevLayout struct {
|
||||
@@ -301,6 +273,7 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context
|
||||
gui.gitVersion,
|
||||
gui.os,
|
||||
git_config.NewStdCachedGitConfig(gui.Log),
|
||||
gui.Mutexes.SyncMutex,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -385,7 +358,6 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||
BisectInfo: git_commands.NewNullBisectInfo(),
|
||||
FilesTrie: patricia.NewTrie(),
|
||||
Authors: map[string]*models.Author{},
|
||||
MergedBranches: set.New[string](),
|
||||
},
|
||||
Modes: &types.Modes{
|
||||
Filtering: filtering.New(startArgs.FilterPath),
|
||||
@@ -491,6 +463,7 @@ func NewGui(
|
||||
RefreshingFilesMutex: &deadlock.Mutex{},
|
||||
RefreshingBranchesMutex: &deadlock.Mutex{},
|
||||
RefreshingStatusMutex: &deadlock.Mutex{},
|
||||
SyncMutex: &deadlock.Mutex{},
|
||||
LocalCommitsMutex: &deadlock.Mutex{},
|
||||
SubCommitsMutex: &deadlock.Mutex{},
|
||||
AuthorsMutex: &deadlock.Mutex{},
|
||||
@@ -500,9 +473,6 @@ func NewGui(
|
||||
},
|
||||
InitialDir: initialDir,
|
||||
afterLayoutFuncs: make(chan func() error, 1000),
|
||||
|
||||
itemOperations: make(map[string]types.ItemOperation),
|
||||
itemOperationsMutex: &deadlock.Mutex{},
|
||||
}
|
||||
|
||||
gui.PopupHandler = popup.NewPopupHandler(
|
||||
@@ -653,10 +623,7 @@ func (gui *Gui) Run(startArgs appTypes.StartArgs) error {
|
||||
deadlock.Opts.LogBuf = utils.NewOnceWriter(os.Stderr, func() {
|
||||
gui.g.Close()
|
||||
})
|
||||
// disable deadlock reporting if we're not running in debug mode, or if
|
||||
// we're debugging an integration test. In this latter case, stopping at
|
||||
// breakpoints and stepping through code can easily take more than 30s.
|
||||
deadlock.Opts.Disable = !gui.Debug || os.Getenv(components.WAIT_FOR_DEBUGGER_ENV_VAR) == ""
|
||||
deadlock.Opts.Disable = !gui.Debug
|
||||
|
||||
if err := gui.Config.ReloadUserConfig(); err != nil {
|
||||
return nil
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -200,8 +199,3 @@ func (self *guiCommon) RunningIntegrationTest() bool {
|
||||
func (self *guiCommon) InDemo() bool {
|
||||
return self.gui.integrationTest != nil && self.gui.integrationTest.IsDemo()
|
||||
}
|
||||
|
||||
func (self *guiCommon) WithInlineStatus(item types.HasUrn, operation types.ItemOperation, contextKey types.ContextKey, f func(gocui.Task) error) error {
|
||||
self.gui.helpers.InlineStatus.WithInlineStatus(helpers.InlineStatusOpts{Item: item, Operation: operation, ContextKey: contextKey}, f)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -100,9 +100,7 @@ func LabelFromKey(key types.Key) string {
|
||||
|
||||
func GetKey(key string) types.Key {
|
||||
runeCount := utf8.RuneCountInString(key)
|
||||
if key == "<disabled>" {
|
||||
return nil
|
||||
} else if runeCount > 1 {
|
||||
if runeCount > 1 {
|
||||
binding, ok := keyByLabel[strings.ToLower(key)]
|
||||
if !ok {
|
||||
log.Fatalf("Unrecognized key %s for keybinding. For permitted values see %s", strings.ToLower(key), constants.Links.Docs.CustomKeybindings)
|
||||
|
||||
@@ -4,13 +4,11 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -21,30 +19,26 @@ var branchPrefixColorCache = make(map[string]style.TextStyle)
|
||||
|
||||
func GetBranchListDisplayStrings(
|
||||
branches []*models.Branch,
|
||||
getItemOperation func(item types.HasUrn) types.ItemOperation,
|
||||
fullDescription bool,
|
||||
diffName string,
|
||||
tr *i18n.TranslationSet,
|
||||
userConfig *config.UserConfig,
|
||||
worktrees []*models.Worktree,
|
||||
mergedBranches *set.Set[string],
|
||||
) [][]string {
|
||||
return lo.Map(branches, func(branch *models.Branch, _ int) []string {
|
||||
diffed := branch.Name == diffName
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, tr, userConfig, worktrees, mergedBranches)
|
||||
return getBranchDisplayStrings(branch, fullDescription, diffed, tr, userConfig, worktrees)
|
||||
})
|
||||
}
|
||||
|
||||
// getBranchDisplayStrings returns the display string of branch
|
||||
func getBranchDisplayStrings(
|
||||
b *models.Branch,
|
||||
itemOperation types.ItemOperation,
|
||||
fullDescription bool,
|
||||
diffed bool,
|
||||
tr *i18n.TranslationSet,
|
||||
userConfig *config.UserConfig,
|
||||
worktrees []*models.Worktree,
|
||||
mergedBranches *set.Set[string],
|
||||
) []string {
|
||||
displayName := b.Name
|
||||
if b.DisplayName != "" {
|
||||
@@ -57,7 +51,7 @@ func getBranchDisplayStrings(
|
||||
}
|
||||
|
||||
coloredName := nameTextStyle.Sprint(displayName)
|
||||
branchStatus := utils.WithPadding(ColoredBranchStatus(b, itemOperation, tr), 2, utils.AlignLeft)
|
||||
branchStatus := utils.WithPadding(ColoredBranchStatus(b, tr), 2, utils.AlignLeft)
|
||||
if git_commands.CheckedOutByOtherWorktree(b, worktrees) {
|
||||
worktreeIcon := lo.Ternary(icons.IsIconEnabled(), icons.LINKED_WORKTREE_ICON, fmt.Sprintf("(%s)", tr.LcWorktree))
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, style.FgDefault.Sprint(worktreeIcon))
|
||||
@@ -77,15 +71,7 @@ func getBranchDisplayStrings(
|
||||
}
|
||||
|
||||
if fullDescription || userConfig.Gui.ShowBranchCommitHash {
|
||||
var commitStyle style.TextStyle
|
||||
if b.HasCommitsToPush() {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusUnpushed)
|
||||
} else if mergedBranches.Includes(b.FullRefName()) {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusMerged)
|
||||
} else {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusPushed)
|
||||
}
|
||||
res = append(res, commitStyle.Sprint(utils.ShortSha(b.CommitHash)))
|
||||
res = append(res, utils.ShortSha(b.CommitHash))
|
||||
}
|
||||
|
||||
res = append(res, coloredName)
|
||||
@@ -123,11 +109,9 @@ func GetBranchTextStyle(name string) style.TextStyle {
|
||||
}
|
||||
}
|
||||
|
||||
func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet) string {
|
||||
func ColoredBranchStatus(branch *models.Branch, tr *i18n.TranslationSet) string {
|
||||
colour := style.FgYellow
|
||||
if itemOperation != types.ItemOperationNone {
|
||||
colour = style.FgCyan
|
||||
} else if branch.UpstreamGone {
|
||||
if branch.UpstreamGone {
|
||||
colour = style.FgRed
|
||||
} else if branch.MatchesUpstream() {
|
||||
colour = style.FgGreen
|
||||
@@ -135,15 +119,10 @@ func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperatio
|
||||
colour = style.FgMagenta
|
||||
}
|
||||
|
||||
return colour.Sprint(BranchStatus(branch, itemOperation, tr))
|
||||
return colour.Sprint(BranchStatus(branch, tr))
|
||||
}
|
||||
|
||||
func BranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet) string {
|
||||
itemOperationStr := itemOperationToString(itemOperation, tr)
|
||||
if itemOperationStr != "" {
|
||||
return itemOperationStr + " " + utils.Loader()
|
||||
}
|
||||
|
||||
func BranchStatus(branch *models.Branch, tr *i18n.TranslationSet) string {
|
||||
if !branch.IsTrackingRemote() {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -413,8 +413,20 @@ func getShaColor(
|
||||
}
|
||||
|
||||
diffed := commit.Sha != "" && commit.Sha == diffName
|
||||
|
||||
shaColor := getShaStyleFromStatus(commit.Status)
|
||||
shaColor := theme.DefaultTextColor
|
||||
switch commit.Status {
|
||||
case models.StatusUnpushed:
|
||||
shaColor = style.FgRed
|
||||
case models.StatusPushed:
|
||||
shaColor = style.FgYellow
|
||||
case models.StatusMerged:
|
||||
shaColor = style.FgGreen
|
||||
case models.StatusRebasing:
|
||||
shaColor = style.FgBlue
|
||||
case models.StatusReflog:
|
||||
shaColor = style.FgBlue
|
||||
default:
|
||||
}
|
||||
|
||||
if diffed {
|
||||
shaColor = theme.DiffTerminalColor
|
||||
@@ -427,23 +439,6 @@ func getShaColor(
|
||||
return shaColor
|
||||
}
|
||||
|
||||
func getShaStyleFromStatus(status models.CommitStatus) style.TextStyle {
|
||||
switch status {
|
||||
case models.StatusUnpushed:
|
||||
return style.FgRed
|
||||
case models.StatusPushed:
|
||||
return style.FgYellow
|
||||
case models.StatusMerged:
|
||||
return style.FgGreen
|
||||
case models.StatusRebasing:
|
||||
return style.FgBlue
|
||||
case models.StatusReflog:
|
||||
return style.FgBlue
|
||||
default:
|
||||
return theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
|
||||
func actionColorMap(action todo.TodoCommand) style.TextStyle {
|
||||
switch action {
|
||||
case todo.Pick:
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
)
|
||||
|
||||
func itemOperationToString(itemOperation types.ItemOperation, tr *i18n.TranslationSet) string {
|
||||
switch itemOperation {
|
||||
case types.ItemOperationNone:
|
||||
return ""
|
||||
case types.ItemOperationPushing:
|
||||
return tr.PushingStatus
|
||||
case types.ItemOperationPulling:
|
||||
return tr.PullingStatus
|
||||
case types.ItemOperationFastForwarding:
|
||||
return tr.FastForwarding
|
||||
case types.ItemOperationDeleting:
|
||||
return tr.DeletingStatus
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -7,15 +7,14 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
)
|
||||
|
||||
func FormatStatus(repoName string, currentBranch *models.Branch, itemOperation types.ItemOperation, linkedWorktreeName string, workingTreeState enums.RebaseMode, tr *i18n.TranslationSet) string {
|
||||
func FormatStatus(repoName string, currentBranch *models.Branch, linkedWorktreeName string, workingTreeState enums.RebaseMode, tr *i18n.TranslationSet) string {
|
||||
status := ""
|
||||
|
||||
if currentBranch.IsRealBranch() {
|
||||
status += ColoredBranchStatus(currentBranch, itemOperation, tr) + " "
|
||||
status += ColoredBranchStatus(currentBranch, tr) + " "
|
||||
}
|
||||
|
||||
if workingTreeState != enums.REBASE_MODE_NONE {
|
||||
|
||||
@@ -4,27 +4,19 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func GetTagListDisplayStrings(
|
||||
tags []*models.Tag,
|
||||
getItemOperation func(item types.HasUrn) types.ItemOperation,
|
||||
diffName string,
|
||||
tr *i18n.TranslationSet,
|
||||
) [][]string {
|
||||
func GetTagListDisplayStrings(tags []*models.Tag, diffName string) [][]string {
|
||||
return lo.Map(tags, func(tag *models.Tag, _ int) []string {
|
||||
diffed := tag.Name == diffName
|
||||
return getTagDisplayStrings(tag, getItemOperation(tag), diffed, tr)
|
||||
return getTagDisplayStrings(tag, diffed)
|
||||
})
|
||||
}
|
||||
|
||||
// getTagDisplayStrings returns the display string of branch
|
||||
func getTagDisplayStrings(t *models.Tag, itemOperation types.ItemOperation, diffed bool, tr *i18n.TranslationSet) []string {
|
||||
func getTagDisplayStrings(t *models.Tag, diffed bool) []string {
|
||||
textStyle := theme.DefaultTextColor
|
||||
if diffed {
|
||||
textStyle = theme.DiffTerminalColor
|
||||
@@ -34,11 +26,6 @@ func getTagDisplayStrings(t *models.Tag, itemOperation types.ItemOperation, diff
|
||||
res = append(res, textStyle.Sprint(icons.IconForTag(t)))
|
||||
}
|
||||
descriptionColor := style.FgYellow
|
||||
descriptionStr := descriptionColor.Sprint(t.Description())
|
||||
itemOperationStr := itemOperationToString(itemOperation, tr)
|
||||
if itemOperationStr != "" {
|
||||
descriptionStr = style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader()) + " " + descriptionStr
|
||||
}
|
||||
res = append(res, textStyle.Sprint(t.Name), descriptionStr)
|
||||
res = append(res, textStyle.Sprint(t.Name), descriptionColor.Sprint(t.Description()))
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -45,12 +45,10 @@ func (gui *Gui) handleTestMode() {
|
||||
log.Fatal("gocui should have already exited")
|
||||
}()
|
||||
|
||||
if os.Getenv(components.WAIT_FOR_DEBUGGER_ENV_VAR) == "" {
|
||||
go utils.Safe(func() {
|
||||
time.Sleep(time.Second * 40)
|
||||
log.Fatal("40 seconds is up, lazygit recording took too long to complete")
|
||||
})
|
||||
}
|
||||
go utils.Safe(func() {
|
||||
time.Sleep(time.Second * 40)
|
||||
log.Fatal("40 seconds is up, lazygit recording took too long to complete")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-39
@@ -1,7 +1,6 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -88,12 +87,6 @@ type IGuiCommon interface {
|
||||
// resized, if in accordion mode.
|
||||
AfterLayout(f func() error)
|
||||
|
||||
// Wraps a function, attaching the given operation to the given item while
|
||||
// the function is executing, and also causes the given context to be
|
||||
// redrawn periodically. This allows the operation to be visualized with a
|
||||
// spinning loader animation (e.g. when a branch is being pushed).
|
||||
WithInlineStatus(item HasUrn, operation ItemOperation, contextKey ContextKey, f func(gocui.Task) error) error
|
||||
|
||||
// returns the gocui Gui struct. There is a good chance you don't actually want to use
|
||||
// this struct and instead want to use another method above
|
||||
GocuiGui() *gocui.Gui
|
||||
@@ -224,17 +217,15 @@ type MenuItem struct {
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
// set of branch names of the form 'refs/heads/mybranch'
|
||||
MergedBranches *set.Set[string]
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
|
||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||
// when in filtering mode we only include the ones that match the given path
|
||||
@@ -265,6 +256,7 @@ type Mutexes struct {
|
||||
RefreshingFilesMutex *deadlock.Mutex
|
||||
RefreshingBranchesMutex *deadlock.Mutex
|
||||
RefreshingStatusMutex *deadlock.Mutex
|
||||
SyncMutex *deadlock.Mutex
|
||||
LocalCommitsMutex *deadlock.Mutex
|
||||
SubCommitsMutex *deadlock.Mutex
|
||||
AuthorsMutex *deadlock.Mutex
|
||||
@@ -273,24 +265,6 @@ type Mutexes struct {
|
||||
PtyMutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
// A long-running operation associated with an item. For example, we'll show
|
||||
// that a branch is being pushed from so that there's visual feedback about
|
||||
// what's happening and so that you can see multiple branches' concurrent
|
||||
// operations
|
||||
type ItemOperation int
|
||||
|
||||
const (
|
||||
ItemOperationNone ItemOperation = iota
|
||||
ItemOperationPushing
|
||||
ItemOperationPulling
|
||||
ItemOperationFastForwarding
|
||||
ItemOperationDeleting
|
||||
)
|
||||
|
||||
type HasUrn interface {
|
||||
URN() string
|
||||
}
|
||||
|
||||
type IStateAccessor interface {
|
||||
GetRepoPathStack() *utils.StringStack
|
||||
GetRepoState() IRepoStateAccessor
|
||||
@@ -303,9 +277,6 @@ type IStateAccessor interface {
|
||||
SetShowExtrasWindow(bool)
|
||||
GetRetainOriginalDir() bool
|
||||
SetRetainOriginalDir(bool)
|
||||
GetItemOperation(item HasUrn) ItemOperation
|
||||
SetItemOperation(item HasUrn, operation ItemOperation)
|
||||
ClearItemOperation(item HasUrn)
|
||||
}
|
||||
|
||||
type IRepoStateAccessor interface {
|
||||
|
||||
@@ -102,7 +102,6 @@ type IFilterableContext interface {
|
||||
SetFilter(string)
|
||||
GetFilter() string
|
||||
ClearFilter()
|
||||
ReApplyFilter()
|
||||
IsFiltering() bool
|
||||
IsFilterableContext()
|
||||
}
|
||||
|
||||
+1
-1
@@ -169,7 +169,7 @@ func chineseTranslationSet() TranslationSet {
|
||||
ToggleStagingPanel: `切换到其他面板`,
|
||||
ReturnToFilesPanel: `返回文件面板`,
|
||||
FastForward: `从上游快进此分支`,
|
||||
FastForwarding: "抓取并快进",
|
||||
FastForwarding: "抓取并快进 {{.branch}} ...",
|
||||
FoundConflictsTitle: "自动合并失败",
|
||||
ViewMergeRebaseOptions: "查看 合并/变基 选项",
|
||||
NotMergingOrRebasing: "您目前既不进行变基也不进行合并",
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ func dutchTranslationSet() TranslationSet {
|
||||
ToggleStagingPanel: `Ga naar een ander paneel`,
|
||||
ReturnToFilesPanel: `Ga terug naar het bestanden paneel`,
|
||||
FastForward: `Fast-forward deze branch vanaf zijn upstream`,
|
||||
FastForwarding: "Fast-forwarding",
|
||||
FastForwarding: "Fast-forwarding {{.branch}} ...",
|
||||
FoundConflictsTitle: "Conflicten!",
|
||||
ViewMergeRebaseOptions: "Bekijk merge/rebase opties",
|
||||
NotMergingOrRebasing: "Je bent momenteel niet aan het rebasen of mergen",
|
||||
|
||||
+1
-5
@@ -57,8 +57,6 @@ type TranslationSet struct {
|
||||
ResetFilter string
|
||||
MergeConflictsTitle string
|
||||
Checkout string
|
||||
CantCheckoutBranchWhilePulling string
|
||||
CantPullOrPushSameBranchTwice string
|
||||
NoChangedFiles string
|
||||
SoftReset string
|
||||
AlreadyCheckedOutBranch string
|
||||
@@ -848,8 +846,6 @@ func EnglishTranslationSet() TranslationSet {
|
||||
Scroll: "Scroll",
|
||||
MergeConflictsTitle: "Merge conflicts",
|
||||
Checkout: "Checkout",
|
||||
CantCheckoutBranchWhilePulling: "You cannot checkout another branch while pulling the current branch",
|
||||
CantPullOrPushSameBranchTwice: "You cannot push or pull a branch while it is already being pushed or pulled",
|
||||
FileFilter: "Filter files by status",
|
||||
FilterStagedFiles: "Show only staged files",
|
||||
FilterUnstagedFiles: "Show only unstaged files",
|
||||
@@ -984,7 +980,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
ToggleStagingPanel: `Switch to other panel (staged/unstaged changes)`,
|
||||
ReturnToFilesPanel: `Return to files panel`,
|
||||
FastForward: `Fast-forward this branch from its upstream`,
|
||||
FastForwarding: "Fast-forwarding",
|
||||
FastForwarding: "Fast-forwarding {{.branch}}",
|
||||
FoundConflictsTitle: "Conflicts!",
|
||||
ViewConflictsMenuItem: "View conflicts",
|
||||
AbortMenuItem: "Abort the %s",
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ func koreanTranslationSet() TranslationSet {
|
||||
ToggleStagingPanel: `패널 전환`,
|
||||
ReturnToFilesPanel: `파일 목록으로 돌아가기`,
|
||||
FastForward: `Fast-forward this branch from its upstream`,
|
||||
FastForwarding: "Fast-forwarding",
|
||||
FastForwarding: "Fast-forwarding {{.branch}} ...",
|
||||
FoundConflictsTitle: "Auto-merge failed",
|
||||
ViewMergeRebaseOptions: "View merge/rebase options",
|
||||
NotMergingOrRebasing: "You are currently neither rebasing nor merging",
|
||||
|
||||
+1
-1
@@ -201,7 +201,7 @@ func RussianTranslationSet() TranslationSet {
|
||||
ToggleStagingPanel: `Переключиться на другую панель (проиндексированные/непроиндексированные изменения)`,
|
||||
ReturnToFilesPanel: `Вернуться к панели файлов`,
|
||||
FastForward: `Перемотать эту ветку вперёд из её upstream-ветки`,
|
||||
FastForwarding: "Получить изменения и перемотать вперёд",
|
||||
FastForwarding: "Получить изменения и перемотать вперёд {{.branch}} ...",
|
||||
FoundConflictsTitle: "Конфликты!",
|
||||
ViewConflictsMenuItem: "Просмотр конфликтов",
|
||||
AbortMenuItem: "Прервать %s",
|
||||
|
||||
@@ -234,7 +234,7 @@ func traditionalChineseTranslationSet() TranslationSet {
|
||||
ToggleStagingPanel: `切換至另一個面板 (已預存/未預存更改)`,
|
||||
ReturnToFilesPanel: `返回檔案面板`,
|
||||
FastForward: `從上游快進此分支`,
|
||||
FastForwarding: "的擷取和快進中",
|
||||
FastForwarding: "{{.branch}} 的擷取和快進中...",
|
||||
FoundConflictsTitle: "自動合併失敗",
|
||||
ViewMergeRebaseOptions: "查看合併/變基選項",
|
||||
NotMergingOrRebasing: "你當前既不在變基也不在合併中",
|
||||
|
||||
@@ -31,7 +31,7 @@ func main() {
|
||||
|
||||
integrationTest := getIntegrationTest()
|
||||
|
||||
if os.Getenv(components.WAIT_FOR_DEBUGGER_ENV_VAR) != "" {
|
||||
if os.Getenv("WAIT_FOR_DEBUGGER") != "" {
|
||||
println("Waiting for debugger to attach...")
|
||||
for !isDebuggerAttached() {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
const (
|
||||
TEST_NAME_ENV_VAR = "TEST_NAME"
|
||||
SANDBOX_ENV_VAR = "SANDBOX"
|
||||
WAIT_FOR_DEBUGGER_ENV_VAR = "WAIT_FOR_DEBUGGER"
|
||||
GIT_CONFIG_GLOBAL_ENV_VAR = "GIT_CONFIG_GLOBAL"
|
||||
)
|
||||
|
||||
@@ -43,7 +42,7 @@ func RunTests(
|
||||
|
||||
testDir := filepath.Join(projectRootDir, "test", "_results")
|
||||
|
||||
if err := buildLazygit(waitForDebugger, raceDetector); err != nil {
|
||||
if err := buildLazygit(raceDetector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -139,17 +138,12 @@ func prepareTestDir(
|
||||
return createFixture(test, paths, rootDir)
|
||||
}
|
||||
|
||||
func buildLazygit(debug bool, raceDetector bool) error {
|
||||
func buildLazygit(raceDetector bool) error {
|
||||
// // TODO: remove this line!
|
||||
// // skipping this because I'm not making changes to the app code atm.
|
||||
// return nil
|
||||
|
||||
args := []string{"go", "build"}
|
||||
if debug {
|
||||
// Disable compiler optimizations (-N) and inlining (-l) because this
|
||||
// makes debugging work better
|
||||
args = append(args, "-gcflags=all=-N -l")
|
||||
}
|
||||
if raceDetector {
|
||||
args = append(args, "-race")
|
||||
}
|
||||
@@ -216,7 +210,7 @@ func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, sandb
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", SANDBOX_ENV_VAR, "true"))
|
||||
}
|
||||
if waitForDebugger {
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("%s=true", WAIT_FOR_DEBUGGER_ENV_VAR))
|
||||
cmdObj.AddEnvVars("WAIT_FOR_DEBUGGER=true")
|
||||
}
|
||||
// Set a race detector log path only to avoid spamming the terminal with the
|
||||
// logs. We are not showing this anywhere yet.
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package filter_and_search
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var FilterRemotes = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Filtering remotes",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("commit-one")
|
||||
shell.CloneIntoRemote("remote1")
|
||||
shell.CloneIntoRemote("remote2")
|
||||
shell.CloneIntoRemote("remote3")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Remotes().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("remote1").IsSelected(),
|
||||
Contains("remote2"),
|
||||
Contains("remote3"),
|
||||
).
|
||||
FilterOrSearch("2").
|
||||
Lines(
|
||||
Contains("remote2").IsSelected(),
|
||||
).
|
||||
// cancel the filter
|
||||
PressEscape().
|
||||
Tap(func() {
|
||||
t.Views().Search().IsInvisible()
|
||||
}).
|
||||
Lines(
|
||||
Contains("remote1"),
|
||||
Contains("remote2").IsSelected(),
|
||||
Contains("remote3"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -1,52 +0,0 @@
|
||||
package filter_and_search
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var FilterUpdatesWhenModelChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Verify that after deleting a branch the filter is reapplied to show only the remaining branches",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.NewBranch("branch-to-delete")
|
||||
shell.NewBranch("other")
|
||||
shell.NewBranch("checked-out-branch")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("checked-out-branch").IsSelected(),
|
||||
Contains("other"),
|
||||
Contains("branch-to-delete"),
|
||||
Contains("master"),
|
||||
).
|
||||
FilterOrSearch("branch").
|
||||
Lines(
|
||||
Contains("branch-to-delete").IsSelected(),
|
||||
Contains("checked-out-branch"),
|
||||
).
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().
|
||||
Menu().
|
||||
Title(Equals("Delete branch 'branch-to-delete'?")).
|
||||
Select(Contains("Delete local branch")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
Contains("checked-out-branch").IsSelected(),
|
||||
).
|
||||
// cancel the filter
|
||||
PressEscape().
|
||||
Lines(
|
||||
Contains("checked-out-branch").IsSelected(),
|
||||
Contains("other"),
|
||||
Contains("master"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DisabledKeybindings = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Confirms you can disable keybindings by setting them to <disabled>",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.UserConfig.Keybinding.Universal.PrevItem = "<disabled>"
|
||||
config.UserConfig.Keybinding.Universal.NextItem = "<disabled>"
|
||||
config.UserConfig.Keybinding.Universal.NextTab = "<up>"
|
||||
config.UserConfig.Keybinding.Universal.PrevTab = "<down>"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press("<up>")
|
||||
|
||||
t.Views().Worktrees().IsFocused()
|
||||
},
|
||||
})
|
||||
@@ -131,9 +131,7 @@ var tests = []*components.IntegrationTest{
|
||||
filter_and_search.FilterMenu,
|
||||
filter_and_search.FilterMenuCancelFilterWithEscape,
|
||||
filter_and_search.FilterRemoteBranches,
|
||||
filter_and_search.FilterRemotes,
|
||||
filter_and_search.FilterSearchHistory,
|
||||
filter_and_search.FilterUpdatesWhenModelChanges,
|
||||
filter_and_search.NestedFilter,
|
||||
filter_and_search.NestedFilterTransient,
|
||||
filter_and_search.NewSearch,
|
||||
@@ -172,7 +170,6 @@ var tests = []*components.IntegrationTest{
|
||||
interactive_rebase.SwapWithConflict,
|
||||
misc.ConfirmOnQuit,
|
||||
misc.CopyToClipboard,
|
||||
misc.DisabledKeybindings,
|
||||
misc.InitialOpen,
|
||||
misc.RecentReposOnLaunch,
|
||||
patch_building.Apply,
|
||||
|
||||
@@ -19,8 +19,6 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
println("Generating test_list.go...")
|
||||
|
||||
code := generateCode()
|
||||
|
||||
formattedCode, err := format.Source(code)
|
||||
|
||||
+1
-26
@@ -1,10 +1,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
import "golang.org/x/exp/slices"
|
||||
|
||||
// NextIndex returns the index of the element that comes after the given number
|
||||
func NextIndex(numbers []int, currentNumber int) int {
|
||||
@@ -183,24 +179,3 @@ func Shift[T any](slice []T) (T, []T) {
|
||||
slice = slice[1:]
|
||||
return value, slice
|
||||
}
|
||||
|
||||
// Map function which handles each element in its own goroutine
|
||||
func ConcurrentMap[T any, V any](items []T, fn func(T) V) []V {
|
||||
results := make([]V, len(items))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i, item := range items {
|
||||
i := i
|
||||
item := item
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
results[i] = fn(item)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
@@ -315,13 +315,3 @@ func TestMoveElement(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestConcurrentMap(t *testing.T) {
|
||||
in := []int{1, 2, 3}
|
||||
|
||||
out := ConcurrentMap(in, func(i int) int {
|
||||
return i * 2
|
||||
})
|
||||
|
||||
assert.EqualValues(t, []int{2, 4, 6}, out)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/cheatsheet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal("Please provide a command: one of 'generate', 'check'")
|
||||
}
|
||||
|
||||
command := os.Args[1]
|
||||
|
||||
switch command {
|
||||
case "generate":
|
||||
cheatsheet.Generate()
|
||||
fmt.Printf("\nGenerated cheatsheets in %s\n", cheatsheet.GetKeybindingsDir())
|
||||
case "check":
|
||||
cheatsheet.Check()
|
||||
default:
|
||||
log.Fatal("\nUnknown command. Expected one of 'generate', 'check'")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user