mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-04 13:06:51 -04:00
Compare commits
222
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8311933de4 | ||
|
|
0afcc3d34d | ||
|
|
23613b8063 | ||
|
|
c426df91f3 | ||
|
|
7274d86d54 | ||
|
|
d9324019a3 | ||
|
|
221ebdc1fb | ||
|
|
2c9b4770bc | ||
|
|
9867180202 | ||
|
|
8dec35ba67 | ||
|
|
74e07080d0 | ||
|
|
ab3004bcd5 | ||
|
|
1ea0c270df | ||
|
|
51fb82d6bf | ||
|
|
280b4d60f8 | ||
|
|
54bd94ad24 | ||
|
|
8840c1a2b7 | ||
|
|
d08fafb1c4 | ||
|
|
f3eb180f75 | ||
|
|
c0c3aac02e | ||
|
|
24a4302c52 | ||
|
|
e887a2eb3c | ||
|
|
8a94663df1 | ||
|
|
8062f96d6b | ||
|
|
cf59b40a1b | ||
|
|
53acbc8f18 | ||
|
|
36e57d16bd | ||
|
|
080aa78266 | ||
|
|
fc8998e377 | ||
|
|
d4a0ca35c7 | ||
|
|
f79305090b | ||
|
|
f133224683 | ||
|
|
83337d9fa8 | ||
|
|
84e1d15079 | ||
|
|
09a24ee97d | ||
|
|
99a3ccde71 | ||
|
|
8ca8a43968 | ||
|
|
9fa43394fe | ||
|
|
37590a495c | ||
|
|
ee106f9af8 | ||
|
|
23a98937b4 | ||
|
|
a0b63090e0 | ||
|
|
53a8bd2e3f | ||
|
|
e9962b98a7 | ||
|
|
a1ce6029c1 | ||
|
|
8c716184c1 | ||
|
|
5c888a0b47 | ||
|
|
498092a8ec | ||
|
|
caf6a3629d | ||
|
|
1ca96bbe5b | ||
|
|
cb5d0bca1c | ||
|
|
ef3d6f4a32 | ||
|
|
b470442a46 | ||
|
|
bf01c0b00e | ||
|
|
76e39af76f | ||
|
|
5b91cd0cc8 | ||
|
|
6255728e63 | ||
|
|
b657fc4f0b | ||
|
|
d294d51791 | ||
|
|
b35f8776e1 | ||
|
|
8ca78412ac | ||
|
|
33f933ba21 | ||
|
|
d70dd5123d | ||
|
|
15da702140 | ||
|
|
cd50c79ae4 | ||
|
|
3ebba5f32c | ||
|
|
9a423c388d | ||
|
|
daf9b8cfa9 | ||
|
|
b6a9220343 | ||
|
|
aeb017e029 | ||
|
|
a6174271aa | ||
|
|
517e0f8248 | ||
|
|
c1cb95db6f | ||
|
|
966e5f5337 | ||
|
|
f244ec8251 | ||
|
|
2b97f0fb43 | ||
|
|
af5e25cfb5 | ||
|
|
93cae68e94 | ||
|
|
6e6fe6a489 | ||
|
|
2c2436574d | ||
|
|
0cdca9ac2c | ||
|
|
4172be6bc8 | ||
|
|
d97b37a178 | ||
|
|
21334fa889 | ||
|
|
36a29f225b | ||
|
|
1e85c4379f | ||
|
|
470632b97a | ||
|
|
50044dd5e0 | ||
|
|
bc330b8ff3 | ||
|
|
7f36494eb2 | ||
|
|
85c48ba887 | ||
|
|
1e3935cbaf | ||
|
|
3fe491fcb2 | ||
|
|
66b608b2f9 | ||
|
|
4ee01d153b | ||
|
|
79e04fad9a | ||
|
|
6778bc04a3 | ||
|
|
d548f857a4 | ||
|
|
f99c59b6d5 | ||
|
|
0fd4983c66 | ||
|
|
240948b882 | ||
|
|
cf82e69bbe | ||
|
|
653e59a3d5 | ||
|
|
e342860ef1 | ||
|
|
569adae6a2 | ||
|
|
79fe885dcd | ||
|
|
a46f26e148 | ||
|
|
ca4b8b25f0 | ||
|
|
dad2c5fa52 | ||
|
|
8a08abcd35 | ||
|
|
b96befa250 | ||
|
|
8cc820668a | ||
|
|
ccb1ee04a5 | ||
|
|
d00936fb4e | ||
|
|
e84d5acd23 | ||
|
|
a8a4211d2b | ||
|
|
6907816af9 | ||
|
|
38db574de9 | ||
|
|
c7012528fc | ||
|
|
2162e5ff64 | ||
|
|
f40f5710f0 | ||
|
|
97940cae10 | ||
|
|
1555503493 | ||
|
|
1a035db4c8 | ||
|
|
df5b3693d6 | ||
|
|
b123719107 | ||
|
|
6ec109c15a | ||
|
|
dee1ff007d | ||
|
|
02b739743a | ||
|
|
f5361fdcb4 | ||
|
|
b86d5bd688 | ||
|
|
4bfffa69ff | ||
|
|
f9b4bcde38 | ||
|
|
aaecd6cc40 | ||
|
|
7e5f25e415 | ||
|
|
d8059d7f7d | ||
|
|
0f2b79a1d4 | ||
|
|
bb87642aee | ||
|
|
4a5b3eaa52 | ||
|
|
1b4e76797f | ||
|
|
58971182ca | ||
|
|
9427a85805 | ||
|
|
d145e818d0 | ||
|
|
e0fc8fe25b | ||
|
|
bb705d91a4 | ||
|
|
738fa286b2 | ||
|
|
1d1b8cc01f | ||
|
|
c357284e1a | ||
|
|
232596b83d | ||
|
|
1c1d558cc7 | ||
|
|
dffb16ed96 | ||
|
|
df8ce76bd6 | ||
|
|
4d18b62c54 | ||
|
|
8193731a82 | ||
|
|
1a6d062192 | ||
|
|
1ff13cdfc6 | ||
|
|
19e8cafe41 | ||
|
|
cbb5fe6007 | ||
|
|
c4fc30c243 | ||
|
|
117aa1dcc6 | ||
|
|
c550737a4f | ||
|
|
9e37ae3f5d | ||
|
|
c89ef8b84a | ||
|
|
23befdd13a | ||
|
|
58a83b0862 | ||
|
|
d5b4f7bb3e | ||
|
|
3cee4de39c | ||
|
|
f609a04671 | ||
|
|
7ffb6ffb0f | ||
|
|
3691856021 | ||
|
|
b5ca6a3add | ||
|
|
ecaa4846f1 | ||
|
|
787f9966ec | ||
|
|
013cfc77a1 | ||
|
|
16f7b01fec | ||
|
|
421c6565f9 | ||
|
|
d02deeefd8 | ||
|
|
235f5bb221 | ||
|
|
be3b4bd791 | ||
|
|
67d6447e12 | ||
|
|
fd9d7cb7bb | ||
|
|
3d6965ccbb | ||
|
|
707fa37160 | ||
|
|
7075b66bc6 | ||
|
|
9d55d71fdd | ||
|
|
cc9a20c4ab | ||
|
|
c39fafe6ec | ||
|
|
2a11725749 | ||
|
|
deed9eb18e | ||
|
|
50b49ebeb1 | ||
|
|
a74c6eef40 | ||
|
|
a3759c65e4 | ||
|
|
519760077b | ||
|
|
8390622f70 | ||
|
|
a04fbafcfd | ||
|
|
4fe8dee40f | ||
|
|
6fd80565c7 | ||
|
|
5ebd8ac7fe | ||
|
|
3cda1d03d9 | ||
|
|
d4eb02fac0 | ||
|
|
9c72d8a2b0 | ||
|
|
4f3127ccb8 | ||
|
|
877c27722f | ||
|
|
0dda8b58d1 | ||
|
|
e997d1ae5d | ||
|
|
3e2ef84d56 | ||
|
|
b2eccd6fe8 | ||
|
|
cc25a3b8c5 | ||
|
|
e93d945dba | ||
|
|
246e08096f | ||
|
|
860a52a736 | ||
|
|
18d8b29461 | ||
|
|
7af371701d | ||
|
|
5ccc95b76f | ||
|
|
cdb1d76484 | ||
|
|
9e423e949c | ||
|
|
e1ceb6892a | ||
|
|
40b8557608 | ||
|
|
92e107f52d | ||
|
|
2f6a87df98 | ||
|
|
2c29577e3c | ||
|
|
e72559bb27 |
@@ -2,7 +2,7 @@
|
||||
|
||||
- **Please check if the PR fulfills these requirements**
|
||||
|
||||
* [ ] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go generate`)
|
||||
* [ ] Cheatsheets are up-to-date (run `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))
|
||||
|
||||
@@ -36,7 +36,14 @@ jobs:
|
||||
- name: Test code
|
||||
# we're passing -short so that we skip the integration tests, which will be run in parallel below
|
||||
run: |
|
||||
go test ./... -short
|
||||
mkdir -p /tmp/code_coverage
|
||||
go test ./... -short -cover -args "-test.gocoverdir=/tmp/code_coverage"
|
||||
- name: Upload code coverage artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-unit-${{ matrix.os }}-${{ github.run_id }}
|
||||
path: /tmp/code_coverage
|
||||
|
||||
integration-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -86,8 +93,17 @@ jobs:
|
||||
- name: Print git version
|
||||
run: git --version
|
||||
- name: Test code
|
||||
env:
|
||||
# See https://go.dev/blog/integration-test-coverage
|
||||
LAZYGIT_GOCOVERDIR: /tmp/code_coverage
|
||||
run: |
|
||||
mkdir -p /tmp/code_coverage
|
||||
./scripts/run_integration_tests.sh
|
||||
- name: Upload code coverage artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-integration-${{ matrix.git-version }}-${{ github.run_id }}
|
||||
path: /tmp/code_coverage
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@@ -127,17 +143,18 @@ 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 Integration Test List
|
||||
# ensure our integration test list is up to date
|
||||
- name: Check go.mod file
|
||||
# ensure our go.mod file is clean
|
||||
run: |
|
||||
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)
|
||||
go mod tidy && git diff --exit-code || (echo "go.mod file is not clean. Run 'go mod tidy' locally and commit the changes" && exit 1)
|
||||
- name: Check All Auto-Generated Files
|
||||
# ensure all our auto-generated files are 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)
|
||||
shell: bash # needed so that we get "-o pipefail"
|
||||
- name: Check Filenames
|
||||
run: scripts/check_filenames.sh
|
||||
@@ -168,3 +185,31 @@ jobs:
|
||||
mode: exactly
|
||||
count: 1
|
||||
labels: "ignore-for-release, feature, enhancement, bug, maintenance, docs, i18n"
|
||||
upload-coverage:
|
||||
# List all jobs that produce coverage files
|
||||
needs: [unit-tests, integration-tests]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: /tmp/code_coverage
|
||||
|
||||
- name: Combine coverage files
|
||||
run: |
|
||||
# Find all directories in /tmp/code_coverage and create a comma-separated list
|
||||
COVERAGE_DIRS=$(find /tmp/code_coverage -mindepth 1 -maxdepth 1 -type d -printf '/tmp/code_coverage/%f,' | sed 's/,$//')
|
||||
echo "Coverage directories: $COVERAGE_DIRS"
|
||||
# Run the combine command with the generated list
|
||||
go tool covdata textfmt -i=$COVERAGE_DIRS -o coverage.out
|
||||
echo "Combined coverage:"
|
||||
go tool cover -func coverage.out | tail -1 | awk '{print $3}'
|
||||
|
||||
- name: Upload to Codacy
|
||||
run: |
|
||||
CODACY_PROJECT_TOKEN=${{ secrets.CODACY_PROJECT_TOKEN }} \
|
||||
bash <(curl -Ls https://coverage.codacy.com/get.sh) report \
|
||||
--force-coverage-parser go -r coverage.out
|
||||
|
||||
@@ -39,6 +39,9 @@ test/_results/**
|
||||
|
||||
oryxBuildBinary
|
||||
__debug_bin
|
||||
__debug_bin.exe
|
||||
|
||||
.worktrees
|
||||
demo/output/*
|
||||
|
||||
coverage.out
|
||||
|
||||
+5
-1
@@ -20,7 +20,11 @@ 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
-6
@@ -13,9 +13,6 @@
|
||||
],
|
||||
"hideSystemGoroutines": true,
|
||||
"console": "integratedTerminal",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Tail Lazygit logs",
|
||||
@@ -28,9 +25,6 @@
|
||||
"--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Attach to a running Lazygit",
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"gopls": {
|
||||
"formatting.gofumpt": true,
|
||||
},
|
||||
}
|
||||
@@ -10,6 +10,15 @@ before making a change.
|
||||
|
||||
[This video](https://www.youtube.com/watch?v=kNavnhzZHtk) walks through the process of adding a small feature to lazygit. If you have no idea where to start, watching that video is a good first step.
|
||||
|
||||
## Codebase guide
|
||||
|
||||
[This doc](./docs/dev/Codebase_Guide.md) explains:
|
||||
* what the different packages in the codebase are for
|
||||
* where important files live
|
||||
* important concepts in the code
|
||||
* how the event loop works
|
||||
* other useful information
|
||||
|
||||
## All code changes happen through Pull Requests
|
||||
|
||||
Pull requests are the best way to propose changes to the codebase. We actively
|
||||
@@ -21,6 +30,8 @@ welcome your pull requests:
|
||||
4. Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
5. Issue that pull request!
|
||||
|
||||
Please do not raise pull request from your fork's master branch: make a feature branch instead. Lazygit maintainers will sometimes push changes to your branch when reviewing a PR and we often can't do this if you use your master branch.
|
||||
|
||||
If you've never written Go in your life, then join the club! Lazygit was the maintainer's first Go program, and most contributors have never used Go before. Go is widely considered an easy-to-learn language, so if you're looking for an open source project to gain dev experience, you've come to the right place.
|
||||
|
||||
## Running in a VSCode dev container
|
||||
@@ -97,6 +108,10 @@ To run gofumpt from your terminal go:
|
||||
go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
|
||||
```
|
||||
|
||||
## Programming Font
|
||||
|
||||
Lazygit supports [Nerd Fonts](https://www.nerdfonts.com) to render certain icons. Sometimes we use some of these icons verbatim in string literals in the code (mainly in tests), so you need to set your development environment to use a nerd font to see these.
|
||||
|
||||
## Internationalisation
|
||||
|
||||
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `self.c.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
|
||||
|
||||
@@ -3,7 +3,7 @@ all: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build
|
||||
go build -gcflags='all=-N -l'
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
@@ -29,6 +29,7 @@ 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 ./...
|
||||
@@ -37,10 +38,6 @@ 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:
|
||||
|
||||
+14
-17
@@ -3,7 +3,7 @@
|
||||
Default path for the config file:
|
||||
|
||||
- Linux: `~/.config/lazygit/config.yml`
|
||||
- MacOS: `~/Library/Application Support/lazygit/config.yml`
|
||||
- MacOS: `~/Library/Application\ Support/lazygit/config.yml`
|
||||
- Windows: `%APPDATA%\lazygit\config.yml`
|
||||
|
||||
For old installations (slightly embarrassing: I didn't realise at the time that you didn't need to supply a vendor name to the path so I just used my name):
|
||||
@@ -40,6 +40,7 @@ gui:
|
||||
sidePanelWidth: 0.3333 # number from 0 to 1
|
||||
expandFocusedSidePanel: false
|
||||
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
|
||||
enlargedSideViewLocation: 'left' # one of 'left' | 'top'
|
||||
language: 'auto' # one of 'auto' | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
timeFormat: '02 Jan 06' # https://pkg.go.dev/time#Time.Format
|
||||
shortTimeFormat: '3:04PM'
|
||||
@@ -56,8 +57,6 @@ gui:
|
||||
- blue
|
||||
selectedLineBgColor:
|
||||
- blue # set to `default` to have no background colour
|
||||
selectedRangeBgColor:
|
||||
- blue
|
||||
cherryPickedCommitBgColor:
|
||||
- cyan
|
||||
cherryPickedCommitFgColor:
|
||||
@@ -85,6 +84,7 @@ 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
|
||||
@@ -162,7 +162,7 @@ keybinding:
|
||||
jumpToBlock: ['1', '2', '3', '4', '5'] # goto the Nth block / panel
|
||||
nextMatch: 'n'
|
||||
prevMatch: 'N'
|
||||
optionMenu: null # show help menu
|
||||
optionMenu: <disabled> # show help menu
|
||||
optionMenu-alt1: '?' # show help menu
|
||||
select: '<space>'
|
||||
goInto: '<enter>'
|
||||
@@ -199,6 +199,9 @@ keybinding:
|
||||
toggleWhitespaceInDiffView: '<c-w>'
|
||||
increaseContextInDiffView: '}'
|
||||
decreaseContextInDiffView: '{'
|
||||
toggleRangeSelect: 'v'
|
||||
rangeSelectUp: '<s-up>'
|
||||
rangeSelectDown: '<s-down>'
|
||||
status:
|
||||
checkForUpdate: 'u'
|
||||
recentRepos: '<enter>'
|
||||
@@ -207,6 +210,7 @@ keybinding:
|
||||
commitChangesWithoutHook: 'w' # commit changes without pre-commit hook
|
||||
amendLastCommit: 'A'
|
||||
commitChangesWithEditor: 'C'
|
||||
findBaseCommitForFixup: '<c-f>'
|
||||
confirmDiscard: 'x'
|
||||
ignoreFile: 'i'
|
||||
refreshFiles: 'r'
|
||||
@@ -245,9 +249,8 @@ keybinding:
|
||||
amendToCommit: 'A'
|
||||
pickCommit: 'p' # pick commit (when mid-rebase)
|
||||
revertCommit: 't'
|
||||
cherryPickCopy: 'c'
|
||||
cherryPickCopyRange: 'C'
|
||||
pasteCommits: 'v'
|
||||
cherryPickCopy: 'C'
|
||||
pasteCommits: 'V'
|
||||
tagCommit: 'T'
|
||||
checkoutCommit: '<space>'
|
||||
resetCherryPick: '<c-R>'
|
||||
@@ -260,8 +263,6 @@ keybinding:
|
||||
commitFiles:
|
||||
checkoutCommitFile: 'c'
|
||||
main:
|
||||
toggleDragSelect: 'v'
|
||||
toggleDragSelect-alt: 'V'
|
||||
toggleSelectHunk: 'a'
|
||||
pickBothHunks: 'b'
|
||||
submodules:
|
||||
@@ -318,7 +319,7 @@ os:
|
||||
editPreset: 'vscode'
|
||||
```
|
||||
|
||||
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.
|
||||
Supported presets are `vim`, `nvim`, `nvim-remote`, `lvim`, `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.
|
||||
|
||||
`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.
|
||||
|
||||
@@ -386,15 +387,13 @@ The available attributes are:
|
||||
|
||||
## Highlighting the selected line
|
||||
|
||||
If you don't like the default behaviour of highlighting the selected line with a blue background, you can use the `selectedLineBgColor` and `selectedRangeBgColor` keys to customise the behaviour. If you just want to embolden the selected line (this was the original default), you can do the following:
|
||||
If you don't like the default behaviour of highlighting the selected line with a blue background, you can use the `selectedLineBgColor` key to customise the behaviour. If you just want to embolden the selected line (this was the original default), you can do the following:
|
||||
|
||||
```yaml
|
||||
gui:
|
||||
theme:
|
||||
selectedLineBgColor:
|
||||
- default
|
||||
selectedRangeBgColor:
|
||||
- default
|
||||
```
|
||||
|
||||
You can also use the reverse attribute like so:
|
||||
@@ -404,8 +403,6 @@ gui:
|
||||
theme:
|
||||
selectedLineBgColor:
|
||||
- reverse
|
||||
selectedRangeBgColor:
|
||||
- reverse
|
||||
```
|
||||
|
||||
## Custom Author Color
|
||||
@@ -461,12 +458,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 `null`.
|
||||
You can disable certain key bindings by specifying `<disabled>`.
|
||||
|
||||
```yaml
|
||||
keybinding:
|
||||
universal:
|
||||
edit: null # disable 'edit file'
|
||||
edit: <disabled> # disable 'edit file'
|
||||
```
|
||||
|
||||
### Example Keybindings For Colemak Users
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Fixup Commits
|
||||
|
||||
## Background
|
||||
|
||||
There's this common scenario that you have a PR in review, the reviewer is
|
||||
requesting some changes, and you make those changes and would normally simply
|
||||
squash them into the original commit that they came from. If you do that,
|
||||
however, there's no way for the reviewer to see what you changed. You could just
|
||||
make a separate commit with those changes at the end of the branch, but this is
|
||||
not ideal because it results in a git history that is not very clean.
|
||||
|
||||
To help with this, git has a concept of fixup commits: you do make a separate
|
||||
commit, but the subject of this commit is the string "fixup! " followed by the
|
||||
original commit subject. This both tells the reviewer what's going on (you are
|
||||
making a change that you later will squash into the designated commit), and it
|
||||
provides an easy way to actually perform this squash operation when you are
|
||||
ready to do that (before merging).
|
||||
|
||||
## Creating fixup commits
|
||||
|
||||
You could of course create fixup commits manually by typing in the commit
|
||||
message with the prefix yourself. But lazygit has an easier way to do that:
|
||||
in the Commits view, select the commit that you want to create a fixup for, and
|
||||
press shift-F (for "Create fixup commit for this commit"). This automatically
|
||||
creates a commit with the appropriate subject line.
|
||||
|
||||
Don't confuse this with the lowercase "f" command ("Fixup commit"); that one
|
||||
squashes the selected commit into its parent, this is not what we want here.
|
||||
|
||||
## Squashing fixup commits
|
||||
|
||||
When you're ready to merge the branch and want to squash all these fixup commits
|
||||
that you created, that's very easy to do: select the first commit of your branch
|
||||
and hit shift-S (for "Squash all 'fixup!' commits above selected commit
|
||||
(autosquash)"). Boom, done.
|
||||
|
||||
## Finding the commit to create a fixup for
|
||||
|
||||
When you are making changes to code that you changed earlier in a long branch,
|
||||
it can be tedious to find the commit to squash it into. Lazygit has a command to
|
||||
help you with this, too: in the Files view, press ctrl-f to select the right
|
||||
base commit in the Commits view automatically. From there, you can either press
|
||||
shift-F to create a fixup commit for it, or shift-A to amend your changes into
|
||||
the commit if you haven't published your branch yet.
|
||||
|
||||
This command works in many cases, and when it does it almost feels like magic,
|
||||
but it's important to understand its limitations because it doesn't always work.
|
||||
The way it works is that it looks at the deleted lines of your current
|
||||
modifications, blames them to find out which commit those lines come from, and
|
||||
if they all come from the same commit, it selects it. So here are cases where it
|
||||
doesn't work:
|
||||
|
||||
- Your current diff has only added lines, but no deleted lines. In this case
|
||||
there's no way for lazygit to know which commit you want to add them to.
|
||||
- The deleted lines belong to multiple different commits. In this case you can
|
||||
help lazygit by staging a set of files or hunks that all belong to the same
|
||||
commit; if some changes are staged, the ctrl-f command works only on those.
|
||||
- The found commit is already on master; in this case, lazygit refuses to select
|
||||
it, because it doesn't make sense to create fixups for it, let alone amend to
|
||||
it.
|
||||
|
||||
To sum it up: the command works great if you are changing code again that you
|
||||
changed or added earlier in the same branch. This is a common enough case to
|
||||
make the command useful.
|
||||
+2
-1
@@ -3,8 +3,9 @@
|
||||
* [Configuration](./Config.md).
|
||||
* [Custom Commands](./Custom_Command_Keybindings.md)
|
||||
* [Custom Pagers](./Custom_Pagers.md)
|
||||
* [Dev docs](./dev)
|
||||
* [Keybindings](./keybindings)
|
||||
* [Undo/Redo](./Undoing.md)
|
||||
* [Range Select](./Range_Select.md)
|
||||
* [Searching/Filtering](./Searching.md)
|
||||
* [Stacked Branches](./Stacked_Branches.md)
|
||||
* [Dev docs](./dev)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Range Select
|
||||
|
||||
Some actions can be performed on a range of contiguous items. For example:
|
||||
* staging multiple files at once
|
||||
* squashing multiple commits at once
|
||||
* copying (for cherry-pick) multiple commits at once
|
||||
|
||||
There are two ways to select a range of items:
|
||||
1. Sticky range select: Press 'v' to toggle range select, then expand the selection using the up/down arrow key. To reset the selection, press 'v' again.
|
||||
2. Non-sticky range select: Press shift+up or shift+down to expand the selection. To reset the selection, press up/down without shift.
|
||||
|
||||
The sticky option will be more familiar to vim users, and the second option will feel more natural to users who aren't used to doing things in a modal way.
|
||||
|
||||
In order to perform an action on a range of items, simply press the normal key for that action. If the action only works on individual items, it will raise an error. This is a new feature and the plan is to incrementally support range select for more and more actions. If there is an action you would like to support range select which currently does not, please raise an issue in the repo.
|
||||
@@ -7,7 +7,7 @@ refactorings, one for backend changes, and one for frontend changes. Those
|
||||
branches would then all be stacked onto each other.
|
||||
|
||||
Git has support for rebasing such a stack as a whole; you can enable it by
|
||||
setting the git config `rebase.updateRfs` to true. If you then rebase the
|
||||
setting the git config `rebase.updateRefs` to true. If you then rebase the
|
||||
topmost branch of the stack, the other ones in the stack will follow. This
|
||||
includes interactive rebases, so for example amending a commit in the first
|
||||
branch of the stack will "just work" in the sense that it keeps the other
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# Lazygit Codebase Guide
|
||||
|
||||
## Packages
|
||||
|
||||
* `pkg/app`: Contains startup code, inititalises a bunch of stuff like logging, the user config, etc, before starting the gui. Catches and handles some errors that the gui raises.
|
||||
* `pkg/app/daemon`: Contains code relating to the lazygit daemon. This could be better named: it's is not a daemon in the sense that it's a long-running background process; rather it's a short-lived background process that we pass to git for certain tasks, like GIT_EDITOR for when we want to set the TODO file for an interactive rebase.
|
||||
* `pkg/cheatsheet`: Generates the keybinding cheatsheets in `docs/keybindings`.
|
||||
* `pkg/commands/git_commands`: All communication to the git binary happens here. So for example there's a `Checkout` method which calls `git checkout`.
|
||||
* `pkg/commands/oscommands`: Contains code for talking to the OS, and for invoking commands in general
|
||||
* `pkg/commands/git_config`: Reading of the git config all happens here.
|
||||
* `pkg/commands/hosting_service`: Contains code that is specific to git hosting services (aka forges).
|
||||
* `pkg/commands/models`: Contains model structs that represent commits, branches, files, etc.
|
||||
* `pkg/commands/patch`: Contains code for parsing and working with git patches
|
||||
* `pkg/common`: Contains the `Common` struct which holds common dependencies like the logger, i18n, and the user config. Most structs in the code will have a field named `c` which holds a common struct (or a derivative of the common struct).
|
||||
* `pkg/config`: Contains code relating to the Lazygit user config. Specifically `pkg/config/user_config/go` defines the user config struct and its default values.
|
||||
* `pkg/constants`: Contains some constant strings (e.g. links to docs)
|
||||
* `pkg/env`: Contains code relating to setting/getting environment variables
|
||||
* `pkg/i18n`: Contains internationalised strings
|
||||
* `pkg/integration`: Contains end-to-end tests
|
||||
* `pkg/jsonschema`: Contains generator for user config JSON schema.
|
||||
* `pkg/logs`: Contains code for instantiating the logger and for tailing the logs via `lazygit --logs`
|
||||
* `pkg/tasks`: Contains code for running asynchronous tasks: mostly related to efficiently rendering command output to the main window.
|
||||
* `pkg/theme`: Contains code related to colour themes.
|
||||
* `pkg/updates`: Contains code related to Lazygit updates (checking for update, download and installing the update)
|
||||
* `pkg/utils`: Contains lots of low-level helper functions
|
||||
* `pkg/gui`: Contains code related to the gui. We've still got a God Struct in the form of our Gui struct, but over time code has been moved out into contexts, controllers, and helpers, and we intend to continue moving more code out over time.
|
||||
* `pkg/gui/context`: Contains code relating to contexts. There is a context for each view e.g. a branches context, a tags context, etc. Contexts manage state related to the view and receive keypresses.
|
||||
* `pkg/gui/controllers`: Contains code relating to controllers. Controllers define a list of keybindings and their associated handlers. One controller can be assigned to multiple contexts, and one context can contain multiple controllers.
|
||||
* `pkg/gui/controllers/helpers`: Contains code that is shared between multiple controllers.
|
||||
* `pkg/gui/filetree`: Contains code relating to the representation of filetrees.
|
||||
* `pkg/gui/keybindings`: Contains code for mapping between keybindings and their labels
|
||||
* `pkg/gui/mergeconflicts`: Contains code relating to the handling of merge conflicts
|
||||
* `pkg/gui/modes`: Contains code relating to the state of different modes e.g. cherry picking mode, rebase mode.
|
||||
* `pkg/gui/patch_exploring`: Contains code relating to the state of patch-oriented views like the staging view.
|
||||
* `pkg/gui/popup`: Contains code that lets you easily raise popups
|
||||
* `pkg/gui/presentation`: Contains presentation code i.e. code concerned with rendering content inside views
|
||||
* `pkg/gui/services/custom_commands`: Contains code related to user-defined custom commands.
|
||||
* `pkg/gui/status`: Contains code for invoking loaders and toasts
|
||||
* `pkg/gui/style`: Contains code for specifying text styles (colour, bold, etc)
|
||||
* `pkg/gui/types`: Contains various gui-specific types and interfaces. Lots of code lives here to avoid circular dependencies
|
||||
* `vendor/github.com/jesseduffield/gocui`: Gocui is the underlying library used for handling the gui event loop, handling keypresses, and rendering the UI. It defines the View struct which our own context structs build upon.
|
||||
|
||||
## Important files
|
||||
|
||||
* `pkg/config/user_config.go`: defines the user config and default values
|
||||
* `pkg/gui/keybindings.go`: defines keybindings which have not yet been moved into a controller (originally all keybindings were defined here)
|
||||
* `pkg/gui/controllers.go`: links up controllers with contexts
|
||||
* `pkg/gui/controllers/helpers/helpers.go`: defines all the different helper structs
|
||||
* `pkg/commands/git.go`: defines all the different git command structs
|
||||
* `pkg/gui/gui.go`: defines the top-level gui state and gui initialisation/run code
|
||||
* `pkg/gui/layout.go`: defines what happens on each render
|
||||
* `pkg/gui/controllers/helpers/window_arrangement_helper.go`: defines the layout of the UI and the size/position of each window
|
||||
* `pkg/gui/context/context.go`: defines the different contexts
|
||||
* `pkg/gui/context/setup.go`: defines initialisation code for all contexts
|
||||
* `pkg/gui/context.go`: manages the lifecycle of contexts, the context stack, and focus changes.
|
||||
* `pkg/gui/types/views.go`: defines views
|
||||
* `pkg/gui/views.go`: defines the ordering of views (front to back) and their initialisation code
|
||||
* `pkg/gui/gui_common.go`: defines gui-specific methods that all controllers and helpers have access to
|
||||
* `pkg/i18n/english.go`: defines the set of i18n strings and their English values
|
||||
* `pkg/gui/controllers/helpers/refresh_helper.go`: manages refreshing of models. The refresh helper is typically invoked at the end of an action to re-load affected models from git (e.g. re-load branches after doing a git pull)
|
||||
* `pkg/gui/controllers/quit_actions.go`: contains code that runs when you hit 'escape' on a view (assuming the view doesn't define its own escape handler)
|
||||
* `vendor/github.com/jesseduffield/gocui/gui.go`: defines the gocui gui struct
|
||||
* `vendor/github.com/jesseduffield/gocui/view.go`: defines the gocui view struct
|
||||
|
||||
## Concepts
|
||||
|
||||
* **View**: Views are defined in the gocui package, and they maintain an internal buffer of content which is rendered each time the screen is drawn.
|
||||
* **Context**: A context is tied to a view and contains some additional state and logic specific to that view e.g. the branches context has code relating specifically to branches, and writes the list of branches to the branches view. Views and contexts share some responsibilities for historical reasons.
|
||||
* **Controller**: A controller defined keybindings with associated handlers. One controller can be assigned to multiple contexts and one context can have multiple controllers. For example the list controller handles keybindings relating to navigating a list, and is assigned to all list contexts (e.g. the branches context).
|
||||
* **Helper**: A helper defines shared code used by controllers, or used by some other parts of the application. Often a controller will have a method that ends up needing to be used by another controller, so in that case we move the method out into a helper so that both controllers can use it. We need to do this because controllers cannot refer to other controllers' methods.
|
||||
|
||||
In terms of dependencies, controllers sit at the highest level, so they can refer to helpers, contexts, and views (although it's preferable for view-specific code to live in contexts). Helpers can refer to contexts and views, and contexts can only refer to views. Views can't refer to contexts, controllers, or helpers.
|
||||
|
||||
* **Window**: A window is a section of the screen which will render a view. Windows are named after the default view that appears there, so for example there is a 'stash' window that is so named because by default the stash view appears there. But if you press enter on a stash entry, the stash entry's files will be shown in a different view, but in the same window.
|
||||
* **Panel**: The term 'panel' is still used in a few places to refer to either a view or a window, and it's a term that is now deprecated in favour of 'view' and 'window'.
|
||||
* **Tab**: Each tab in a window (e.g. Files, Worktrees, Submodules) actually has a corresponding view which we bring to the front upon changing tabs.
|
||||
* **Model**: Representation of a git object e.g. commits, branches, files.
|
||||
* **ViewModel**: Used by a context to maintain state related to the view.
|
||||
* **Keybinding**: A keybinding associates a _key_ with an _action_. For example if you press the 'down' arrow, the action performed will be your cursor moving down a list by one.
|
||||
* **Action**: An action is the thing that happens when you press a key. Often an action will invoke a git command, but not always: for example, navigation actions don't involve git.
|
||||
* **Common structs**: Most structs have a field named `c` which contains a 'common' struct: a struct containing a bag of dependencies that most structs of the same layer require. For example if you want to access a helper from a controller you can do so with `self.c.Helpers.MyHelper`.
|
||||
|
||||
## Event loop and threads
|
||||
|
||||
The event loop is managed in the `MainLoop` function of `vendor/github.com/jesseduffield/gocui/gui.go`. Any time there is an event like a key press or a window resize, the event will be processed and then the screen will be redrawn. This involves calling the `layout` function defined in `pkg/gui/layout.go`, which lays out the windows and invokes some on-render hooks.
|
||||
|
||||
Often, as part of handling a keypress, we'll want to run some code asynchronously so that it doesn't block the UI thread. For this we'll typically run `self.c.OnWorker(myFunc)`. If the worker wants to then do something on the UI thread again it can call `self.c.OnUIThread(myOtherFunc)`.
|
||||
|
||||
## Legacy code structure
|
||||
|
||||
Before we had controllers and contexts, all the code lived directly in the gui package under a gui God Struct. This was fairly bloated and so we split things out to have a better separation of concerns. Nonetheless, it's a big effort to migrate all the code so we still have some logic in the gui struct that ought to live somewhere else. Likewise, we have some keybindings defined in `pkg/gui/keybindings.go` that ought to live on a controller (all keybindings used to be defined in that one file).
|
||||
|
||||
The new structure has its own problems: we don't have a clear guide on whether code should live in a controller or helper. The current approach is to put code in a controller until it's needed by another controller, and to then extract it out into a helper. We may be better off just putting code in helpers to start with and leaving controllers super-thin, with the responsibility of just pairing keys with corresponding helper functions. But it's not clear to me if that would be better than the current approach.
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
# Dev Documentation Overview
|
||||
|
||||
* [Busy/Idle tracking](./Busy.md).
|
||||
* [Codebase Guide](./Codebase_Guide.md)
|
||||
* [Busy/Idle Tracking](./Busy.md)
|
||||
* [Integration Tests](../../pkg/integration/README.md)
|
||||
* [Demo Recordings](./Demo_Recordings.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
@@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>.</kbd>: Next page
|
||||
<kbd><</kbd>: Scroll to top
|
||||
<kbd>></kbd>: Scroll to bottom
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
<kbd>H</kbd>: Scroll left
|
||||
<kbd>L</kbd>: Scroll right
|
||||
@@ -52,6 +55,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||
<kbd>o</kbd>: Open file
|
||||
<kbd>e</kbd>: Edit file
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -78,12 +82,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>R</kbd>: Reword commit with editor
|
||||
<kbd>d</kbd>: Delete commit
|
||||
<kbd>e</kbd>: Edit commit
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: Pick commit (when mid-rebase)
|
||||
<kbd>F</kbd>: Create fixup commit for this commit
|
||||
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
|
||||
<kbd><c-j></kbd>: Move commit down one
|
||||
<kbd><c-k></kbd>: Move commit up one
|
||||
<kbd>v</kbd>: Paste commits (cherry-pick)
|
||||
<kbd>V</kbd>: Paste commits (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Amend commit with staged changes
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
@@ -96,8 +101,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Create new branch off of commit
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
@@ -116,10 +121,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: View 'discard changes' options
|
||||
<kbd><space></kbd>: Toggle staged
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Commit changes
|
||||
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: Amend last commit
|
||||
<kbd>C</kbd>: Commit changes using git editor
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Edit file
|
||||
<kbd>o</kbd>: Open file
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
@@ -131,6 +138,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: View reset options
|
||||
<kbd>`</kbd>: Toggle file tree view
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
@@ -146,13 +154,14 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Create pull request
|
||||
<kbd>O</kbd>: Create pull request options
|
||||
<kbd><c-y></kbd>: Copy pull request URL to clipboard
|
||||
<kbd>c</kbd>: Checkout by name
|
||||
<kbd>c</kbd>: Checkout by name, enter '-' to switch to last
|
||||
<kbd>F</kbd>: Force checkout
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>M</kbd>: Merge into currently checked out branch
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: Create tag
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>R</kbd>: Rename branch
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -189,8 +198,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><left></kbd>: Select previous hunk
|
||||
<kbd><right></kbd>: Select next hunk
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open file
|
||||
@@ -205,8 +213,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><left></kbd>: Select previous hunk
|
||||
<kbd><right></kbd>: Select next hunk
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open file
|
||||
@@ -240,9 +247,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Create new branch off of commit
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -257,6 +264,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
@@ -306,9 +314,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Create new branch off of commit
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit キーバインド
|
||||
|
||||
@@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>.</kbd>: 次のページ
|
||||
<kbd><</kbd>: 最上部までスクロール
|
||||
<kbd>></kbd>: 最下部までスクロール
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
<kbd>H</kbd>: 左スクロール
|
||||
<kbd>L</kbd>: 右スクロール
|
||||
@@ -67,9 +70,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
<kbd>n</kbd>: コミットにブランチを作成
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
</pre>
|
||||
@@ -97,12 +100,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>R</kbd>: エディタでコミットメッセージを編集
|
||||
<kbd>d</kbd>: コミットを削除
|
||||
<kbd>e</kbd>: コミットを編集
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: Pick commit (when mid-rebase)
|
||||
<kbd>F</kbd>: このコミットに対するfixupコミットを作成
|
||||
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
|
||||
<kbd><c-j></kbd>: コミットを1つ下に移動
|
||||
<kbd><c-k></kbd>: コミットを1つ上に移動
|
||||
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
|
||||
<kbd>V</kbd>: コミットを貼り付け (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: ステージされた変更でamendコミット
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
@@ -115,8 +119,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
<kbd>n</kbd>: コミットにブランチを作成
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
</pre>
|
||||
@@ -129,6 +133,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -188,10 +193,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: View 'discard changes' options
|
||||
<kbd><space></kbd>: ステージ/アンステージ
|
||||
<kbd><c-b></kbd>: ファイルをフィルタ (ステージ/アンステージ)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 変更をコミット
|
||||
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
||||
<kbd>A</kbd>: 最新のコミットにamend
|
||||
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>i</kbd>: ファイルをignore
|
||||
@@ -203,6 +210,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: View reset options
|
||||
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Git mergetoolを開く
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
@@ -218,13 +226,14 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Pull Requestを作成
|
||||
<kbd>O</kbd>: Create pull request options
|
||||
<kbd><c-y></kbd>: Pull RequestのURLをクリップボードにコピー
|
||||
<kbd>c</kbd>: Checkout by name
|
||||
<kbd>c</kbd>: Checkout by name, enter '-' to switch to last
|
||||
<kbd>F</kbd>: Force checkout
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>M</kbd>: 現在のブランチにマージ
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: タグを作成
|
||||
<kbd>s</kbd>: 並び替え
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>R</kbd>: ブランチ名を変更
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -262,7 +271,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: 前のhunkを選択
|
||||
<kbd><right></kbd>: 次のhunkを選択
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: Hunk選択を切り替え
|
||||
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
@@ -278,7 +286,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: 前のhunkを選択
|
||||
<kbd><right></kbd>: 次のhunkを選択
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: Hunk選択を切り替え
|
||||
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
@@ -322,6 +329,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: 並び替え
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
@@ -338,9 +346,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
<kbd>n</kbd>: コミットにブランチを作成
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit 키 바인딩
|
||||
|
||||
@@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>.</kbd>: 다음 페이지
|
||||
<kbd><</kbd>: 맨 위로 스크롤
|
||||
<kbd>></kbd>: 맨 아래로 스크롤
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
<kbd>H</kbd>: 우 스크롤
|
||||
<kbd>L</kbd>: 좌 스크롤
|
||||
@@ -54,9 +57,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -84,9 +87,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
</pre>
|
||||
@@ -139,7 +142,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: 이전 hunk를 선택
|
||||
<kbd><right></kbd>: 다음 hunk를 선택
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
@@ -155,7 +157,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: 이전 hunk를 선택
|
||||
<kbd><right></kbd>: 다음 hunk를 선택
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
@@ -188,6 +189,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>M</kbd>: 현재 브랜치에 병합
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: 태그를 생성
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>R</kbd>: 브랜치 이름 변경
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -241,6 +243,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
@@ -259,12 +262,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>R</kbd>: 에디터에서 커밋메시지 수정
|
||||
<kbd>d</kbd>: 커밋 삭제
|
||||
<kbd>e</kbd>: 커밋을 편집
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: Pick commit (when mid-rebase)
|
||||
<kbd>F</kbd>: Create fixup commit for this commit
|
||||
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
|
||||
<kbd><c-j></kbd>: 커밋을 1개 아래로 이동
|
||||
<kbd><c-k></kbd>: 커밋을 1개 위로 이동
|
||||
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
|
||||
<kbd>V</kbd>: 커밋을 붙여넣기 (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Amend commit with staged changes
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
@@ -277,8 +281,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
</pre>
|
||||
@@ -291,6 +295,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -325,10 +330,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: View 'discard changes' options
|
||||
<kbd><space></kbd>: Staged 전환
|
||||
<kbd><c-b></kbd>: 파일을 필터하기 (Staged/unstaged)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 커밋 변경내용
|
||||
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: 마지맛 커밋 수정
|
||||
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>i</kbd>: Ignore file
|
||||
@@ -340,6 +347,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: View reset options
|
||||
<kbd>`</kbd>: 파일 트리뷰로 전환
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Git mergetool를 열기
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Sneltoetsen
|
||||
|
||||
@@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>.</kbd>: Volgende pagina
|
||||
<kbd><</kbd>: Scroll naar boven
|
||||
<kbd>></kbd>: Scroll naar beneden
|
||||
<kbd>v</kbd>: Toggle drag selecteer
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
<kbd>H</kbd>: Scroll left
|
||||
<kbd>L</kbd>: Scroll right
|
||||
@@ -51,10 +54,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Bekijk 'veranderingen ongedaan maken' opties
|
||||
<kbd><space></kbd>: Toggle staged
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Commit veranderingen
|
||||
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
|
||||
<kbd>A</kbd>: Wijzig laatste commit
|
||||
<kbd>C</kbd>: Commit veranderingen met de git editor
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Verander bestand
|
||||
<kbd>o</kbd>: Open bestand
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
@@ -66,6 +71,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: Bekijk upstream reset opties
|
||||
<kbd>D</kbd>: Bekijk reset opties
|
||||
<kbd>`</kbd>: Toggle bestandsboom weergave
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
@@ -95,6 +101,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>M</kbd>: Merge in met huidige checked out branch
|
||||
<kbd>f</kbd>: Fast-forward deze branch vanaf zijn upstream
|
||||
<kbd>T</kbd>: Creëer tag
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>R</kbd>: Hernoem branch
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -118,6 +125,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Uitsluit deze commit zijn veranderingen aan dit bestand
|
||||
<kbd>o</kbd>: Open bestand
|
||||
<kbd>e</kbd>: Verander bestand
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle bestand inbegrepen in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter bestand om geselecteerde regels toe te voegen aan de patch
|
||||
@@ -137,12 +145,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>R</kbd>: Hernoem commit met editor
|
||||
<kbd>d</kbd>: Verwijder commit
|
||||
<kbd>e</kbd>: Wijzig commit
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: Kies commit (wanneer midden in rebase)
|
||||
<kbd>F</kbd>: Creëer fixup commit
|
||||
<kbd>S</kbd>: Squash bovenstaande commits
|
||||
<kbd><c-j></kbd>: Verplaats commit 1 naar beneden
|
||||
<kbd><c-k></kbd>: Verplaats commit 1 naar boven
|
||||
<kbd>v</kbd>: Plak commits (cherry-pick)
|
||||
<kbd>V</kbd>: Plak commits (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Wijzig commit met staged veranderingen
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
@@ -155,8 +164,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Creëer nieuwe branch van commit
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
</pre>
|
||||
@@ -198,7 +207,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: Selecteer de vorige hunk
|
||||
<kbd><right></kbd>: Selecteer de volgende hunk
|
||||
<kbd>v</kbd>: Toggle drag selecteer
|
||||
<kbd>V</kbd>: Toggle drag selecteer
|
||||
<kbd>a</kbd>: Toggle selecteer hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open bestand
|
||||
@@ -218,9 +226,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Creëer nieuwe branch van commit
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Bekijk commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -235,6 +243,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>r</kbd>: Rebase branch
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Stel in als upstream van uitgecheckte branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Bekijk commits
|
||||
@@ -257,7 +266,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: Selecteer de vorige hunk
|
||||
<kbd><right></kbd>: Selecteer de volgende hunk
|
||||
<kbd>v</kbd>: Toggle drag selecteer
|
||||
<kbd>V</kbd>: Toggle drag selecteer
|
||||
<kbd>a</kbd>: Toggle selecteer hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open bestand
|
||||
@@ -306,9 +314,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Creëer nieuwe branch van commit
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
@@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>.</kbd>: Next page
|
||||
<kbd><</kbd>: Scroll to top
|
||||
<kbd>></kbd>: Scroll to bottom
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
<kbd>H</kbd>: Scroll left
|
||||
<kbd>L</kbd>: Scroll right
|
||||
@@ -63,12 +66,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>R</kbd>: Zmień nazwę commita w edytorze
|
||||
<kbd>d</kbd>: Usuń commit
|
||||
<kbd>e</kbd>: Edytuj commit
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: Wybierz commit (podczas zmiany bazy)
|
||||
<kbd>F</kbd>: Utwórz commit naprawczy dla tego commita
|
||||
<kbd>S</kbd>: Spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash)
|
||||
<kbd><c-j></kbd>: Przenieś commit 1 w dół
|
||||
<kbd><c-k></kbd>: Przenieś commit 1 w górę
|
||||
<kbd>v</kbd>: Wklej commity (przebieranie)
|
||||
<kbd>V</kbd>: Wklej commity (przebieranie)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Popraw commit zmianami z poczekalni
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
@@ -81,8 +85,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Create new branch off of commit
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
@@ -111,6 +115,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>M</kbd>: Scal do obecnej gałęzi
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: Create tag
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>R</kbd>: Rename branch
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -124,8 +129,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><left></kbd>: Poprzedni kawałek
|
||||
<kbd><right></kbd>: Następny kawałek
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
@@ -150,10 +154,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Pokaż opcje porzucania zmian
|
||||
<kbd><space></kbd>: Przełącz stan poczekalni
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Zatwierdź zmiany
|
||||
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
|
||||
<kbd>A</kbd>: Zmień ostatni commit
|
||||
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Edytuj plik
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
@@ -165,6 +171,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: Wyświetl opcje resetu
|
||||
<kbd>`</kbd>: Toggle file tree view
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||
<kbd>f</kbd>: Pobierz
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
@@ -178,6 +185,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Porzuć zmiany commita dla tego pliku
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
<kbd>e</kbd>: Edytuj plik
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -190,8 +198,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><left></kbd>: Poprzedni kawałek
|
||||
<kbd><right></kbd>: Następny kawałek
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
@@ -217,9 +224,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Create new branch off of commit
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -234,6 +241,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>r</kbd>: Zmiana bazy gałęzi
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
@@ -299,9 +307,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
<kbd>n</kbd>: Create new branch off of commit
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Связки клавиш
|
||||
|
||||
@@ -37,6 +37,9 @@ _Связки клавиш_
|
||||
<kbd>.</kbd>: Следующая страница
|
||||
<kbd><</kbd>: Пролистать наверх
|
||||
<kbd>></kbd>: Прокрутить вниз
|
||||
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Найти
|
||||
<kbd>H</kbd>: Прокрутить влево
|
||||
<kbd>L</kbd>: Прокрутить вправо
|
||||
@@ -61,7 +64,6 @@ _Связки клавиш_
|
||||
<kbd><left></kbd>: Выбрать предыдущую часть
|
||||
<kbd><right></kbd>: Выбрать следующую часть
|
||||
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>V</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>a</kbd>: Переключить выборку частей
|
||||
<kbd><c-o></kbd>: Скопировать выделенный текст в буфер обмена
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
@@ -106,7 +108,6 @@ _Связки клавиш_
|
||||
<kbd><left></kbd>: Выбрать предыдущую часть
|
||||
<kbd><right></kbd>: Выбрать следующую часть
|
||||
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>V</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>a</kbd>: Переключить выборку частей
|
||||
<kbd><c-o></kbd>: Скопировать выделенный текст в буфер обмена
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
@@ -126,9 +127,9 @@ _Связки клавиш_
|
||||
<kbd>o</kbd>: Открыть коммит в браузере
|
||||
<kbd>n</kbd>: Создать новую ветку с этого коммита
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd><c-r></kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Просмотреть коммиты
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -145,12 +146,13 @@ _Связки клавиш_
|
||||
<kbd>R</kbd>: Переписать коммит с помощью редактора
|
||||
<kbd>d</kbd>: Удалить коммит
|
||||
<kbd>e</kbd>: Изменить коммит
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: Выбрать коммит (в середине перебазирования)
|
||||
<kbd>F</kbd>: Создать fixup коммит для этого коммита
|
||||
<kbd>S</kbd>: Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение)
|
||||
<kbd><c-j></kbd>: Переместить коммит вниз на один
|
||||
<kbd><c-k></kbd>: Переместить коммит вверх на один
|
||||
<kbd>v</kbd>: Вставить отобранные коммиты (cherry-pick)
|
||||
<kbd>V</kbd>: Вставить отобранные коммиты (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Править последний коммит с проиндексированными изменениями
|
||||
<kbd>a</kbd>: Установить/убрать автора коммита
|
||||
@@ -163,8 +165,8 @@ _Связки клавиш_
|
||||
<kbd>o</kbd>: Открыть коммит в браузере
|
||||
<kbd>n</kbd>: Создать новую ветку с этого коммита
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||
<kbd>/</kbd>: Найти
|
||||
</pre>
|
||||
@@ -186,6 +188,7 @@ _Связки клавиш_
|
||||
<kbd>M</kbd>: Слияние с текущей переключённой веткой
|
||||
<kbd>f</kbd>: Перемотать эту ветку вперёд из её upstream-ветки
|
||||
<kbd>T</kbd>: Создать тег
|
||||
<kbd>s</kbd>: Порядок сортировки
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>R</kbd>: Переименовать ветку
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -219,9 +222,9 @@ _Связки клавиш_
|
||||
<kbd>o</kbd>: Открыть коммит в браузере
|
||||
<kbd>n</kbd>: Создать новую ветку с этого коммита
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd><c-r></kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||
<kbd>/</kbd>: Найти
|
||||
</pre>
|
||||
@@ -256,6 +259,7 @@ _Связки клавиш_
|
||||
<kbd>d</kbd>: Отменить изменения коммита в этом файле
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
<kbd>e</kbd>: Редактировать файл
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Переключить файлы включённые в патч
|
||||
<kbd>a</kbd>: Переключить все файлы, включённые в патч
|
||||
<kbd><enter></kbd>: Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения)
|
||||
@@ -296,6 +300,7 @@ _Связки клавиш_
|
||||
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Установить как upstream-ветку переключённую ветку
|
||||
<kbd>s</kbd>: Порядок сортировки
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Просмотреть коммиты
|
||||
@@ -319,10 +324,12 @@ _Связки клавиш_
|
||||
<kbd>d</kbd>: Просмотреть параметры «отмены изменении»
|
||||
<kbd><space></kbd>: Переключить индекс
|
||||
<kbd><c-b></kbd>: Фильтровать файлы (проиндексированные/непроиндексированные)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Сохранить изменения
|
||||
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
|
||||
<kbd>A</kbd>: Правка последнего коммита
|
||||
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Редактировать файл
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
<kbd>i</kbd>: Игнорировать или исключить файл
|
||||
@@ -334,6 +341,7 @@ _Связки клавиш_
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса upstream-ветки
|
||||
<kbd>D</kbd>: Просмотреть параметры сброса
|
||||
<kbd>`</kbd>: Переключить вид дерева файлов
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
|
||||
<kbd>f</kbd>: Получить изменения
|
||||
<kbd>/</kbd>: Найти
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit 按键绑定
|
||||
|
||||
@@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>.</kbd>: 下一页
|
||||
<kbd><</kbd>: 滚动到顶部
|
||||
<kbd>></kbd>: 滚动到底部
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
<kbd>H</kbd>: 向左滚动
|
||||
<kbd>L</kbd>: 向右滚动
|
||||
@@ -54,9 +57,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
<kbd>n</kbd>: 从提交创建新分支
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd>C</kbd>: 复制提交(拣选)
|
||||
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -89,6 +92,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>M</kbd>: 合并到当前检出的分支
|
||||
<kbd>f</kbd>: 从上游快进此分支
|
||||
<kbd>T</kbd>: 创建标签
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>R</kbd>: 重命名分支
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -107,9 +111,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
<kbd>n</kbd>: 从提交创建新分支
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd>C</kbd>: 复制提交(拣选)
|
||||
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
</pre>
|
||||
@@ -141,12 +145,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>R</kbd>: 使用编辑器重命名提交
|
||||
<kbd>d</kbd>: 删除提交
|
||||
<kbd>e</kbd>: 编辑提交
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: 选择提交(变基过程中)
|
||||
<kbd>F</kbd>: 创建修正提交
|
||||
<kbd>S</kbd>: 压缩在所选提交之上的所有“fixup!”提交(自动压缩)
|
||||
<kbd><c-j></kbd>: 下移提交
|
||||
<kbd><c-k></kbd>: 上移提交
|
||||
<kbd>v</kbd>: 粘贴提交(拣选)
|
||||
<kbd>V</kbd>: 粘贴提交(拣选)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: 用已暂存的更改来修补提交
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
@@ -159,8 +164,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
<kbd>n</kbd>: 从提交创建新分支
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd>C</kbd>: 复制提交(拣选)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
</pre>
|
||||
@@ -173,6 +178,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: 放弃对此文件的提交更改
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: 补丁中包含的切换文件
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
|
||||
@@ -194,10 +200,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: 查看'放弃更改'选项
|
||||
<kbd><space></kbd>: 切换暂存状态
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 提交更改
|
||||
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
||||
<kbd>A</kbd>: 修补最后一次提交
|
||||
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>i</kbd>: 忽略文件
|
||||
@@ -209,6 +217,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: 查看上游重置选项
|
||||
<kbd>D</kbd>: 查看重置选项
|
||||
<kbd>`</kbd>: 切换文件树视图
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
|
||||
<kbd>f</kbd>: 抓取
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
@@ -220,7 +229,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: 选择上一个区块
|
||||
<kbd><right></kbd>: 选择下一个区块
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
@@ -265,7 +273,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><left></kbd>: 选择上一个区块
|
||||
<kbd><right></kbd>: 选择下一个区块
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
@@ -336,6 +343,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>r</kbd>: 将已检出的分支变基到该分支
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: 设置为检出分支的上游
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_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._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit 鍵盤快捷鍵
|
||||
|
||||
@@ -37,6 +37,9 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>.</kbd>: 下一頁
|
||||
<kbd><</kbd>: 捲動到頂部
|
||||
<kbd>></kbd>: 捲動到底部
|
||||
<kbd>v</kbd>: 切換拖曳選擇
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
<kbd>H</kbd>: 向左捲動
|
||||
<kbd>L</kbd>: 向右捲動
|
||||
@@ -54,9 +57,9 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||
<kbd>n</kbd>: 從提交建立新分支
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>c</kbd>: 複製提交 (揀選)
|
||||
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||
<kbd>C</kbd>: 複製提交 (揀選)
|
||||
<kbd><c-r></kbd>: 重設選定的揀選 (複製) 提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 檢視提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -101,7 +104,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd><left></kbd>: 選擇上一段
|
||||
<kbd><right></kbd>: 選擇下一段
|
||||
<kbd>v</kbd>: 切換拖曳選擇
|
||||
<kbd>V</kbd>: 切換拖曳選擇
|
||||
<kbd>a</kbd>: 切換選擇程式碼塊
|
||||
<kbd><c-o></kbd>: 複製所選文本至剪貼簿
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
@@ -123,7 +125,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd><left></kbd>: 選擇上一段
|
||||
<kbd><right></kbd>: 選擇下一段
|
||||
<kbd>v</kbd>: 切換拖曳選擇
|
||||
<kbd>V</kbd>: 切換拖曳選擇
|
||||
<kbd>a</kbd>: 切換選擇程式碼塊
|
||||
<kbd><c-o></kbd>: 複製所選文本至剪貼簿
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
@@ -151,9 +152,9 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||
<kbd>n</kbd>: 從提交建立新分支
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>c</kbd>: 複製提交 (揀選)
|
||||
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||
<kbd>C</kbd>: 複製提交 (揀選)
|
||||
<kbd><c-r></kbd>: 重設選定的揀選 (複製) 提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
</pre>
|
||||
@@ -185,12 +186,13 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>R</kbd>: 使用編輯器改寫提交
|
||||
<kbd>d</kbd>: 刪除提交
|
||||
<kbd>e</kbd>: 編輯提交
|
||||
<kbd>i</kbd>: Start interactive rebase
|
||||
<kbd>p</kbd>: 挑選提交 (於變基過程中)
|
||||
<kbd>F</kbd>: 為此提交建立修復提交
|
||||
<kbd>S</kbd>: 壓縮上方所有的“fixup!”提交 (自動壓縮)
|
||||
<kbd><c-j></kbd>: 向下移動提交
|
||||
<kbd><c-k></kbd>: 向上移動提交
|
||||
<kbd>v</kbd>: 貼上提交 (揀選)
|
||||
<kbd>V</kbd>: 貼上提交 (揀選)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: 使用已預存的更改修正提交
|
||||
<kbd>a</kbd>: 設置/重設提交作者
|
||||
@@ -203,8 +205,8 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||
<kbd>n</kbd>: 從提交建立新分支
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>c</kbd>: 複製提交 (揀選)
|
||||
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||
<kbd>C</kbd>: 複製提交 (揀選)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
</pre>
|
||||
@@ -224,6 +226,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>d</kbd>: 捨棄此提交對此檔案的更改
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
<kbd>e</kbd>: 編輯檔案
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: 切換檔案是否包含在補丁中
|
||||
<kbd>a</kbd>: 切換所有檔案是否包含在補丁中
|
||||
<kbd><enter></kbd>: 輸入檔案以將選定的行添加至補丁(或切換目錄折疊)
|
||||
@@ -261,6 +264,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>M</kbd>: 合併到當前檢出的分支
|
||||
<kbd>f</kbd>: 從上游快進此分支
|
||||
<kbd>T</kbd>: 建立標籤
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>R</kbd>: 重新命名分支
|
||||
<kbd>u</kbd>: View upstream options
|
||||
@@ -289,10 +293,12 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>d</kbd>: 檢視“捨棄更改”的選項
|
||||
<kbd><space></kbd>: 切換預存
|
||||
<kbd><c-b></kbd>: 篩選檔案 (預存/未預存)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 提交變更
|
||||
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
|
||||
<kbd>A</kbd>: 修正上次提交
|
||||
<kbd>C</kbd>: 使用 git 編輯器提交變更
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 編輯檔案
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
<kbd>i</kbd>: 忽略或排除檔案
|
||||
@@ -304,6 +310,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>g</kbd>: 檢視上游重設選項
|
||||
<kbd>D</kbd>: 檢視重設選項
|
||||
<kbd>`</kbd>: 切換檔案樹狀視圖
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
|
||||
<kbd>f</kbd>: 擷取
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
@@ -346,6 +353,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>r</kbd>: 將已檢出的分支變基至此分支
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: 將此分支設為當前分支之上游
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 檢視提交
|
||||
|
||||
@@ -9,24 +9,25 @@ require (
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/fsmiamoto/git-todo-parser v0.0.5
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240103180601-96e29905643b
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230925062444-7cd0d7e2a70a
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240118234343-2d41754af383
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
|
||||
github.com/kyokomi/emoji/v2 v2.2.8
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/mattn/go-runewidth v0.0.15
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
github.com/samber/lo v1.31.0
|
||||
github.com/sanity-io/litter v1.5.2
|
||||
@@ -34,8 +35,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spf13/afero v1.9.5
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
@@ -43,6 +43,8 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/fatih/color v1.9.0 // indirect
|
||||
@@ -52,24 +54,28 @@ require (
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/invopop/jsonschema v0.10.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/onsi/gomega v1.7.1 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/term v0.12.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
@@ -51,6 +51,10 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn
|
||||
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
|
||||
github.com/aybabtme/humanlog v0.4.1/go.mod h1:B0bnQX4FTSU3oftPMTTPvENCy8LqixLDvYJA9TUCAGo=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
@@ -85,6 +89,8 @@ github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLI
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240103180601-96e29905643b h1:VkiXff8uJUkhjcxcLwwzQLYBCc+k5tJeOVx4W0qMYTk=
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240103180601-96e29905643b/go.mod h1:hl/KtAANGBecfIPxk+FzKvThTqI84oplgbPEmVX60b8=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
@@ -173,14 +179,16 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
|
||||
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
|
||||
github.com/invopop/jsonschema v0.10.0 h1:c1ktzNLBun3LyQQhyty5WE3lulbOdIIyOVlkmDLehcE=
|
||||
github.com/invopop/jsonschema v0.10.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8TIcC6Y4RI+1ZbJDOHfGJ570tPeYVCqo7/tws=
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230925062444-7cd0d7e2a70a h1:/Ifbvq4BWY5VKoyPnHIW5oRLiy00S3ebFtVIY2MlJm0=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230925062444-7cd0d7e2a70a/go.mod h1:trXE7RRGL2hTsv+Ntk+SHLtRobg9JE138n3Ug/X2Cf4=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240118234343-2d41754af383 h1:twcgVo+K7UTXwrsNtlCvTi8AyCp7CuBX//+j4wWkivQ=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240118234343-2d41754af383/go.mod h1:9zkyjnUmdL3+sUknJrQy/3HweUu8mVln/3J2wRF/l8M=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||
@@ -190,10 +198,13 @@ github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible/go.mod h1:w0xGhOSIJCGYYW+hnFPTutCy5aACpkcwbmORt5axGqk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3 h1:s995u+gNQADMaixtNOs+jilRC/Q78q0UXSI7+4T0cDE=
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3/go.mod h1:MCbEh21gjOzxc31udr3u4QM9DAdf8TFJCZz3u5hYIxA=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@@ -214,6 +225,8 @@ github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqU
|
||||
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
@@ -223,7 +236,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
@@ -270,12 +282,11 @@ github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68 h1:NSTj9xAKUu85d6pAdNFyblL84BfiOB1rVnzxQO/cYUk=
|
||||
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68/go.mod h1:PuJ7T6QKbsU7iVOHlhRoV3D/ipIAJsyiV4dbwcVaYj8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -283,10 +294,13 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
@@ -349,6 +363,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -383,6 +398,7 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -405,6 +421,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -452,14 +469,15 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -469,8 +487,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -522,6 +540,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
+4
-3
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/logs"
|
||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||
)
|
||||
@@ -42,7 +43,7 @@ func Run(
|
||||
common *common.Common,
|
||||
startArgs appTypes.StartArgs,
|
||||
) {
|
||||
app, err := NewApp(config, common)
|
||||
app, err := NewApp(config, startArgs.IntegrationTest, common)
|
||||
|
||||
if err == nil {
|
||||
err = app.Run(startArgs)
|
||||
@@ -94,7 +95,7 @@ func newLogger(cfg config.AppConfigurer) *logrus.Entry {
|
||||
}
|
||||
|
||||
// NewApp bootstrap a new application
|
||||
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
||||
func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest, common *common.Common) (*App, error) {
|
||||
app := &App{
|
||||
closers: []io.Closer{},
|
||||
Config: config,
|
||||
@@ -128,7 +129,7 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
||||
showRecentRepos = true
|
||||
}
|
||||
|
||||
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
|
||||
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName, test)
|
||||
if err != nil {
|
||||
return app, err
|
||||
}
|
||||
|
||||
@@ -180,10 +180,10 @@ func parseCliArgsAndEnvVars() *cliArgs {
|
||||
useConfigDir := ""
|
||||
flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory")
|
||||
|
||||
workTree := ""
|
||||
workTree := os.Getenv("GIT_WORK_TREE")
|
||||
flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
|
||||
|
||||
gitDir := ""
|
||||
gitDir := os.Getenv("GIT_DIR")
|
||||
flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
|
||||
|
||||
customConfigFile := ""
|
||||
|
||||
@@ -27,6 +27,10 @@ func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
|
||||
originalError: "fatal: not a git repository",
|
||||
newError: tr.NotARepository,
|
||||
},
|
||||
{
|
||||
originalError: "getwd: no such file or directory",
|
||||
newError: tr.WorkingDirectoryDoesNotExist,
|
||||
},
|
||||
}
|
||||
|
||||
if mapping, ok := lo.Find(mappings, func(mapping errorMapping) bool {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
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,10 +1,12 @@
|
||||
// This "script" generates a file called Keybindings_{{.LANG}}.md
|
||||
// in current working directory.
|
||||
//go:generate go run generator.go
|
||||
|
||||
// This "script" generates files called Keybindings_{{.LANG}}.md
|
||||
// in the docs/keybindings directory.
|
||||
//
|
||||
// The content of this generated file is a keybindings cheatsheet.
|
||||
// The content of these generated files is a keybindings cheatsheet.
|
||||
//
|
||||
// To generate cheatsheet in english run:
|
||||
// go run scripts/generate_cheatsheet.go
|
||||
// To generate the cheatsheets, run:
|
||||
// go generate pkg/cheatsheet/generate.go
|
||||
|
||||
package cheatsheet
|
||||
|
||||
@@ -42,7 +44,7 @@ type headerWithBindings struct {
|
||||
}
|
||||
|
||||
func CommandToRun() string {
|
||||
return "go run scripts/cheatsheet/main.go generate"
|
||||
return "go generate ./..."
|
||||
}
|
||||
|
||||
func GetKeybindingsDir() string {
|
||||
@@ -59,7 +61,7 @@ func generateAtDir(cheatsheetDir string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
mApp, _ := app.NewApp(mConfig, common)
|
||||
mApp, _ := app.NewApp(mConfig, nil, common)
|
||||
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
//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()
|
||||
}
|
||||
+4
-5
@@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
@@ -22,6 +21,7 @@ import (
|
||||
|
||||
// GitCommand is our main git interface
|
||||
type GitCommand struct {
|
||||
Blame *git_commands.BlameCommands
|
||||
Branch *git_commands.BranchCommands
|
||||
Commit *git_commands.CommitCommands
|
||||
Config *git_commands.ConfigCommands
|
||||
@@ -63,7 +63,6 @@ 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 {
|
||||
@@ -118,7 +117,6 @@ func NewGitCommand(
|
||||
gitConfig,
|
||||
repoPaths,
|
||||
repository,
|
||||
syncMutex,
|
||||
), nil
|
||||
}
|
||||
|
||||
@@ -129,7 +127,6 @@ func NewGitCommandAux(
|
||||
gitConfig git_config.IGitConfig,
|
||||
repoPaths *git_commands.RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) *GitCommand {
|
||||
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd)
|
||||
|
||||
@@ -140,7 +137,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, syncMutex)
|
||||
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands)
|
||||
|
||||
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
|
||||
statusCommands := git_commands.NewStatusCommands(gitCommon)
|
||||
@@ -164,6 +161,7 @@ func NewGitCommandAux(
|
||||
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
||||
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
||||
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
||||
blameCommands := git_commands.NewBlameCommands(gitCommon)
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
@@ -175,6 +173,7 @@ func NewGitCommandAux(
|
||||
tagLoader := git_commands.NewTagLoader(cmn, cmd)
|
||||
|
||||
return &GitCommand{
|
||||
Blame: blameCommands,
|
||||
Branch: branchCommands,
|
||||
Commit: commitCommands,
|
||||
Config: configCommands,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type BlameCommands struct {
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
|
||||
return &BlameCommands{
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
}
|
||||
|
||||
// Blame a range of lines. For each line, output the hash of the commit where
|
||||
// the line last changed, then a space, then a description of the commit (author
|
||||
// and date), another space, and then the line. For example:
|
||||
//
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 11) func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 12) return &BlameCommands{
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 13) GitCommon: gitCommon,
|
||||
func (self *BlameCommands) BlameLineRange(filename string, commit string, firstLine int, numLines int) (string, error) {
|
||||
cmdArgs := NewGitCmd("blame").
|
||||
Arg("-l").
|
||||
Arg(fmt.Sprintf("-L%d,+%d", firstLine, numLines)).
|
||||
Arg(commit).
|
||||
Arg("--").
|
||||
Arg(filename)
|
||||
|
||||
return self.cmd.New(cmdArgs.ToArgv()).RunWithOutput()
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package git_commands
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
@@ -62,33 +63,34 @@ func NewBranchLoader(
|
||||
func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch, error) {
|
||||
branches := self.obtainBranches()
|
||||
|
||||
reflogBranches := self.obtainReflogBranches(reflogCommits)
|
||||
|
||||
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
|
||||
branchesWithRecency := make([]*models.Branch, 0)
|
||||
outer:
|
||||
for _, reflogBranch := range reflogBranches {
|
||||
for j, branch := range branches {
|
||||
if branch.Head {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(reflogBranch.Name, branch.Name) {
|
||||
branch.Recency = reflogBranch.Recency
|
||||
branchesWithRecency = append(branchesWithRecency, branch)
|
||||
branches = utils.Remove(branches, j)
|
||||
continue outer
|
||||
if self.AppState.LocalBranchSortOrder == "recency" {
|
||||
reflogBranches := self.obtainReflogBranches(reflogCommits)
|
||||
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
|
||||
branchesWithRecency := make([]*models.Branch, 0)
|
||||
outer:
|
||||
for _, reflogBranch := range reflogBranches {
|
||||
for j, branch := range branches {
|
||||
if branch.Head {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(reflogBranch.Name, branch.Name) {
|
||||
branch.Recency = reflogBranch.Recency
|
||||
branchesWithRecency = append(branchesWithRecency, branch)
|
||||
branches = utils.Remove(branches, j)
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort branches that don't have a recency value alphabetically
|
||||
// (we're really doing this for the sake of deterministic behaviour across git versions)
|
||||
slices.SortFunc(branches, func(a *models.Branch, b *models.Branch) bool {
|
||||
return a.Name < b.Name
|
||||
})
|
||||
|
||||
branches = utils.Prepend(branches, branchesWithRecency...)
|
||||
}
|
||||
|
||||
// Sort branches that don't have a recency value alphabetically
|
||||
// (we're really doing this for the sake of deterministic behaviour across git versions)
|
||||
slices.SortFunc(branches, func(a *models.Branch, b *models.Branch) bool {
|
||||
return a.Name < b.Name
|
||||
})
|
||||
|
||||
branches = utils.Prepend(branches, branchesWithRecency...)
|
||||
|
||||
foundHead := false
|
||||
for i, branch := range branches {
|
||||
if branch.Head {
|
||||
@@ -144,7 +146,8 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return obtainBranch(split), true
|
||||
storeCommitDateAsRecency := self.AppState.LocalBranchSortOrder != "recency"
|
||||
return obtainBranch(split, storeCommitDateAsRecency), true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -156,8 +159,18 @@ func (self *BranchLoader) getRawBranches() (string, error) {
|
||||
"%00",
|
||||
)
|
||||
|
||||
var sortOrder string
|
||||
switch strings.ToLower(self.AppState.LocalBranchSortOrder) {
|
||||
case "recency", "date":
|
||||
sortOrder = "-committerdate"
|
||||
case "alphabetical":
|
||||
sortOrder = "refname"
|
||||
default:
|
||||
sortOrder = "refname"
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("for-each-ref").
|
||||
Arg("--sort=-committerdate").
|
||||
Arg(fmt.Sprintf("--sort=%s", sortOrder)).
|
||||
Arg(fmt.Sprintf("--format=%s", format)).
|
||||
Arg("refs/heads").
|
||||
ToArgv()
|
||||
@@ -172,22 +185,32 @@ var branchFields = []string{
|
||||
"upstream:track",
|
||||
"subject",
|
||||
"objectname",
|
||||
"committerdate:unix",
|
||||
}
|
||||
|
||||
// Obtain branch information from parsed line output of getRawBranches()
|
||||
func obtainBranch(split []string) *models.Branch {
|
||||
func obtainBranch(split []string, storeCommitDateAsRecency bool) *models.Branch {
|
||||
headMarker := split[0]
|
||||
fullName := split[1]
|
||||
upstreamName := split[2]
|
||||
track := split[3]
|
||||
subject := split[4]
|
||||
commitHash := split[5]
|
||||
commitDate := split[6]
|
||||
|
||||
name := strings.TrimPrefix(fullName, "heads/")
|
||||
pushables, pullables, gone := parseUpstreamInfo(upstreamName, track)
|
||||
|
||||
recency := ""
|
||||
if storeCommitDateAsRecency {
|
||||
if unixTimestamp, err := strconv.ParseInt(commitDate, 10, 64); err == nil {
|
||||
recency = utils.UnixToTimeAgo(unixTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.Branch{
|
||||
Name: name,
|
||||
Recency: recency,
|
||||
Pushables: pushables,
|
||||
Pullables: pullables,
|
||||
UpstreamGone: gone,
|
||||
|
||||
@@ -2,7 +2,9 @@ package git_commands
|
||||
|
||||
// "*|feat/detect-purge|origin/feat/detect-purge|[ahead 1]"
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -10,15 +12,21 @@ import (
|
||||
|
||||
func TestObtainBranch(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
input []string
|
||||
expectedBranch *models.Branch
|
||||
testName string
|
||||
input []string
|
||||
storeCommitDateAsRecency bool
|
||||
expectedBranch *models.Branch
|
||||
}
|
||||
|
||||
// Use a time stamp of 2 1/2 hours ago, resulting in a recency string of "2h"
|
||||
now := time.Now().Unix()
|
||||
timeStamp := strconv.Itoa(int(now - 2.5*60*60))
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "TrimHeads",
|
||||
input: []string{"", "heads/a_branch", "", "", "subject", "123"},
|
||||
testName: "TrimHeads",
|
||||
input: []string{"", "heads/a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "?",
|
||||
@@ -29,8 +37,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "NoUpstream",
|
||||
input: []string{"", "a_branch", "", "", "subject", "123"},
|
||||
testName: "NoUpstream",
|
||||
input: []string{"", "a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "?",
|
||||
@@ -41,8 +50,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "IsHead",
|
||||
input: []string{"*", "a_branch", "", "", "subject", "123"},
|
||||
testName: "IsHead",
|
||||
input: []string{"*", "a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "?",
|
||||
@@ -53,8 +63,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "IsBehindAndAhead",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123"},
|
||||
testName: "IsBehindAndAhead",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "3",
|
||||
@@ -65,8 +76,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "RemoteBranchIsGone",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123"},
|
||||
testName: "RemoteBranchIsGone",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
UpstreamGone: true,
|
||||
@@ -77,11 +89,25 @@ func TestObtainBranch(t *testing.T) {
|
||||
CommitHash: "123",
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "WithCommitDateAsRecency",
|
||||
input: []string{"", "a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: true,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Recency: "2h",
|
||||
Pushables: "?",
|
||||
Pullables: "?",
|
||||
Head: false,
|
||||
Subject: "subject",
|
||||
CommitHash: "123",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
branch := obtainBranch(s.input)
|
||||
branch := obtainBranch(s.input, s.storeCommitDateAsRecency)
|
||||
assert.EqualValues(t, s.expectedBranch, branch)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -137,15 +137,23 @@ func (self *CommitCommands) signoffFlag() string {
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-list").
|
||||
cmdArgs := NewGitCmd("log").
|
||||
Arg("--format=%B", "--max-count=1", commitSha).
|
||||
ToArgv()
|
||||
|
||||
messageWithHeader, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
|
||||
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return strings.TrimSpace(message), err
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitSubject(commitSha string) (string, error) {
|
||||
cmdArgs := NewGitCmd("log").
|
||||
Arg("--format=%s", "--max-count=1", commitSha).
|
||||
ToArgv()
|
||||
|
||||
subject, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return strings.TrimSpace(subject), err
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").Arg("--no-color", commitSha).ToArgv()
|
||||
|
||||
@@ -190,6 +198,20 @@ func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, e
|
||||
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
// Example output:
|
||||
//
|
||||
// cd50c79ae Preserve the commit message correctly even if the description has blank lines
|
||||
// 3ebba5f32 Add test demonstrating a bug with preserving the commit message
|
||||
// 9a423c388 Remove unused function
|
||||
func (self *CommitCommands) GetShasAndCommitMessagesFirstLine(shas []string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg("--no-patch", "--pretty=format:%h %s").
|
||||
Arg(shas...).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg("--no-patch", "--oneline").
|
||||
|
||||
@@ -260,19 +260,17 @@ func TestGetCommitMsg(t *testing.T) {
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"empty",
|
||||
` commit deadbeef`,
|
||||
``,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"no line breaks (single line)",
|
||||
`commit deadbeef
|
||||
use generics to DRY up context code`,
|
||||
`use generics to DRY up context code`,
|
||||
`use generics to DRY up context code`,
|
||||
},
|
||||
{
|
||||
"with line breaks",
|
||||
`commit deadbeef
|
||||
Merge pull request #1750 from mark2185/fix-issue-template
|
||||
`Merge pull request #1750 from mark2185/fix-issue-template
|
||||
|
||||
'git-rev parse' should be 'git rev-parse'`,
|
||||
`Merge pull request #1750 from mark2185/fix-issue-template
|
||||
@@ -285,7 +283,7 @@ Merge pull request #1750 from mark2185/fix-issue-template
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildCommitCommands(commonDeps{
|
||||
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
|
||||
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
|
||||
})
|
||||
|
||||
output, err := instance.GetCommitMessage("deadbeef")
|
||||
@@ -306,15 +304,14 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"Empty message",
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1"}, "", nil),
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"log", "--format=%B", "--max-count=1"}, "", nil),
|
||||
func(output string, err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Default case to retrieve a commit in history",
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "sha3"}, `commit sha3
|
||||
use generics to DRY up context code`, nil),
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"log", "--format=%B", "--max-count=1", "sha3"}, `use generics to DRY up context code`, nil),
|
||||
func(output string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "use generics to DRY up context code", output)
|
||||
|
||||
@@ -4,7 +4,6 @@ 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 {
|
||||
@@ -15,8 +14,6 @@ type GitCommon struct {
|
||||
repoPaths *RepoPaths
|
||||
repo *gogit.Repository
|
||||
config *ConfigCommands
|
||||
// mutex for doing things like push/pull/fetch
|
||||
syncMutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
func NewGitCommon(
|
||||
@@ -27,7 +24,6 @@ func NewGitCommon(
|
||||
repoPaths *RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
config *ConfigCommands,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) *GitCommon {
|
||||
return &GitCommon{
|
||||
Common: cmn,
|
||||
@@ -37,6 +33,5 @@ func NewGitCommon(
|
||||
repoPaths: repoPaths,
|
||||
repo: repo,
|
||||
config: config,
|
||||
syncMutex: syncMutex,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (self *ConfigCommands) GetPager(width int) string {
|
||||
"columnWidth": strconv.Itoa(width/2 - 6),
|
||||
}
|
||||
|
||||
pagerTemplate := self.UserConfig.Git.Paging.Pager
|
||||
pagerTemplate := string(self.UserConfig.Git.Paging.Pager)
|
||||
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,72 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *DiffCommands) internalDiffCmdObj(diffArgs ...string) *GitCommandBuilder {
|
||||
return NewGitCmd("diff").
|
||||
Arg("--no-ext-diff", "--no-color").
|
||||
Arg(diffArgs...)
|
||||
}
|
||||
|
||||
func (self *DiffCommands) GetPathDiff(path string, staged bool) (string, error) {
|
||||
return self.cmd.New(
|
||||
self.internalDiffCmdObj().
|
||||
ArgIf(staged, "--staged").
|
||||
Arg(path).
|
||||
ToArgv(),
|
||||
).RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *DiffCommands) GetAllDiff(staged bool) (string, error) {
|
||||
return self.cmd.New(
|
||||
self.internalDiffCmdObj().
|
||||
ArgIf(staged, "--staged").
|
||||
ToArgv(),
|
||||
).RunWithOutput()
|
||||
}
|
||||
|
||||
type DiffToolCmdOptions struct {
|
||||
// The path to show a diff for. Pass "." for the entire repo.
|
||||
Filepath string
|
||||
|
||||
// The commit against which to show the diff. Leave empty to show a diff of
|
||||
// the working copy.
|
||||
FromCommit string
|
||||
|
||||
// The commit to diff against FromCommit. Leave empty to diff the working
|
||||
// copy against FromCommit. Leave both FromCommit and ToCommit empty to show
|
||||
// the diff of the unstaged working copy changes against the index if Staged
|
||||
// is false, or the staged changes against HEAD if Staged is true.
|
||||
ToCommit string
|
||||
|
||||
// Whether to reverse the left and right sides of the diff.
|
||||
Reverse bool
|
||||
|
||||
// Whether the given Filepath is a directory. We'll pass --dir-diff to
|
||||
// git-difftool in that case.
|
||||
IsDirectory bool
|
||||
|
||||
// Whether to show the staged or the unstaged changes. Must be false if both
|
||||
// FromCommit and ToCommit are non-empty.
|
||||
Staged bool
|
||||
}
|
||||
|
||||
func (self *DiffCommands) OpenDiffToolCmdObj(opts DiffToolCmdOptions) oscommands.ICmdObj {
|
||||
return self.cmd.New(NewGitCmd("difftool").
|
||||
Arg("--no-prompt").
|
||||
ArgIf(opts.IsDirectory, "--dir-diff").
|
||||
ArgIf(opts.Staged, "--cached").
|
||||
ArgIf(opts.FromCommit != "", opts.FromCommit).
|
||||
ArgIf(opts.ToCommit != "", opts.ToCommit).
|
||||
ArgIf(opts.Reverse, "-R").
|
||||
Arg("--", opts.Filepath).
|
||||
ToArgv())
|
||||
}
|
||||
|
||||
func (self *DiffCommands) DiffIndexCmdObj(diffArgs ...string) oscommands.ICmdObj {
|
||||
return self.cmd.New(
|
||||
NewGitCmd("diff-index").
|
||||
Arg("--submodule", "--no-ext-diff", "--no-color", "--patch").
|
||||
Arg(diffArgs...).ToArgv(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -221,9 +221,9 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
|
||||
Arg("--interactive").
|
||||
Arg("--autostash").
|
||||
Arg("--keep-empty").
|
||||
ArgIf(opts.keepCommitsThatBecomeEmpty && !self.version.IsOlderThan(2, 26, 0), "--empty=keep").
|
||||
ArgIf(opts.keepCommitsThatBecomeEmpty && self.version.IsAtLeast(2, 26, 0), "--empty=keep").
|
||||
Arg("--no-autosquash").
|
||||
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
|
||||
ArgIf(self.version.IsAtLeast(2, 22, 0), "--rebase-merges").
|
||||
ArgIf(opts.onto != "", "--onto", opts.onto).
|
||||
Arg(opts.baseShaOrRoot).
|
||||
ToArgv()
|
||||
|
||||
@@ -2,6 +2,7 @@ package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
)
|
||||
@@ -53,7 +54,7 @@ func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName strin
|
||||
Arg(remoteName, "--delete", branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) DeleteRemoteTag(task gocui.Task, remoteName string, tagName string) error {
|
||||
@@ -61,7 +62,7 @@ func (self *RemoteCommands) DeleteRemoteTag(task gocui.Task, remoteName string,
|
||||
Arg(remoteName, "--delete", tagName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
// CheckRemoteBranchExists Returns remote branch
|
||||
@@ -74,3 +75,14 @@ func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Resolve what might be a aliased URL into a full URL
|
||||
// SEE: `man -P 'less +/--get-url +n' git-ls-remote`
|
||||
func (self *RemoteCommands) GetRemoteURL(remoteName string) (string, error) {
|
||||
cmdArgs := NewGitCmd("ls-remote").
|
||||
Arg("--get-url", remoteName).
|
||||
ToArgv()
|
||||
|
||||
url, err := self.cmd.New(cmdArgs).RunWithOutput()
|
||||
return strings.TrimSpace(url), err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -83,14 +84,23 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
||||
func (self *RemoteLoader) getRemoteBranchesByRemoteName() (map[string][]*models.RemoteBranch, error) {
|
||||
remoteBranchesByRemoteName := make(map[string][]*models.RemoteBranch)
|
||||
|
||||
cmdArgs := NewGitCmd("branch").Arg("-r").ToArgv()
|
||||
err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) {
|
||||
// excluding lines like 'origin/HEAD -> origin/master' (there will be a separate
|
||||
// line for 'origin/master')
|
||||
if strings.Contains(line, "->") {
|
||||
return false, nil
|
||||
}
|
||||
var sortOrder string
|
||||
switch strings.ToLower(self.AppState.RemoteBranchSortOrder) {
|
||||
case "alphabetical":
|
||||
sortOrder = "refname"
|
||||
case "date":
|
||||
sortOrder = "-committerdate"
|
||||
default:
|
||||
sortOrder = "refname"
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("for-each-ref").
|
||||
Arg(fmt.Sprintf("--sort=%s", sortOrder)).
|
||||
Arg("--format=%(refname:short)").
|
||||
Arg("refs/remotes").
|
||||
ToArgv()
|
||||
|
||||
err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
split := strings.SplitN(line, "/", 2)
|
||||
|
||||
@@ -32,14 +32,14 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
|
||||
return self.getUnfilteredStashEntries()
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "--name-only").ToArgv()
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--name-only", "--pretty=%ct|%gs").ToArgv()
|
||||
rawString, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return self.getUnfilteredStashEntries()
|
||||
}
|
||||
stashEntries := []*models.StashEntry{}
|
||||
var currentStashEntry *models.StashEntry
|
||||
lines := utils.SplitLines(rawString)
|
||||
lines := utils.SplitNul(rawString)
|
||||
isAStash := func(line string) bool { return strings.HasPrefix(line, "stash@{") }
|
||||
re := regexp.MustCompile(`stash@\{(\d+)\}`)
|
||||
|
||||
@@ -66,7 +66,7 @@ outer:
|
||||
}
|
||||
|
||||
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%ct|%gs").ToArgv()
|
||||
|
||||
rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return lo.Map(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||
@@ -75,8 +75,23 @@ func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
||||
}
|
||||
|
||||
func (c *StashLoader) stashEntryFromLine(line string, index int) *models.StashEntry {
|
||||
return &models.StashEntry{
|
||||
model := &models.StashEntry{
|
||||
Name: line,
|
||||
Index: index,
|
||||
}
|
||||
|
||||
tstr, msg, ok := strings.Cut(line, "|")
|
||||
if !ok {
|
||||
return model
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return model
|
||||
}
|
||||
|
||||
model.Name = msg
|
||||
model.Recency = utils.UnixToTimeAgo(t)
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ func TestGetStashEntries(t *testing.T) {
|
||||
"No stash entries found",
|
||||
"",
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"}, "", nil),
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"}, "", nil),
|
||||
[]*models.StashEntry{},
|
||||
},
|
||||
{
|
||||
"Several stash entries found",
|
||||
"",
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"},
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"},
|
||||
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
|
||||
nil,
|
||||
),
|
||||
|
||||
@@ -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).WithMutex(self.syncMutex)
|
||||
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task)
|
||||
return cmdObj, nil
|
||||
}
|
||||
|
||||
@@ -49,10 +49,16 @@ func (self *SyncCommands) Push(task gocui.Task, opts PushOpts) error {
|
||||
return cmdObj.Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) fetchCommandBuilder(fetchAll bool) *GitCommandBuilder {
|
||||
return NewGitCmd("fetch").
|
||||
ArgIf(fetchAll, "--all").
|
||||
// avoid writing to .git/FETCH_HEAD; this allows running a pull
|
||||
// concurrently without getting errors
|
||||
ArgIf(self.version.IsAtLeast(2, 29, 0), "--no-write-fetch-head")
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchCmdObj(task gocui.Task) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
ArgIf(self.UserConfig.Git.FetchAll, "--all").
|
||||
ToArgv()
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig.Git.FetchAll).ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs)
|
||||
cmdObj.PromptOnCredentialRequest(task)
|
||||
@@ -64,13 +70,10 @@ func (self *SyncCommands) Fetch(task gocui.Task) error {
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchBackgroundCmdObj() oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
ArgIf(self.UserConfig.Git.FetchAll, "--all").
|
||||
ToArgv()
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig.Git.FetchAll).ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs)
|
||||
cmdObj.DontLog().FailOnCredentialRequest()
|
||||
cmdObj.WithMutex(self.syncMutex)
|
||||
return cmdObj
|
||||
}
|
||||
|
||||
@@ -96,7 +99,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).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FastForward(
|
||||
@@ -105,18 +108,18 @@ func (self *SyncCommands) FastForward(
|
||||
remoteName string,
|
||||
remoteBranchName string,
|
||||
) error {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
cmdArgs := self.fetchCommandBuilder(false).
|
||||
Arg(remoteName).
|
||||
Arg(remoteBranchName + ":" + branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchRemote(task gocui.Task, remoteName string) error {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
cmdArgs := self.fetchCommandBuilder(false).
|
||||
Arg(remoteName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).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).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
@@ -69,3 +69,11 @@ func (v *GitVersion) IsOlderThan(major, minor, patch int) bool {
|
||||
func (v *GitVersion) IsOlderThanVersion(version *GitVersion) bool {
|
||||
return v.IsOlderThan(version.Major, version.Minor, version.Patch)
|
||||
}
|
||||
|
||||
func (v *GitVersion) IsAtLeast(major, minor, patch int) bool {
|
||||
return !v.IsOlderThan(major, minor, patch)
|
||||
}
|
||||
|
||||
func (v *GitVersion) IsAtLeastVersion(version *GitVersion) bool {
|
||||
return v.IsAtLeast(version.Major, version.Minor, version.Patch)
|
||||
}
|
||||
|
||||
@@ -45,3 +45,12 @@ func TestGitVersionIsOlderThan(t *testing.T) {
|
||||
assert.True(t, (&GitVersion{2, 0, 1, ""}).IsOlderThan(2, 1, 0))
|
||||
assert.True(t, (&GitVersion{2, 0, 1, ""}).IsOlderThan(3, 0, 0))
|
||||
}
|
||||
|
||||
func TestGitVersionIsAtLeast(t *testing.T) {
|
||||
assert.True(t, (&GitVersion{2, 0, 0, ""}).IsAtLeast(1, 99, 99))
|
||||
assert.True(t, (&GitVersion{2, 0, 0, ""}).IsAtLeast(2, 0, 0))
|
||||
assert.True(t, (&GitVersion{2, 1, 0, ""}).IsAtLeast(2, 0, 9))
|
||||
|
||||
assert.False(t, (&GitVersion{2, 0, 1, ""}).IsAtLeast(2, 1, 0))
|
||||
assert.False(t, (&GitVersion{2, 0, 1, ""}).IsAtLeast(3, 0, 0))
|
||||
}
|
||||
|
||||
@@ -31,10 +31,6 @@ func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New(NewGitCmd("mergetool").ToArgv())
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) OpenMergeTool() error {
|
||||
return self.OpenMergeToolCmdObj().Run()
|
||||
}
|
||||
|
||||
// StageFile stages a file
|
||||
func (self *WorkingTreeCommands) StageFile(path string) error {
|
||||
return self.StageFiles([]string{path})
|
||||
|
||||
@@ -99,32 +99,30 @@ func (self *HostingServiceMgr) getCandidateServiceDomains() []ServiceDomain {
|
||||
|
||||
serviceDomains := slices.Clone(defaultServiceDomains)
|
||||
|
||||
if len(self.configServiceDomains) > 0 {
|
||||
for gitDomain, typeAndDomain := range self.configServiceDomains {
|
||||
provider, webDomain, success := strings.Cut(typeAndDomain, ":")
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
// 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
|
||||
})
|
||||
|
||||
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,6 +65,10 @@ 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()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import "fmt"
|
||||
|
||||
// StashEntry : A git stash entry
|
||||
type StashEntry struct {
|
||||
Index int
|
||||
Name string
|
||||
Index int
|
||||
Recency string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s *StashEntry) FullRefName() string {
|
||||
|
||||
@@ -24,6 +24,10 @@ 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,11 +74,6 @@ 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.
|
||||
|
||||
@@ -14,11 +14,6 @@ type patchPresenter struct {
|
||||
// if true, all following fields are ignored
|
||||
plain bool
|
||||
|
||||
isFocused bool
|
||||
// first line index for selected cursor range
|
||||
firstLineIndex int
|
||||
// last line index for selected cursor range
|
||||
lastLineIndex int
|
||||
// line indices for tagged lines (e.g. lines added to a custom patch)
|
||||
incLineIndices *set.Set[int]
|
||||
}
|
||||
@@ -44,11 +39,6 @@ func formatRangePlain(patch *Patch, startIdx int, endIdx int) string {
|
||||
}
|
||||
|
||||
type FormatViewOpts struct {
|
||||
IsFocused bool
|
||||
// first line index for selected cursor range
|
||||
FirstLineIndex int
|
||||
// last line index for selected cursor range
|
||||
LastLineIndex int
|
||||
// line indices for tagged lines (e.g. lines added to a custom patch)
|
||||
IncLineIndices *set.Set[int]
|
||||
}
|
||||
@@ -63,9 +53,6 @@ func formatView(patch *Patch, opts FormatViewOpts) string {
|
||||
presenter := &patchPresenter{
|
||||
patch: patch,
|
||||
plain: false,
|
||||
isFocused: opts.IsFocused,
|
||||
firstLineIndex: opts.FirstLineIndex,
|
||||
lastLineIndex: opts.LastLineIndex,
|
||||
incLineIndices: includedLineIndices,
|
||||
}
|
||||
return presenter.format()
|
||||
@@ -112,7 +99,6 @@ func (self *patchPresenter) format() string {
|
||||
self.formatLineAux(
|
||||
hunk.headerContext,
|
||||
theme.DefaultTextColor,
|
||||
lineIdx,
|
||||
false,
|
||||
),
|
||||
)
|
||||
@@ -139,23 +125,17 @@ func (self *patchPresenter) patchLineStyle(patchLine *PatchLine) style.TextStyle
|
||||
func (self *patchPresenter) formatLine(str string, textStyle style.TextStyle, index int) string {
|
||||
included := self.incLineIndices.Includes(index)
|
||||
|
||||
return self.formatLineAux(str, textStyle, index, included)
|
||||
return self.formatLineAux(str, textStyle, included)
|
||||
}
|
||||
|
||||
// 'selected' means you've got it highlighted with your cursor
|
||||
// 'included' means the line has been included in the patch (only applicable when
|
||||
// building a patch)
|
||||
func (self *patchPresenter) formatLineAux(str string, textStyle style.TextStyle, index int, included bool) string {
|
||||
func (self *patchPresenter) formatLineAux(str string, textStyle style.TextStyle, included bool) string {
|
||||
if self.plain {
|
||||
return str
|
||||
}
|
||||
|
||||
selected := self.isFocused && index >= self.firstLineIndex && index <= self.lastLineIndex
|
||||
|
||||
if selected {
|
||||
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor)
|
||||
}
|
||||
|
||||
firstCharStyle := textStyle
|
||||
if included {
|
||||
firstCharStyle = firstCharStyle.MergeStyle(style.BgGreen)
|
||||
|
||||
@@ -197,9 +197,7 @@ func (p *PatchBuilder) RenderPatchForFile(filename string, plain bool, reverse b
|
||||
if plain {
|
||||
return patch.FormatPlain()
|
||||
} else {
|
||||
return patch.FormatView(FormatViewOpts{
|
||||
IsFocused: false,
|
||||
})
|
||||
return patch.FormatView(FormatViewOpts{})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -323,14 +323,18 @@ type AppState struct {
|
||||
HideCommandLog bool
|
||||
IgnoreWhitespaceInDiffView bool
|
||||
DiffContextSize int
|
||||
LocalBranchSortOrder string
|
||||
RemoteBranchSortOrder string
|
||||
}
|
||||
|
||||
func getDefaultAppState() *AppState {
|
||||
return &AppState{
|
||||
LastUpdateCheck: 0,
|
||||
RecentRepos: []string{},
|
||||
StartupPopupVersion: 0,
|
||||
DiffContextSize: 3,
|
||||
LastUpdateCheck: 0,
|
||||
RecentRepos: []string{},
|
||||
StartupPopupVersion: 0,
|
||||
DiffContextSize: 3,
|
||||
LocalBranchSortOrder: "recency",
|
||||
RemoteBranchSortOrder: "alphabetical",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,9 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
||||
openDirInEditorTemplate: `nvim --server "$NVIM" --remote-tab {{dir}}`,
|
||||
suspend: false,
|
||||
},
|
||||
"lvim": standardTerminalEditorPreset("lvim"),
|
||||
"emacs": standardTerminalEditorPreset("emacs"),
|
||||
"micro": standardTerminalEditorPreset("micro"),
|
||||
"nano": standardTerminalEditorPreset("nano"),
|
||||
"kakoune": standardTerminalEditorPreset("kak"),
|
||||
"helix": {
|
||||
|
||||
+315
-136
@@ -2,134 +2,272 @@ package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/karimkhaleel/jsonschema"
|
||||
)
|
||||
|
||||
type UserConfig struct {
|
||||
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"`
|
||||
// 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" jsonschema:"uniqueItems=true"`
|
||||
// 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" jsonschema:"enum=prompt,enum=create,enum=skip,enum=quit"`
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type RefresherConfig struct {
|
||||
RefreshInterval int `yaml:"refreshInterval"`
|
||||
FetchInterval int `yaml:"fetchInterval"`
|
||||
// File/submodule refresh interval in seconds.
|
||||
// Auto-refresh can be disabled via option 'git.autoRefresh'.
|
||||
RefreshInterval int `yaml:"refreshInterval" jsonschema:"minimum=0"`
|
||||
// Re-fetch interval in seconds.
|
||||
// Auto-fetch can be disabled via option 'git.autoFetch'.
|
||||
FetchInterval int `yaml:"fetchInterval" jsonschema:"minimum=0"`
|
||||
}
|
||||
|
||||
type GuiConfig struct {
|
||||
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"`
|
||||
// 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" jsonschema:"minimum=1"`
|
||||
// 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" jsonschema:"maximum=1,minimum=0"`
|
||||
// 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" jsonschema:"enum=horizontal,enum=flexible,enum=vertical"`
|
||||
// How the window is split when in half screen mode (i.e. after hitting '+' once).
|
||||
// Possible values:
|
||||
// - 'left': split the window horizontally (side panel on the left, main view on the right)
|
||||
// - 'top': split the window vertically (side panel on top, main view below)
|
||||
EnlargedSideViewLocation string `yaml:"enlargedSideViewLocation"`
|
||||
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`
|
||||
// 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" jsonschema:"enum=2,enum=3,enum="`
|
||||
// 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" jsonschema:"minimum=0"`
|
||||
// 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" jsonschema:"enum=auto,enum=always"`
|
||||
// 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" jsonschema:"enum=normal,enum=half,enum=full"`
|
||||
// Window border style.
|
||||
// One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
||||
Border string `yaml:"border" jsonschema:"enum=single,enum=double,enum=rounded,enum=hidden"`
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type ThemeConfig struct {
|
||||
ActiveBorderColor []string `yaml:"activeBorderColor"`
|
||||
InactiveBorderColor []string `yaml:"inactiveBorderColor"`
|
||||
SearchingActiveBorderColor []string `yaml:"searchingActiveBorderColor"`
|
||||
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"`
|
||||
// Border color of focused window
|
||||
ActiveBorderColor []string `yaml:"activeBorderColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Border color of non-focused windows
|
||||
InactiveBorderColor []string `yaml:"inactiveBorderColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Border color of focused window when searching in that window
|
||||
SearchingActiveBorderColor []string `yaml:"searchingActiveBorderColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Color of keybindings help text in the bottom line
|
||||
OptionsTextColor []string `yaml:"optionsTextColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Background color of selected line.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
|
||||
SelectedLineBgColor []string `yaml:"selectedLineBgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Foreground color of copied commit
|
||||
CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Background color of copied commit
|
||||
CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// 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" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Default text color
|
||||
DefaultFgColor []string `yaml:"defaultFgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
}
|
||||
|
||||
type CommitLengthConfig struct {
|
||||
// If true, show an indicator of commit message length
|
||||
Show bool `yaml:"show"`
|
||||
}
|
||||
|
||||
type GitConfig struct {
|
||||
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"`
|
||||
// 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" jsonschema:"uniqueItems=true"`
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type PagerType string
|
||||
|
||||
func (PagerType) JSONSchemaExtend(schema *jsonschema.Schema) {
|
||||
schema.Examples = []any{
|
||||
"delta --dark --paging=never",
|
||||
"diff-so-fancy",
|
||||
"ydiff -p cat -s --wrap --width={{columnWidth}}",
|
||||
}
|
||||
}
|
||||
|
||||
type PagingConfig struct {
|
||||
ColorArg string `yaml:"colorArg"`
|
||||
Pager string `yaml:"pager"`
|
||||
UseConfig bool `yaml:"useConfig"`
|
||||
// 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" jsonschema:"enum=always,enum=never"`
|
||||
// e.g.
|
||||
// diff-so-fancy
|
||||
// delta --dark --paging=never
|
||||
// ydiff -p cat -s --wrap --width={{columnWidth}}
|
||||
Pager PagerType `yaml:"pager" jsonschema:"minLength=1"`
|
||||
// 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'
|
||||
ExternalDiffCommand string `yaml:"externalDiffCommand"`
|
||||
}
|
||||
|
||||
type CommitConfig struct {
|
||||
// If true, pass '--signoff' flag when committing
|
||||
SignOff bool `yaml:"signOff"`
|
||||
}
|
||||
|
||||
type MergingConfig struct {
|
||||
ManualCommit bool `yaml:"manualCommit"`
|
||||
Args string `yaml:"args"`
|
||||
// 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" jsonschema:"example=--no-ff"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
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"`
|
||||
// One of: 'date-order' | 'author-date-order' | 'topo-order | default'
|
||||
// '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" jsonschema:"enum=date-order,enum=author-date-order,enum=topo-order,enum=default"`
|
||||
// This determines whether the git graph is rendered in the commits panel
|
||||
// One of 'always' | 'never' | 'when-maximised'
|
||||
ShowGraph string `yaml:"showGraph" jsonschema:"enum=always,enum=never,enum=when-maximised"`
|
||||
// displays the whole git graph by default in the commits view (equivalent to passing the `--all` argument to `git log`)
|
||||
ShowWholeGraph bool `yaml:"showWholeGraph"`
|
||||
}
|
||||
|
||||
type CommitPrefixConfig struct {
|
||||
Pattern string `yaml:"pattern"`
|
||||
Replace string `yaml:"replace"`
|
||||
// pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*"
|
||||
Pattern string `yaml:"pattern" jsonschema:"example=^\\w+\\/(\\w+-\\w+).*,minLength=1"`
|
||||
// Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] "
|
||||
Replace string `yaml:"replace" jsonschema:"example=[$1] ,minLength=1"`
|
||||
}
|
||||
|
||||
type UpdateConfig struct {
|
||||
Method string `yaml:"method"`
|
||||
Days int64 `yaml:"days"`
|
||||
// One of: 'prompt' (default) | 'background' | 'never'
|
||||
Method string `yaml:"method" jsonschema:"enum=prompt,enum=background,enum=never"`
|
||||
// Period in days between update checks
|
||||
Days int64 `yaml:"days" jsonschema:"minimum=0"`
|
||||
}
|
||||
|
||||
type KeybindingConfig struct {
|
||||
@@ -163,6 +301,9 @@ type KeybindingUniversalConfig struct {
|
||||
ScrollRight string `yaml:"scrollRight"`
|
||||
GotoTop string `yaml:"gotoTop"`
|
||||
GotoBottom string `yaml:"gotoBottom"`
|
||||
ToggleRangeSelect string `yaml:"toggleRangeSelect"`
|
||||
RangeSelectDown string `yaml:"rangeSelectDown"`
|
||||
RangeSelectUp string `yaml:"rangeSelectUp"`
|
||||
PrevBlock string `yaml:"prevBlock"`
|
||||
NextBlock string `yaml:"nextBlock"`
|
||||
PrevBlockAlt string `yaml:"prevBlock-alt"`
|
||||
@@ -211,6 +352,7 @@ type KeybindingUniversalConfig struct {
|
||||
ToggleWhitespaceInDiffView string `yaml:"toggleWhitespaceInDiffView"`
|
||||
IncreaseContextInDiffView string `yaml:"increaseContextInDiffView"`
|
||||
DecreaseContextInDiffView string `yaml:"decreaseContextInDiffView"`
|
||||
OpenDiffTool string `yaml:"openDiffTool"`
|
||||
}
|
||||
|
||||
type KeybindingStatusConfig struct {
|
||||
@@ -224,6 +366,7 @@ type KeybindingFilesConfig struct {
|
||||
CommitChangesWithoutHook string `yaml:"commitChangesWithoutHook"`
|
||||
AmendLastCommit string `yaml:"amendLastCommit"`
|
||||
CommitChangesWithEditor string `yaml:"commitChangesWithEditor"`
|
||||
FindBaseCommitForFixup string `yaml:"findBaseCommitForFixup"`
|
||||
ConfirmDiscard string `yaml:"confirmDiscard"`
|
||||
IgnoreFile string `yaml:"ignoreFile"`
|
||||
RefreshFiles string `yaml:"refreshFiles"`
|
||||
@@ -235,6 +378,7 @@ type KeybindingFilesConfig struct {
|
||||
ToggleTreeView string `yaml:"toggleTreeView"`
|
||||
OpenMergeTool string `yaml:"openMergeTool"`
|
||||
OpenStatusFilter string `yaml:"openStatusFilter"`
|
||||
CopyFileInfoToClipboard string `yaml:"copyFileInfoToClipboard"`
|
||||
}
|
||||
|
||||
type KeybindingBranchesConfig struct {
|
||||
@@ -252,6 +396,7 @@ type KeybindingBranchesConfig struct {
|
||||
PushTag string `yaml:"pushTag"`
|
||||
SetUpstream string `yaml:"setUpstream"`
|
||||
FetchRemote string `yaml:"fetchRemote"`
|
||||
SortOrder string `yaml:"sortOrder"`
|
||||
}
|
||||
|
||||
type KeybindingWorktreesConfig struct {
|
||||
@@ -273,7 +418,6 @@ type KeybindingCommitsConfig struct {
|
||||
PickCommit string `yaml:"pickCommit"`
|
||||
RevertCommit string `yaml:"revertCommit"`
|
||||
CherryPickCopy string `yaml:"cherryPickCopy"`
|
||||
CherryPickCopyRange string `yaml:"cherryPickCopyRange"`
|
||||
PasteCommits string `yaml:"pasteCommits"`
|
||||
MarkCommitAsBaseForRebase string `yaml:"markCommitAsBaseForRebase"`
|
||||
CreateTag string `yaml:"tagCommit"`
|
||||
@@ -283,6 +427,7 @@ type KeybindingCommitsConfig struct {
|
||||
OpenLogMenu string `yaml:"openLogMenu"`
|
||||
OpenInBrowser string `yaml:"openInBrowser"`
|
||||
ViewBisectOptions string `yaml:"viewBisectOptions"`
|
||||
StartInteractiveRebase string `yaml:"startInteractiveRebase"`
|
||||
}
|
||||
|
||||
type KeybindingStashConfig struct {
|
||||
@@ -295,11 +440,9 @@ type KeybindingCommitFilesConfig struct {
|
||||
}
|
||||
|
||||
type KeybindingMainConfig struct {
|
||||
ToggleDragSelect string `yaml:"toggleDragSelect"`
|
||||
ToggleDragSelectAlt string `yaml:"toggleDragSelect-alt"`
|
||||
ToggleSelectHunk string `yaml:"toggleSelectHunk"`
|
||||
PickBothHunks string `yaml:"pickBothHunks"`
|
||||
EditSelectHunk string `yaml:"editSelectHunk"`
|
||||
ToggleSelectHunk string `yaml:"toggleSelectHunk"`
|
||||
PickBothHunks string `yaml:"pickBothHunks"`
|
||||
EditSelectHunk string `yaml:"editSelectHunk"`
|
||||
}
|
||||
|
||||
type KeybindingSubmodulesConfig struct {
|
||||
@@ -335,7 +478,7 @@ type OSConfig struct {
|
||||
|
||||
// A built-in preset that sets all of the above settings. Supported presets
|
||||
// are defined in the getPreset function in editor_presets.go.
|
||||
EditPreset string `yaml:"editPreset,omitempty"`
|
||||
EditPreset string `yaml:"editPreset,omitempty" jsonschema:"example=vim,example=nvim,example=emacs,example=nano,example=vscode,example=sublime,example=kakoune,example=helix,example=xcode"`
|
||||
|
||||
// Command for opening a file, as if the file is double-clicked. Should
|
||||
// contain "{{filename}}", but doesn't support "{{line}}".
|
||||
@@ -367,7 +510,8 @@ type OSConfig struct {
|
||||
// Deprecated: use OpenLink instead.
|
||||
OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
|
||||
|
||||
// CopyToClipboardCmd is the command for copying to clipboard
|
||||
// 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 string `yaml:"copyToClipboardCmd,omitempty"`
|
||||
}
|
||||
|
||||
@@ -376,50 +520,79 @@ type CustomCommandAfterHook struct {
|
||||
}
|
||||
|
||||
type CustomCommand struct {
|
||||
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"`
|
||||
// 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" jsonschema:"enum=status,enum=files,enum=worktrees,enum=localBranches,enum=remotes,enum=remoteBranches,enum=tags,enum=commits,enum=reflogCommits,enum=subCommits,enum=commitFiles,enum=stash,enum=global"`
|
||||
// The command to run (using Go template syntax for placeholder values)
|
||||
Command string `yaml:"command" jsonschema:"example=git fetch {{.Form.Remote}} {{.Form.Branch}} && git checkout FETCH_HEAD"`
|
||||
// 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" jsonschema:"example=Loading..."`
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type CustomCommandPrompt struct {
|
||||
// one of 'input', 'menu', 'confirm', or 'menuFromCommand'
|
||||
Type string `yaml:"type"`
|
||||
Key string `yaml:"key"`
|
||||
// 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
|
||||
Title string `yaml:"title"`
|
||||
|
||||
// these only apply to input prompts
|
||||
InitialValue string `yaml:"initialValue"`
|
||||
Suggestions CustomCommandSuggestions `yaml:"suggestions"`
|
||||
// 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"`
|
||||
|
||||
// this only applies to confirm prompts
|
||||
Body string `yaml:"body"`
|
||||
// The message of the confirmation prompt.
|
||||
// Only for confirm prompts.
|
||||
Body string `yaml:"body" jsonschema:"example=Are you sure you want to push to the remote?"`
|
||||
|
||||
// this only applies to menus
|
||||
Options []CustomCommandMenuOption
|
||||
// Menu options.
|
||||
// Only for menu prompts.
|
||||
Options []CustomCommandMenuOption `yaml:"options"`
|
||||
|
||||
// this only applies to menuFromCommand
|
||||
Command string `yaml:"command"`
|
||||
Filter string `yaml:"filter"`
|
||||
ValueFormat string `yaml:"valueFormat"`
|
||||
LabelFormat string `yaml:"labelFormat"`
|
||||
// The command to run to generate menu options
|
||||
// Only for menuFromCommand prompts.
|
||||
Command string `yaml:"command" jsonschema:"example=git fetch {{.Form.Remote}} {{.Form.Branch}} && git checkout FETCH_HEAD"`
|
||||
// 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" jsonschema:"example=.*{{.SelectedRemote.Name }}/(?P<branch>.*)"`
|
||||
// How to format matched groups from the filter to construct a menu item's value.
|
||||
// Only for menuFromCommand prompts.
|
||||
ValueFormat string `yaml:"valueFormat" jsonschema:"example={{ .branch }}"`
|
||||
// Like valueFormat but for the labels. If `labelFormat` is not specified, `valueFormat` is shown instead.
|
||||
// Only for menuFromCommand prompts.
|
||||
LabelFormat string `yaml:"labelFormat" jsonschema:"example={{ .branch | green }}"`
|
||||
}
|
||||
|
||||
type CustomCommandSuggestions struct {
|
||||
Preset string `yaml:"preset"`
|
||||
Command string `yaml:"command"`
|
||||
// Uses built-in logic to obtain the suggestions. One of 'authors' | 'branches' | 'files' | 'refs' | 'remotes' | 'remoteBranches' | 'tags'
|
||||
Preset string `yaml:"preset" jsonschema:"enum=authors,enum=branches,enum=files,enum=refs,enum=remotes,enum=remoteBranches,enum=tags"`
|
||||
// Command to run such that each line in the output becomes a suggestion. Mutually exclusive with 'preset' field.
|
||||
Command string `yaml:"command" jsonschema:"example=git fetch {{.Form.Remote}} {{.Form.Branch}} && git checkout FETCH_HEAD"`
|
||||
}
|
||||
|
||||
type CustomCommandMenuOption struct {
|
||||
Name string `yaml:"name"`
|
||||
// The first part of the label
|
||||
Name string `yaml:"name"`
|
||||
// The second part of the label
|
||||
Description string `yaml:"description"`
|
||||
Value string `yaml:"value"`
|
||||
// The value that will be used in the command
|
||||
Value string `yaml:"value" jsonschema:"example=feature,minLength=1"`
|
||||
}
|
||||
|
||||
func GetDefaultConfig() *UserConfig {
|
||||
@@ -435,6 +608,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
SidePanelWidth: 0.3333,
|
||||
ExpandFocusedSidePanel: false,
|
||||
MainPanelSplitMode: "flexible",
|
||||
EnlargedSideViewLocation: "left",
|
||||
Language: "auto",
|
||||
TimeFormat: "02 Jan 06",
|
||||
ShortTimeFormat: time.Kitchen,
|
||||
@@ -444,7 +618,6 @@ func GetDefaultConfig() *UserConfig {
|
||||
InactiveBorderColor: []string{"default"},
|
||||
OptionsTextColor: []string{"blue"},
|
||||
SelectedLineBgColor: []string{"blue"},
|
||||
SelectedRangeBgColor: []string{"blue"},
|
||||
CherryPickedCommitBgColor: []string{"cyan"},
|
||||
CherryPickedCommitFgColor: []string{"blue"},
|
||||
MarkedBaseCommitBgColor: []string{"yellow"},
|
||||
@@ -468,6 +641,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "rounded",
|
||||
AnimateExplosion: true,
|
||||
PortraitMode: "auto",
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
@@ -526,6 +700,9 @@ func GetDefaultConfig() *UserConfig {
|
||||
ScrollRight: "L",
|
||||
GotoTop: "<",
|
||||
GotoBottom: ">",
|
||||
ToggleRangeSelect: "v",
|
||||
RangeSelectDown: "<s-down>",
|
||||
RangeSelectUp: "<s-up>",
|
||||
PrevBlock: "<left>",
|
||||
NextBlock: "<right>",
|
||||
PrevBlockAlt: "h",
|
||||
@@ -536,7 +713,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
NextMatch: "n",
|
||||
PrevMatch: "N",
|
||||
StartSearch: "/",
|
||||
OptionMenu: "",
|
||||
OptionMenu: "<disabled>",
|
||||
OptionMenuAlt1: "?",
|
||||
Select: "<space>",
|
||||
GoInto: "<enter>",
|
||||
@@ -574,6 +751,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
ToggleWhitespaceInDiffView: "<c-w>",
|
||||
IncreaseContextInDiffView: "}",
|
||||
DecreaseContextInDiffView: "{",
|
||||
OpenDiffTool: "<c-t>",
|
||||
},
|
||||
Status: KeybindingStatusConfig{
|
||||
CheckForUpdate: "u",
|
||||
@@ -585,6 +763,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
CommitChangesWithoutHook: "w",
|
||||
AmendLastCommit: "A",
|
||||
CommitChangesWithEditor: "C",
|
||||
FindBaseCommitForFixup: "<c-f>",
|
||||
IgnoreFile: "i",
|
||||
RefreshFiles: "r",
|
||||
StashAllChanges: "s",
|
||||
@@ -596,6 +775,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
OpenMergeTool: "M",
|
||||
OpenStatusFilter: "<c-b>",
|
||||
ConfirmDiscard: "x",
|
||||
CopyFileInfoToClipboard: "y",
|
||||
},
|
||||
Branches: KeybindingBranchesConfig{
|
||||
CopyPullRequestURL: "<c-y>",
|
||||
@@ -612,6 +792,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
PushTag: "P",
|
||||
SetUpstream: "u",
|
||||
FetchRemote: "f",
|
||||
SortOrder: "s",
|
||||
},
|
||||
Worktrees: KeybindingWorktreesConfig{
|
||||
ViewWorktreeOptions: "w",
|
||||
@@ -630,9 +811,8 @@ func GetDefaultConfig() *UserConfig {
|
||||
ResetCommitAuthor: "a",
|
||||
PickCommit: "p",
|
||||
RevertCommit: "t",
|
||||
CherryPickCopy: "c",
|
||||
CherryPickCopyRange: "C",
|
||||
PasteCommits: "v",
|
||||
CherryPickCopy: "C",
|
||||
PasteCommits: "V",
|
||||
MarkCommitAsBaseForRebase: "B",
|
||||
CreateTag: "T",
|
||||
CheckoutCommit: "<space>",
|
||||
@@ -641,6 +821,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
OpenLogMenu: "<c-l>",
|
||||
OpenInBrowser: "o",
|
||||
ViewBisectOptions: "b",
|
||||
StartInteractiveRebase: "i",
|
||||
},
|
||||
Stash: KeybindingStashConfig{
|
||||
PopStash: "g",
|
||||
@@ -650,11 +831,9 @@ func GetDefaultConfig() *UserConfig {
|
||||
CheckoutCommitFile: "c",
|
||||
},
|
||||
Main: KeybindingMainConfig{
|
||||
ToggleDragSelect: "v",
|
||||
ToggleDragSelectAlt: "V",
|
||||
ToggleSelectHunk: "a",
|
||||
PickBothHunks: "b",
|
||||
EditSelectHunk: "E",
|
||||
ToggleSelectHunk: "a",
|
||||
PickBothHunks: "b",
|
||||
EditSelectHunk: "E",
|
||||
},
|
||||
Submodules: KeybindingSubmodulesConfig{
|
||||
Init: "i",
|
||||
|
||||
@@ -20,10 +20,11 @@ type BaseContext struct {
|
||||
onFocusFn onFocusFn
|
||||
onFocusLostFn onFocusLostFn
|
||||
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
highlightOnFocus bool
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
needsRerenderOnWidthChange bool
|
||||
highlightOnFocus bool
|
||||
|
||||
*ParentContextMgr
|
||||
}
|
||||
@@ -36,14 +37,15 @@ type (
|
||||
var _ types.IBaseContext = &BaseContext{}
|
||||
|
||||
type NewBaseContextOpts struct {
|
||||
Kind types.ContextKind
|
||||
Key types.ContextKey
|
||||
View *gocui.View
|
||||
WindowName string
|
||||
Focusable bool
|
||||
Transient bool
|
||||
HasUncontrolledBounds bool // negating for the sake of making false the default
|
||||
HighlightOnFocus bool
|
||||
Kind types.ContextKind
|
||||
Key types.ContextKey
|
||||
View *gocui.View
|
||||
WindowName string
|
||||
Focusable bool
|
||||
Transient bool
|
||||
HasUncontrolledBounds bool // negating for the sake of making false the default
|
||||
HighlightOnFocus bool
|
||||
NeedsRerenderOnWidthChange bool
|
||||
|
||||
OnGetOptionsMap func() map[string]string
|
||||
}
|
||||
@@ -54,17 +56,18 @@ func NewBaseContext(opts NewBaseContextOpts) *BaseContext {
|
||||
hasControlledBounds := !opts.HasUncontrolledBounds
|
||||
|
||||
return &BaseContext{
|
||||
kind: opts.Kind,
|
||||
key: opts.Key,
|
||||
view: opts.View,
|
||||
windowName: opts.WindowName,
|
||||
onGetOptionsMap: opts.OnGetOptionsMap,
|
||||
focusable: opts.Focusable,
|
||||
transient: opts.Transient,
|
||||
hasControlledBounds: hasControlledBounds,
|
||||
highlightOnFocus: opts.HighlightOnFocus,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
viewTrait: viewTrait,
|
||||
kind: opts.Kind,
|
||||
key: opts.Key,
|
||||
view: opts.View,
|
||||
windowName: opts.WindowName,
|
||||
onGetOptionsMap: opts.OnGetOptionsMap,
|
||||
focusable: opts.Focusable,
|
||||
transient: opts.Transient,
|
||||
hasControlledBounds: hasControlledBounds,
|
||||
highlightOnFocus: opts.HighlightOnFocus,
|
||||
needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
viewTrait: viewTrait,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +193,10 @@ func (self *BaseContext) HasControlledBounds() bool {
|
||||
return self.hasControlledBounds
|
||||
}
|
||||
|
||||
func (self *BaseContext) NeedsRerenderOnWidthChange() bool {
|
||||
return self.needsRerenderOnWidthChange
|
||||
}
|
||||
|
||||
func (self *BaseContext) Title() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -27,8 +27,10 @@ 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.Views().Branches.Width(),
|
||||
c.Tr,
|
||||
c.UserConfig,
|
||||
c.Model().Worktrees,
|
||||
@@ -39,11 +41,12 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
FilteredListViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().Branches,
|
||||
WindowName: "branches",
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
View: c.Views().Branches,
|
||||
WindowName: "branches",
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -63,7 +63,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
}
|
||||
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
|
||||
ctx.GetList().SetSelectedLineIdx(selectedLineIdx)
|
||||
ctx.GetList().SetSelection(selectedLineIdx)
|
||||
return ctx.HandleFocus(types.OnFocusOpts{})
|
||||
}))
|
||||
|
||||
|
||||
+17
-11
@@ -31,11 +31,13 @@ const (
|
||||
MERGE_CONFLICTS_CONTEXT_KEY types.ContextKey = "mergeConflicts"
|
||||
|
||||
// these shouldn't really be needed for anything but I'm giving them unique keys nonetheless
|
||||
OPTIONS_CONTEXT_KEY types.ContextKey = "options"
|
||||
APP_STATUS_CONTEXT_KEY types.ContextKey = "appStatus"
|
||||
SEARCH_PREFIX_CONTEXT_KEY types.ContextKey = "searchPrefix"
|
||||
INFORMATION_CONTEXT_KEY types.ContextKey = "information"
|
||||
LIMIT_CONTEXT_KEY types.ContextKey = "limit"
|
||||
OPTIONS_CONTEXT_KEY types.ContextKey = "options"
|
||||
APP_STATUS_CONTEXT_KEY types.ContextKey = "appStatus"
|
||||
SEARCH_PREFIX_CONTEXT_KEY types.ContextKey = "searchPrefix"
|
||||
INFORMATION_CONTEXT_KEY types.ContextKey = "information"
|
||||
LIMIT_CONTEXT_KEY types.ContextKey = "limit"
|
||||
STATUS_SPACER1_CONTEXT_KEY types.ContextKey = "statusSpacer1"
|
||||
STATUS_SPACER2_CONTEXT_KEY types.ContextKey = "statusSpacer2"
|
||||
|
||||
MENU_CONTEXT_KEY types.ContextKey = "menu"
|
||||
CONFIRMATION_CONTEXT_KEY types.ContextKey = "confirmation"
|
||||
@@ -109,12 +111,14 @@ type ContextTree struct {
|
||||
CommandLog types.Context
|
||||
|
||||
// display contexts
|
||||
AppStatus types.Context
|
||||
Options types.Context
|
||||
SearchPrefix types.Context
|
||||
Search types.Context
|
||||
Information types.Context
|
||||
Limit types.Context
|
||||
AppStatus types.Context
|
||||
Options types.Context
|
||||
SearchPrefix types.Context
|
||||
Search types.Context
|
||||
Information types.Context
|
||||
Limit types.Context
|
||||
StatusSpacer1 types.Context
|
||||
StatusSpacer2 types.Context
|
||||
}
|
||||
|
||||
// the order of this decides which context is initially at the top of its window
|
||||
@@ -156,6 +160,8 @@ func (self *ContextTree) Flatten() []types.Context {
|
||||
self.Search,
|
||||
self.Information,
|
||||
self.Limit,
|
||||
self.StatusSpacer1,
|
||||
self.StatusSpacer2,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ func (self *FilteredList[T]) ClearFilter() {
|
||||
self.SetFilter("")
|
||||
}
|
||||
|
||||
func (self *FilteredList[T]) ReApplyFilter() {
|
||||
self.applyFilter()
|
||||
}
|
||||
|
||||
func (self *FilteredList[T]) IsFiltering() bool {
|
||||
return self.filter != ""
|
||||
}
|
||||
|
||||
@@ -31,5 +31,5 @@ func (self *FilteredListViewModel[T]) ClearFilter() {
|
||||
|
||||
self.FilteredList.ClearFilter()
|
||||
|
||||
self.SetSelectedLineIdx(unfilteredIndex)
|
||||
self.SetSelection(unfilteredIndex)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,14 @@ func (self *ListContextTrait) FocusLine() {
|
||||
self.GetViewTrait().FocusPoint(
|
||||
self.ModelIndexToViewIndex(self.list.GetSelectedLineIdx()))
|
||||
|
||||
selectRangeIndex, isSelectingRange := self.list.GetRangeStartIdx()
|
||||
if isSelectingRange {
|
||||
selectRangeIndex = self.ModelIndexToViewIndex(selectRangeIndex)
|
||||
self.GetViewTrait().SetRangeSelectStart(selectRangeIndex)
|
||||
} else {
|
||||
self.GetViewTrait().CancelRangeSelect()
|
||||
}
|
||||
|
||||
// If FocusPoint() caused the view to scroll (because the selected line
|
||||
// was out of view before), we need to rerender the view port again.
|
||||
// This can happen when pressing , or . to scroll by pages, or < or > to
|
||||
@@ -84,7 +92,7 @@ func (self *ListContextTrait) HandleFocusLost(opts types.OnFocusLostOpts) error
|
||||
|
||||
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
|
||||
func (self *ListContextTrait) HandleRender() error {
|
||||
self.list.RefreshSelectedIdx()
|
||||
self.list.ClampSelection()
|
||||
content := self.renderLines(-1, -1)
|
||||
self.GetViewTrait().SetContent(content)
|
||||
self.c.Render()
|
||||
@@ -94,6 +102,24 @@ func (self *ListContextTrait) HandleRender() error {
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
|
||||
self.GetList().SetSelectedLineIdx(selectedLineIdx)
|
||||
self.GetList().SetSelection(selectedLineIdx)
|
||||
return self.HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool {
|
||||
startIdx, length := self.GetViewTrait().ViewPortYBounds()
|
||||
selectionStart := self.ViewIndexToModelIndex(startIdx)
|
||||
selectionEnd := self.ViewIndexToModelIndex(startIdx + length)
|
||||
for i := selectionStart; i < selectionEnd; i++ {
|
||||
iterItem := self.GetList().GetItem(i)
|
||||
if iterItem != nil && iterItem.URN() == item.URN() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// By default, list contexts supporta range select
|
||||
func (self *ListContextTrait) RangeSelectEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package context
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/gui/context/traits"
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type ListViewModel[T any] struct {
|
||||
*traits.ListCursor
|
||||
@@ -36,3 +39,8 @@ func (self *ListViewModel[T]) GetItems() []T {
|
||||
func Zero[T any]() T {
|
||||
return *new(T)
|
||||
}
|
||||
|
||||
func (self *ListViewModel[T]) GetItem(index int) types.HasUrn {
|
||||
item := self.getModel()[index]
|
||||
return any(item).(types.HasUrn)
|
||||
}
|
||||
|
||||
@@ -68,11 +68,12 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
SearchTrait: NewSearchTrait(c),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().Commits,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
View: c.Views().Commits,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
@@ -84,7 +85,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
}
|
||||
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
|
||||
ctx.GetList().SetSelectedLineIdx(selectedLineIdx)
|
||||
ctx.GetList().SetSelection(selectedLineIdx)
|
||||
return ctx.HandleFocus(types.OnFocusOpts{})
|
||||
}))
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
|
||||
|
||||
return lo.Map(menuItems, func(item *types.MenuItem, _ int) []string {
|
||||
displayStrings := item.LabelColumns
|
||||
if item.DisabledReason != "" {
|
||||
if item.DisabledReason != nil {
|
||||
displayStrings[0] = style.FgDefault.SetStrikethrough().Sprint(displayStrings[0])
|
||||
}
|
||||
|
||||
@@ -172,8 +172,13 @@ func (self *MenuContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Bin
|
||||
}
|
||||
|
||||
func (self *MenuContext) OnMenuPress(selectedItem *types.MenuItem) error {
|
||||
if selectedItem != nil && selectedItem.DisabledReason != "" {
|
||||
return self.c.ErrorMsg(selectedItem.DisabledReason)
|
||||
if selectedItem != nil && selectedItem.DisabledReason != nil {
|
||||
if selectedItem.DisabledReason.ShowErrorInPanel {
|
||||
return self.c.ErrorMsg(selectedItem.DisabledReason.Text)
|
||||
}
|
||||
|
||||
self.c.ErrorToast(self.c.Tr.DisabledMenuItemPrefix + selectedItem.DisabledReason.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := self.c.PopContext(); err != nil {
|
||||
@@ -190,3 +195,8 @@ func (self *MenuContext) OnMenuPress(selectedItem *types.MenuItem) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// There is currently no need to use range-select in a menu so we're disabling it.
|
||||
func (self *MenuContext) RangeSelectEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ func (self *MergeConflictsContext) IsUserScrolling() bool {
|
||||
return self.viewModel.userVerticalScrolling
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) RenderAndFocus(isFocused bool) error {
|
||||
self.setContent(isFocused)
|
||||
func (self *MergeConflictsContext) RenderAndFocus() error {
|
||||
self.setContent()
|
||||
self.FocusSelection()
|
||||
|
||||
self.c.Render()
|
||||
@@ -77,30 +77,41 @@ func (self *MergeConflictsContext) RenderAndFocus(isFocused bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) Render(isFocused bool) error {
|
||||
self.setContent(isFocused)
|
||||
func (self *MergeConflictsContext) Render() error {
|
||||
self.setContent()
|
||||
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) GetContentToRender(isFocused bool) string {
|
||||
func (self *MergeConflictsContext) GetContentToRender() string {
|
||||
if self.GetState() == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return mergeconflicts.ColoredConflictFile(self.GetState(), isFocused)
|
||||
return mergeconflicts.ColoredConflictFile(self.GetState())
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) setContent(isFocused bool) {
|
||||
self.GetView().SetContent(self.GetContentToRender(isFocused))
|
||||
func (self *MergeConflictsContext) setContent() {
|
||||
self.GetView().SetContent(self.GetContentToRender())
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) FocusSelection() {
|
||||
if !self.IsUserScrolling() {
|
||||
_ = self.GetView().SetOriginY(self.GetOriginY())
|
||||
}
|
||||
|
||||
self.SetSelectedLineRange()
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) SetSelectedLineRange() {
|
||||
startIdx, endIdx := self.GetState().GetSelectedRange()
|
||||
view := self.GetView()
|
||||
originY := view.OriginY()
|
||||
// As far as the view is concerned, we are always selecting a range
|
||||
view.SetRangeSelectStart(startIdx)
|
||||
view.SetCursorY(endIdx - originY)
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) GetOriginY() int {
|
||||
|
||||
@@ -115,7 +115,10 @@ func (self *PatchExplorerContext) FocusSelection() {
|
||||
|
||||
_ = view.SetOriginY(newOriginY)
|
||||
|
||||
view.SetCursorY(state.GetSelectedLineIdx() - newOriginY)
|
||||
startIdx, endIdx := state.SelectedRange()
|
||||
// As far as the view is concerned, we are always selecting a range
|
||||
view.SetRangeSelectStart(startIdx)
|
||||
view.SetCursorY(endIdx - newOriginY)
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetContentToRender(isFocused bool) string {
|
||||
|
||||
@@ -43,11 +43,12 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||
FilteredListViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().ReflogCommits,
|
||||
WindowName: "commits",
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
View: c.Views().ReflogCommits,
|
||||
WindowName: "commits",
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -36,12 +36,13 @@ func NewRemoteBranchesContext(
|
||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().RemoteBranches,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
View: c.Views().RemoteBranches,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
type RemotesContext struct {
|
||||
*FilteredListViewModel[*models.Remote]
|
||||
*ListContextTrait
|
||||
*SearchHistory
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -138,10 +138,12 @@ func NewContextTree(c *ContextCommon) *ContextTree {
|
||||
Focusable: true,
|
||||
}),
|
||||
),
|
||||
Options: NewDisplayContext(OPTIONS_CONTEXT_KEY, c.Views().Options, "options"),
|
||||
AppStatus: NewDisplayContext(APP_STATUS_CONTEXT_KEY, c.Views().AppStatus, "appStatus"),
|
||||
SearchPrefix: NewDisplayContext(SEARCH_PREFIX_CONTEXT_KEY, c.Views().SearchPrefix, "searchPrefix"),
|
||||
Information: NewDisplayContext(INFORMATION_CONTEXT_KEY, c.Views().Information, "information"),
|
||||
Limit: NewDisplayContext(LIMIT_CONTEXT_KEY, c.Views().Limit, "limit"),
|
||||
Options: NewDisplayContext(OPTIONS_CONTEXT_KEY, c.Views().Options, "options"),
|
||||
AppStatus: NewDisplayContext(APP_STATUS_CONTEXT_KEY, c.Views().AppStatus, "appStatus"),
|
||||
SearchPrefix: NewDisplayContext(SEARCH_PREFIX_CONTEXT_KEY, c.Views().SearchPrefix, "searchPrefix"),
|
||||
Information: NewDisplayContext(INFORMATION_CONTEXT_KEY, c.Views().Information, "information"),
|
||||
Limit: NewDisplayContext(LIMIT_CONTEXT_KEY, c.Views().Limit, "limit"),
|
||||
StatusSpacer1: NewDisplayContext(STATUS_SPACER1_CONTEXT_KEY, c.Views().StatusSpacer1, "statusSpacer1"),
|
||||
StatusSpacer2: NewDisplayContext(STATUS_SPACER2_CONTEXT_KEY, c.Views().StatusSpacer2, "statusSpacer2"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +115,13 @@ func NewSubCommitsContext(
|
||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().SubCommits,
|
||||
WindowName: "branches",
|
||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
View: c.Views().SubCommits,
|
||||
WindowName: "branches",
|
||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
@@ -133,7 +134,7 @@ func NewSubCommitsContext(
|
||||
}
|
||||
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
|
||||
ctx.GetList().SetSelectedLineIdx(selectedLineIdx)
|
||||
ctx.GetList().SetSelection(selectedLineIdx)
|
||||
return ctx.HandleFocus(types.OnFocusOpts{})
|
||||
}))
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func (self *SuggestionsContext) GetSelectedItemId() string {
|
||||
|
||||
func (self *SuggestionsContext) SetSuggestions(suggestions []*types.Suggestion) {
|
||||
self.State.Suggestions = suggestions
|
||||
self.SetSelectedLineIdx(0)
|
||||
self.SetSelection(0)
|
||||
self.c.ResetViewOrigin(self.GetView())
|
||||
_ = self.HandleRender()
|
||||
}
|
||||
@@ -90,3 +90,8 @@ func (self *SuggestionsContext) RefreshSuggestions() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// There is currently no need to use range-select in the suggestions view so we're disabling it.
|
||||
func (self *SuggestionsContext) RangeSelectEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ func NewTagsContext(
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
return presentation.GetTagListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref)
|
||||
return presentation.GetTagListDisplayStrings(
|
||||
viewModel.GetItems(),
|
||||
c.State().GetItemOperation,
|
||||
c.Modes().Diffing.Ref, c.Tr)
|
||||
}
|
||||
|
||||
return &TagsContext{
|
||||
|
||||
@@ -9,13 +9,34 @@ type HasLength interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
type RangeSelectMode int
|
||||
|
||||
const (
|
||||
// None means we are not selecting a range
|
||||
RangeSelectModeNone RangeSelectMode = iota
|
||||
// Sticky range select is started by pressing 'v', then the range is expanded
|
||||
// when you move up or down. It is cancelled by pressing 'v' again.
|
||||
RangeSelectModeSticky
|
||||
// Nonsticky range select is started by pressing shift+arrow and cancelled
|
||||
// when pressing up/down without shift, or by pressing 'v'
|
||||
RangeSelectModeNonSticky
|
||||
)
|
||||
|
||||
type ListCursor struct {
|
||||
selectedIdx int
|
||||
list HasLength
|
||||
selectedIdx int
|
||||
rangeSelectMode RangeSelectMode
|
||||
// value is ignored when rangeSelectMode is RangeSelectModeNone
|
||||
rangeStartIdx int
|
||||
list HasLength
|
||||
}
|
||||
|
||||
func NewListCursor(list HasLength) *ListCursor {
|
||||
return &ListCursor{selectedIdx: 0, list: list}
|
||||
return &ListCursor{
|
||||
selectedIdx: 0,
|
||||
rangeStartIdx: 0,
|
||||
rangeSelectMode: RangeSelectModeNone,
|
||||
list: list,
|
||||
}
|
||||
}
|
||||
|
||||
var _ types.IListCursor = (*ListCursor)(nil)
|
||||
@@ -24,25 +45,110 @@ func (self *ListCursor) GetSelectedLineIdx() int {
|
||||
return self.selectedIdx
|
||||
}
|
||||
|
||||
// Sets the selected line index. Note, you probably don't want to use this directly,
|
||||
// because it doesn't affect the range select mode or range start index. You should only
|
||||
// use this for navigation situations where e.g. the user wants to jump to the top of
|
||||
// a list while in range select mode so that the selection ends up being between
|
||||
// the top of the list and the previous selection
|
||||
func (self *ListCursor) SetSelectedLineIdx(value int) {
|
||||
self.selectedIdx = self.clampValue(value)
|
||||
}
|
||||
|
||||
// Sets the selected index and cancels the range. You almost always want to use
|
||||
// this instead of SetSelectedLineIdx. For example, if you want to jump the cursor
|
||||
// to the top of a list after checking out a branch, you should use this method,
|
||||
// or you may end up with a large range selection from the previous cursor position
|
||||
// to the top of the list.
|
||||
func (self *ListCursor) SetSelection(value int) {
|
||||
self.selectedIdx = self.clampValue(value)
|
||||
self.CancelRangeSelect()
|
||||
}
|
||||
|
||||
func (self *ListCursor) clampValue(value int) int {
|
||||
clampedValue := -1
|
||||
if self.list.Len() > 0 {
|
||||
clampedValue = utils.Clamp(value, 0, self.list.Len()-1)
|
||||
}
|
||||
|
||||
self.selectedIdx = clampedValue
|
||||
return clampedValue
|
||||
}
|
||||
|
||||
// moves the cursor up or down by the given amount
|
||||
func (self *ListCursor) MoveSelectedLine(delta int) {
|
||||
self.SetSelectedLineIdx(self.selectedIdx + delta)
|
||||
// Moves the cursor up or down by the given amount.
|
||||
// If we are in non-sticky range select mode, this will cancel the range select
|
||||
func (self *ListCursor) MoveSelectedLine(change int) {
|
||||
if self.rangeSelectMode == RangeSelectModeNonSticky {
|
||||
self.CancelRangeSelect()
|
||||
}
|
||||
|
||||
self.SetSelectedLineIdx(self.selectedIdx + change)
|
||||
}
|
||||
|
||||
// to be called when the model might have shrunk so that our selection is not not out of bounds
|
||||
func (self *ListCursor) RefreshSelectedIdx() {
|
||||
self.SetSelectedLineIdx(self.selectedIdx)
|
||||
// Moves the cursor up or down by the given amount, and also moves the range start
|
||||
// index by the same amount
|
||||
func (self *ListCursor) MoveSelection(delta int) {
|
||||
self.selectedIdx = self.clampValue(self.selectedIdx + delta)
|
||||
if self.IsSelectingRange() {
|
||||
self.rangeStartIdx = self.clampValue(self.rangeStartIdx + delta)
|
||||
}
|
||||
}
|
||||
|
||||
// To be called when the model might have shrunk so that our selection is not out of bounds
|
||||
func (self *ListCursor) ClampSelection() {
|
||||
self.selectedIdx = self.clampValue(self.selectedIdx)
|
||||
self.rangeStartIdx = self.clampValue(self.rangeStartIdx)
|
||||
}
|
||||
|
||||
func (self *ListCursor) Len() int {
|
||||
return self.list.Len()
|
||||
}
|
||||
|
||||
func (self *ListCursor) GetRangeStartIdx() (int, bool) {
|
||||
if self.IsSelectingRange() {
|
||||
return self.rangeStartIdx, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (self *ListCursor) CancelRangeSelect() {
|
||||
self.rangeSelectMode = RangeSelectModeNone
|
||||
}
|
||||
|
||||
// Returns true if we are in range select mode. Note that we may be in range select
|
||||
// mode and still only selecting a single item. See AreMultipleItemsSelected below.
|
||||
func (self *ListCursor) IsSelectingRange() bool {
|
||||
return self.rangeSelectMode != RangeSelectModeNone
|
||||
}
|
||||
|
||||
// Returns true if we are in range select mode and selecting multiple items
|
||||
func (self *ListCursor) AreMultipleItemsSelected() bool {
|
||||
startIdx, endIdx := self.GetSelectionRange()
|
||||
return startIdx != endIdx
|
||||
}
|
||||
|
||||
func (self *ListCursor) GetSelectionRange() (int, int) {
|
||||
if self.IsSelectingRange() {
|
||||
return utils.MinMax(self.selectedIdx, self.rangeStartIdx)
|
||||
}
|
||||
|
||||
return self.selectedIdx, self.selectedIdx
|
||||
}
|
||||
|
||||
func (self *ListCursor) ToggleStickyRange() {
|
||||
if self.IsSelectingRange() {
|
||||
self.CancelRangeSelect()
|
||||
} else {
|
||||
self.rangeStartIdx = self.selectedIdx
|
||||
self.rangeSelectMode = RangeSelectModeSticky
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ListCursor) ExpandNonStickyRange(change int) {
|
||||
if !self.IsSelectingRange() {
|
||||
self.rangeStartIdx = self.selectedIdx
|
||||
}
|
||||
|
||||
self.rangeSelectMode = RangeSelectModeNonSticky
|
||||
|
||||
self.SetSelectedLineIdx(self.selectedIdx + change)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,14 @@ func (self *ViewTrait) FocusPoint(yIdx int) {
|
||||
self.view.FocusPoint(self.view.OriginX(), yIdx)
|
||||
}
|
||||
|
||||
func (self *ViewTrait) SetRangeSelectStart(yIdx int) {
|
||||
self.view.SetRangeSelectStart(yIdx)
|
||||
}
|
||||
|
||||
func (self *ViewTrait) CancelRangeSelect() {
|
||||
self.view.CancelRangeSelect()
|
||||
}
|
||||
|
||||
func (self *ViewTrait) SetViewPortContent(content string) {
|
||||
_, y := self.view.Origin()
|
||||
self.view.OverwriteLines(y, content)
|
||||
|
||||
@@ -50,7 +50,7 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||
}
|
||||
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
|
||||
ctx.GetList().SetSelectedLineIdx(selectedLineIdx)
|
||||
ctx.GetList().SetSelection(selectedLineIdx)
|
||||
return ctx.HandleFocus(types.OnFocusOpts{})
|
||||
}))
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
patchBuildingHelper := helpers.NewPatchBuildingHelper(helperCommon)
|
||||
stagingHelper := helpers.NewStagingHelper(helperCommon)
|
||||
mergeConflictsHelper := helpers.NewMergeConflictsHelper(helperCommon)
|
||||
searchHelper := helpers.NewSearchHelper(helperCommon)
|
||||
|
||||
refreshHelper := helpers.NewRefreshHelper(
|
||||
helperCommon,
|
||||
@@ -56,6 +57,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
stagingHelper,
|
||||
mergeConflictsHelper,
|
||||
worktreeHelper,
|
||||
searchHelper,
|
||||
)
|
||||
diffHelper := helpers.NewDiffHelper(helperCommon)
|
||||
cherryPickHelper := helpers.NewCherryPickHelper(
|
||||
@@ -75,6 +77,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
appStatusHelper := helpers.NewAppStatusHelper(
|
||||
helperCommon,
|
||||
func() *status.StatusManager { return gui.statusManager },
|
||||
modeHelper,
|
||||
)
|
||||
|
||||
setSubCommits := func(commits []*models.Commit) {
|
||||
@@ -100,6 +103,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
CherryPick: cherryPickHelper,
|
||||
Upstream: helpers.NewUpstreamHelper(helperCommon, suggestionsHelper.GetRemoteBranchesSuggestionsFunc),
|
||||
AmendHelper: helpers.NewAmendHelper(helperCommon, gpgHelper),
|
||||
FixupHelper: helpers.NewFixupHelper(helperCommon),
|
||||
Commits: commitsHelper,
|
||||
Snake: helpers.NewSnakeHelper(helperCommon),
|
||||
Diff: diffHelper,
|
||||
@@ -112,13 +116,14 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
Confirmation: helpers.NewConfirmationHelper(helperCommon),
|
||||
Mode: modeHelper,
|
||||
AppStatus: appStatusHelper,
|
||||
InlineStatus: helpers.NewInlineStatusHelper(helperCommon, windowHelper),
|
||||
WindowArrangement: helpers.NewWindowArrangementHelper(
|
||||
gui.c,
|
||||
windowHelper,
|
||||
modeHelper,
|
||||
appStatusHelper,
|
||||
),
|
||||
Search: helpers.NewSearchHelper(helperCommon),
|
||||
Search: searchHelper,
|
||||
Worktree: worktreeHelper,
|
||||
SubCommits: helpers.NewSubCommitsHelper(helperCommon, refreshHelper, setSubCommits),
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package controllers
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
@@ -13,6 +14,7 @@ var _ types.IController = &BasicCommitsController{}
|
||||
|
||||
type ContainsCommits interface {
|
||||
types.Context
|
||||
types.IListContext
|
||||
GetSelected() *models.Commit
|
||||
GetCommits() []*models.Commit
|
||||
GetSelectedLineIdx() int
|
||||
@@ -20,82 +22,79 @@ type ContainsCommits interface {
|
||||
|
||||
type BasicCommitsController struct {
|
||||
baseController
|
||||
*ListControllerTrait[*models.Commit]
|
||||
c *ControllerCommon
|
||||
context ContainsCommits
|
||||
}
|
||||
|
||||
func NewBasicCommitsController(controllerCommon *ControllerCommon, context ContainsCommits) *BasicCommitsController {
|
||||
func NewBasicCommitsController(c *ControllerCommon, context ContainsCommits) *BasicCommitsController {
|
||||
return &BasicCommitsController{
|
||||
baseController: baseController{},
|
||||
c: controllerCommon,
|
||||
c: c,
|
||||
context: context,
|
||||
ListControllerTrait: NewListControllerTrait[*models.Commit](
|
||||
c,
|
||||
context,
|
||||
context.GetSelected,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.CheckoutCommit),
|
||||
Handler: self.checkSelected(self.checkout),
|
||||
Description: self.c.Tr.CheckoutCommit,
|
||||
Key: opts.GetKey(opts.Config.Commits.CheckoutCommit),
|
||||
Handler: self.withItem(self.checkout),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CheckoutCommit,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.CopyCommitAttributeToClipboard),
|
||||
Handler: self.checkSelected(self.copyCommitAttribute),
|
||||
Description: self.c.Tr.CopyCommitAttributeToClipboard,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Commits.CopyCommitAttributeToClipboard),
|
||||
Handler: self.withItem(self.copyCommitAttribute),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CopyCommitAttributeToClipboard,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.OpenInBrowser),
|
||||
Handler: self.checkSelected(self.openInBrowser),
|
||||
Description: self.c.Tr.OpenCommitInBrowser,
|
||||
Key: opts.GetKey(opts.Config.Commits.OpenInBrowser),
|
||||
Handler: self.withItem(self.openInBrowser),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.OpenCommitInBrowser,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.New),
|
||||
Handler: self.checkSelected(self.newBranch),
|
||||
Description: self.c.Tr.CreateNewBranchFromCommit,
|
||||
Key: opts.GetKey(opts.Config.Universal.New),
|
||||
Handler: self.withItem(self.newBranch),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CreateNewBranchFromCommit,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
|
||||
Handler: self.checkSelected(self.createResetMenu),
|
||||
Description: self.c.Tr.ViewResetOptions,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
|
||||
Handler: self.withItem(self.createResetMenu),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.ViewResetOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.CherryPickCopy),
|
||||
Handler: self.checkSelected(self.copy),
|
||||
Handler: self.withItem(self.copyRange),
|
||||
Description: self.c.Tr.CherryPickCopy,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.CherryPickCopyRange),
|
||||
Handler: self.checkSelected(self.copyRange),
|
||||
Description: self.c.Tr.CherryPickCopyRange,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.ResetCherryPick),
|
||||
Handler: self.c.Helpers().CherryPick.Reset,
|
||||
Description: self.c.Tr.ResetCherryPick,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenDiffTool),
|
||||
Handler: self.withItem(self.openDiffTool),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.OpenDiffTool,
|
||||
},
|
||||
}
|
||||
|
||||
return bindings
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) checkSelected(callback func(*models.Commit) error) func() error {
|
||||
return func() error {
|
||||
commit := self.context.GetSelected()
|
||||
if commit == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(commit)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) Context() types.Context {
|
||||
return self.context
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) error {
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.Actions.CopyCommitAttributeToClipboard,
|
||||
@@ -105,8 +104,21 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e
|
||||
OnPress: func() error {
|
||||
return self.copyCommitSHAToClipboard(commit)
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.CommitSubject,
|
||||
OnPress: func() error {
|
||||
return self.copyCommitSubjectToClipboard(commit)
|
||||
},
|
||||
Key: 's',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.CommitMessage,
|
||||
OnPress: func() error {
|
||||
return self.copyCommitMessageToClipboard(commit)
|
||||
},
|
||||
Key: 'm',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.CommitURL,
|
||||
OnPress: func() error {
|
||||
@@ -121,13 +133,6 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e
|
||||
},
|
||||
Key: 'd',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.CommitMessage,
|
||||
OnPress: func() error {
|
||||
return self.copyCommitMessageToClipboard(commit)
|
||||
},
|
||||
Key: 'm',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.CommitAuthor,
|
||||
OnPress: func() error {
|
||||
@@ -211,6 +216,21 @@ func (self *BasicCommitsController) copyCommitMessageToClipboard(commit *models.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyCommitSubjectToClipboard(commit *models.Commit) error {
|
||||
message, err := self.c.Git().Commit.GetCommitSubject(commit.Sha)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitSubjectToClipboard)
|
||||
if err := self.c.OS().CopyToClipboard(message); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.Toast(self.c.Tr.CommitSubjectCopiedToClipboard)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error {
|
||||
url, err := self.c.Helpers().Host.GetCommitURL(commit.Sha)
|
||||
if err != nil {
|
||||
@@ -244,10 +264,21 @@ func (self *BasicCommitsController) checkout(commit *models.Commit) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copy(commit *models.Commit) error {
|
||||
return self.c.Helpers().CherryPick.Copy(commit, self.context.GetCommits(), self.context)
|
||||
func (self *BasicCommitsController) copyRange(*models.Commit) error {
|
||||
return self.c.Helpers().CherryPick.CopyRange(self.context.GetCommits(), self.context)
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyRange(*models.Commit) error {
|
||||
return self.c.Helpers().CherryPick.CopyRange(self.context.GetSelectedLineIdx(), self.context.GetCommits(), self.context)
|
||||
func (self *BasicCommitsController) openDiffTool(commit *models.Commit) error {
|
||||
to := commit.RefName()
|
||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(commit.ParentRefName())
|
||||
_, err := self.c.RunSubprocess(self.c.Git().Diff.OpenDiffToolCmdObj(
|
||||
git_commands.DiffToolCmdOptions{
|
||||
Filepath: ".",
|
||||
FromCommit: from,
|
||||
ToCommit: to,
|
||||
Reverse: reverse,
|
||||
IsDirectory: true,
|
||||
Staged: false,
|
||||
}))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -14,17 +14,23 @@ import (
|
||||
|
||||
type BisectController struct {
|
||||
baseController
|
||||
*ListControllerTrait[*models.Commit]
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &BisectController{}
|
||||
|
||||
func NewBisectController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *BisectController {
|
||||
return &BisectController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
ListControllerTrait: NewListControllerTrait[*models.Commit](
|
||||
c,
|
||||
c.Contexts().LocalCommits,
|
||||
c.Contexts().LocalCommits.GetSelected,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +38,7 @@ func (self *BisectController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.ViewBisectOptions),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.checkSelected(self.openMenu)),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.withItem(self.openMenu)),
|
||||
Description: self.c.Tr.ViewBisectOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
@@ -70,9 +76,19 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
// If we have a current sha already, then we always want to use that one. If
|
||||
// not, we're still picking the initial commits before we really start, so
|
||||
// use the selected commit in that case.
|
||||
shaToMark := lo.Ternary(info.GetCurrentSha() != "", info.GetCurrentSha(), commit.Sha)
|
||||
|
||||
bisecting := info.GetCurrentSha() != ""
|
||||
shaToMark := lo.Ternary(bisecting, info.GetCurrentSha(), commit.Sha)
|
||||
shortShaToMark := utils.ShortSha(shaToMark)
|
||||
|
||||
// For marking a commit as bad, when we're not already bisecting, we require
|
||||
// a single item selected, but once we are bisecting, it doesn't matter because
|
||||
// the action applies to the HEAD commit rather than the selected commit.
|
||||
var singleItemIfNotBisecting *types.DisabledReason
|
||||
if !bisecting {
|
||||
singleItemIfNotBisecting = self.require(self.singleItemSelected())()
|
||||
}
|
||||
|
||||
menuItems := []*types.MenuItem{
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, shortShaToMark, info.NewTerm()),
|
||||
@@ -84,7 +100,8 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
|
||||
return self.afterMark(selectCurrentAfter, waitToReselect)
|
||||
},
|
||||
Key: 'b',
|
||||
DisabledReason: singleItemIfNotBisecting,
|
||||
Key: 'b',
|
||||
},
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, shortShaToMark, info.OldTerm()),
|
||||
@@ -96,7 +113,8 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
|
||||
return self.afterMark(selectCurrentAfter, waitToReselect)
|
||||
},
|
||||
Key: 'g',
|
||||
DisabledReason: singleItemIfNotBisecting,
|
||||
Key: 'g',
|
||||
},
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.SkipCurrent, shortShaToMark),
|
||||
@@ -108,7 +126,8 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
|
||||
return self.afterMark(selectCurrentAfter, waitToReselect)
|
||||
},
|
||||
Key: 's',
|
||||
DisabledReason: singleItemIfNotBisecting,
|
||||
Key: 's',
|
||||
},
|
||||
}
|
||||
if info.GetCurrentSha() != "" && info.GetCurrentSha() != commit.Sha {
|
||||
@@ -122,7 +141,8 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
|
||||
return self.afterMark(selectCurrentAfter, waitToReselect)
|
||||
},
|
||||
Key: 'S',
|
||||
DisabledReason: self.require(self.singleItemSelected())(),
|
||||
Key: 'S',
|
||||
}))
|
||||
}
|
||||
menuItems = append(menuItems, lo.ToPtr(types.MenuItem{
|
||||
@@ -157,7 +177,8 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo,
|
||||
|
||||
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||
},
|
||||
Key: 'b',
|
||||
DisabledReason: self.require(self.singleItemSelected())(),
|
||||
Key: 'b',
|
||||
},
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
|
||||
@@ -173,7 +194,8 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo,
|
||||
|
||||
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||
},
|
||||
Key: 'g',
|
||||
DisabledReason: self.require(self.singleItemSelected())(),
|
||||
Key: 'g',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.Bisect.ChooseTerms,
|
||||
@@ -265,7 +287,7 @@ func (self *BisectController) selectCurrentBisectCommit() {
|
||||
// find index of commit with that sha, move cursor to that.
|
||||
for i, commit := range self.c.Model().Commits {
|
||||
if commit.Sha == info.GetCurrentSha() {
|
||||
self.context().SetSelectedLineIdx(i)
|
||||
self.context().SetSelection(i)
|
||||
_ = self.context().HandleFocus(types.OnFocusOpts{})
|
||||
break
|
||||
}
|
||||
@@ -273,21 +295,6 @@ func (self *BisectController) selectCurrentBisectCommit() {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BisectController) checkSelected(callback func(*models.Commit) error) func() error {
|
||||
return func() error {
|
||||
commit := self.context().GetSelected()
|
||||
if commit == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(commit)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BisectController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
|
||||
func (self *BisectController) context() *context.LocalCommitsContext {
|
||||
return self.c.Contexts().LocalCommits
|
||||
}
|
||||
|
||||
@@ -17,47 +17,61 @@ import (
|
||||
|
||||
type BranchesController struct {
|
||||
baseController
|
||||
*ListControllerTrait[*models.Branch]
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &BranchesController{}
|
||||
|
||||
func NewBranchesController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *BranchesController {
|
||||
return &BranchesController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
ListControllerTrait: NewListControllerTrait[*models.Branch](
|
||||
c,
|
||||
c.Contexts().Branches,
|
||||
c.Contexts().Branches.GetSelected,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
return []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.checkSelected(self.press),
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.withItem(self.press),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(),
|
||||
self.notPulling,
|
||||
),
|
||||
Description: self.c.Tr.Checkout,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.New),
|
||||
Handler: self.checkSelected(self.newBranch),
|
||||
Description: self.c.Tr.NewBranch,
|
||||
Key: opts.GetKey(opts.Config.Universal.New),
|
||||
Handler: self.withItem(self.newBranch),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.NewBranch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.CreatePullRequest),
|
||||
Handler: self.checkSelected(self.handleCreatePullRequest),
|
||||
Description: self.c.Tr.CreatePullRequest,
|
||||
Key: opts.GetKey(opts.Config.Branches.CreatePullRequest),
|
||||
Handler: self.withItem(self.handleCreatePullRequest),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CreatePullRequest,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.ViewPullRequestOptions),
|
||||
Handler: self.checkSelected(self.handleCreatePullRequestMenu),
|
||||
Description: self.c.Tr.CreatePullRequestOptions,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Branches.ViewPullRequestOptions),
|
||||
Handler: self.withItem(self.handleCreatePullRequestMenu),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CreatePullRequestOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.CopyPullRequestURL),
|
||||
Handler: self.copyPullRequestURL,
|
||||
Description: self.c.Tr.CopyPullRequestURL,
|
||||
Key: opts.GetKey(opts.Config.Branches.CopyPullRequestURL),
|
||||
Handler: self.copyPullRequestURL,
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CopyPullRequestURL,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.CheckoutBranchByName),
|
||||
@@ -65,54 +79,69 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
|
||||
Description: self.c.Tr.CheckoutByName,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.ForceCheckoutBranch),
|
||||
Handler: self.forceCheckout,
|
||||
Description: self.c.Tr.ForceCheckout,
|
||||
Key: opts.GetKey(opts.Config.Branches.ForceCheckoutBranch),
|
||||
Handler: self.forceCheckout,
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.ForceCheckout,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.checkSelectedAndReal(self.delete),
|
||||
Description: self.c.Tr.ViewDeleteOptions,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.withItem(self.delete),
|
||||
GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)),
|
||||
Description: self.c.Tr.ViewDeleteOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.rebase),
|
||||
Description: self.c.Tr.RebaseBranch,
|
||||
GetDisabledReason: self.getDisabledReasonForRebase,
|
||||
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.rebase),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.notRebasingOntoSelf),
|
||||
),
|
||||
Description: self.c.Tr.RebaseBranch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.merge),
|
||||
Description: self.c.Tr.MergeIntoCurrentBranch,
|
||||
Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.merge),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.MergeIntoCurrentBranch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.FastForward),
|
||||
Handler: self.checkSelectedAndReal(self.fastForward),
|
||||
Description: self.c.Tr.FastForward,
|
||||
Key: opts.GetKey(opts.Config.Branches.FastForward),
|
||||
Handler: self.withItem(self.fastForward),
|
||||
GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)),
|
||||
Description: self.c.Tr.FastForward,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.CreateTag),
|
||||
Handler: self.checkSelected(self.createTag),
|
||||
Description: self.c.Tr.CreateTag,
|
||||
Key: opts.GetKey(opts.Config.Branches.CreateTag),
|
||||
Handler: self.withItem(self.createTag),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CreateTag,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
|
||||
Handler: self.checkSelected(self.createResetMenu),
|
||||
Description: self.c.Tr.ViewResetOptions,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Branches.SortOrder),
|
||||
Handler: self.createSortMenu,
|
||||
Description: self.c.Tr.SortOrder,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.RenameBranch),
|
||||
Handler: self.checkSelectedAndReal(self.rename),
|
||||
Description: self.c.Tr.RenameBranch,
|
||||
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
|
||||
Handler: self.withItem(self.createResetMenu),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.ViewResetOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.SetUpstream),
|
||||
Handler: self.checkSelected(self.viewUpstreamOptions),
|
||||
Description: self.c.Tr.ViewBranchUpstreamOptions,
|
||||
Tooltip: self.c.Tr.ViewBranchUpstreamOptionsTooltip,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Branches.RenameBranch),
|
||||
Handler: self.withItem(self.rename),
|
||||
GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)),
|
||||
Description: self.c.Tr.RenameBranch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.SetUpstream),
|
||||
Handler: self.withItem(self.viewUpstreamOptions),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.ViewBranchUpstreamOptions,
|
||||
Tooltip: self.c.Tr.ViewBranchUpstreamOptionsTooltip,
|
||||
OpensMenu: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -158,7 +187,6 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc
|
||||
ShowBranchHeads: false,
|
||||
})
|
||||
},
|
||||
Key: 'v',
|
||||
}
|
||||
|
||||
unsetUpstreamItem := &types.MenuItem{
|
||||
@@ -256,11 +284,14 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc
|
||||
Key: 'r',
|
||||
}
|
||||
|
||||
if !selectedBranch.IsTrackingRemote() {
|
||||
unsetUpstreamItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.UpstreamNotSetError}
|
||||
}
|
||||
|
||||
if !selectedBranch.RemoteBranchStoredLocally() {
|
||||
viewDivergenceItem.DisabledReason = self.c.Tr.UpstreamNotSetError
|
||||
unsetUpstreamItem.DisabledReason = self.c.Tr.UpstreamNotSetError
|
||||
upstreamResetItem.DisabledReason = self.c.Tr.UpstreamNotSetError
|
||||
upstreamRebaseItem.DisabledReason = self.c.Tr.UpstreamNotSetError
|
||||
viewDivergenceItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.UpstreamNotSetError}
|
||||
upstreamResetItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.UpstreamNotSetError}
|
||||
upstreamRebaseItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.UpstreamNotSetError}
|
||||
}
|
||||
|
||||
options := []*types.MenuItem{
|
||||
@@ -299,6 +330,18 @@ func (self *BranchesController) press(selectedBranch *models.Branch) error {
|
||||
return self.c.Helpers().Refs.CheckoutRef(selectedBranch.Name, types.CheckoutRefOptions{})
|
||||
}
|
||||
|
||||
func (self *BranchesController) notPulling() *types.DisabledReason {
|
||||
currentBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||
if currentBranch != nil {
|
||||
op := self.c.State().GetItemOperation(currentBranch)
|
||||
if op == types.ItemOperationFastForwarding || op == types.ItemOperationPulling {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CantCheckoutBranchWhilePulling}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BranchesController) worktreeForBranch(branch *models.Branch) (*models.Worktree, bool) {
|
||||
return git_commands.WorktreeForBranch(branch, self.c.Model().Worktrees)
|
||||
}
|
||||
@@ -403,8 +446,8 @@ func (self *BranchesController) createNewBranchWithName(newBranchName string) er
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.context().SetSelectedLineIdx(0)
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
self.context().SetSelection(0)
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, KeepBranchSelectionIndex: true})
|
||||
}
|
||||
|
||||
func (self *BranchesController) checkedOutByOtherWorktree(branch *models.Branch) bool {
|
||||
@@ -503,7 +546,7 @@ func (self *BranchesController) delete(branch *models.Branch) error {
|
||||
},
|
||||
}
|
||||
if checkedOutBranch.Name == branch.Name {
|
||||
localDeleteItem.DisabledReason = self.c.Tr.CantDeleteCheckOutBranch
|
||||
localDeleteItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.CantDeleteCheckOutBranch}
|
||||
}
|
||||
|
||||
remoteDeleteItem := &types.MenuItem{
|
||||
@@ -514,7 +557,7 @@ func (self *BranchesController) delete(branch *models.Branch) error {
|
||||
},
|
||||
}
|
||||
if !branch.IsTrackingRemote() || branch.UpstreamGone {
|
||||
remoteDeleteItem.DisabledReason = self.c.Tr.UpstreamNotSetError
|
||||
remoteDeleteItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.UpstreamNotSetError}
|
||||
}
|
||||
|
||||
menuTitle := utils.ResolvePlaceholderString(
|
||||
@@ -540,14 +583,14 @@ func (self *BranchesController) rebase() error {
|
||||
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranchName)
|
||||
}
|
||||
|
||||
func (self *BranchesController) getDisabledReasonForRebase() string {
|
||||
selectedBranchName := self.context().GetSelected().Name
|
||||
func (self *BranchesController) notRebasingOntoSelf(branch *models.Branch) *types.DisabledReason {
|
||||
selectedBranchName := branch.Name
|
||||
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef().Name
|
||||
if selectedBranchName == checkedOutBranch {
|
||||
return self.c.Tr.CantRebaseOntoSelf
|
||||
return &types.DisabledReason{Text: self.c.Tr.CantRebaseOntoSelf}
|
||||
}
|
||||
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
@@ -563,14 +606,7 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
|
||||
action := self.c.Tr.Actions.FastForwardBranch
|
||||
|
||||
message := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.FastForwarding,
|
||||
map[string]string{
|
||||
"branch": branch.Name,
|
||||
},
|
||||
)
|
||||
|
||||
return self.c.WithWaitingStatus(message, func(task gocui.Task) error {
|
||||
return self.c.WithInlineStatus(branch, types.ItemOperationFastForwarding, context.LOCAL_BRANCHES_CONTEXT_KEY, func(task gocui.Task) error {
|
||||
worktree, ok := self.worktreeForBranch(branch)
|
||||
if ok {
|
||||
self.c.LogAction(action)
|
||||
@@ -590,24 +626,17 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
WorktreeGitDir: worktreeGitDir,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
return err
|
||||
} 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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -615,6 +644,18 @@ func (self *BranchesController) createTag(branch *models.Branch) error {
|
||||
return self.c.Helpers().Tags.OpenCreateTagPrompt(branch.FullRefName(), func() {})
|
||||
}
|
||||
|
||||
func (self *BranchesController) createSortMenu() error {
|
||||
return self.c.Helpers().Refs.CreateSortOrderMenu([]string{"recency", "alphabetical", "date"}, func(sortOrder string) error {
|
||||
if self.c.GetAppState().LocalBranchSortOrder != sortOrder {
|
||||
self.c.GetAppState().LocalBranchSortOrder = sortOrder
|
||||
self.c.SaveAppStateAndLogError()
|
||||
self.c.Contexts().Branches.SetSelection(0)
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.BRANCHES}})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (self *BranchesController) createResetMenu(selectedBranch *models.Branch) error {
|
||||
return self.c.Helpers().Refs.CreateGitResetMenu(selectedBranch.Name)
|
||||
}
|
||||
@@ -639,7 +680,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
|
||||
// now that we've got our stuff again we need to find that branch and reselect it.
|
||||
for i, newBranch := range self.c.Model().Branches {
|
||||
if newBranch.Name == newBranchName {
|
||||
self.context().SetSelectedLineIdx(i)
|
||||
self.context().SetSelection(i)
|
||||
if err := self.context().HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -734,24 +775,10 @@ func (self *BranchesController) createPullRequest(from string, to string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BranchesController) checkSelected(callback func(*models.Branch) error) func() error {
|
||||
return func() error {
|
||||
selectedItem := self.context().GetSelected()
|
||||
if selectedItem == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(selectedItem)
|
||||
func (self *BranchesController) branchIsReal(branch *models.Branch) *types.DisabledReason {
|
||||
if !branch.IsRealBranch() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.SelectedItemIsNotABranch}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BranchesController) checkSelectedAndReal(callback func(*models.Branch) error) func() error {
|
||||
return func() error {
|
||||
selectedItem := self.context().GetSelected()
|
||||
if selectedItem == nil || !selectedItem.IsRealBranch() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(selectedItem)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ type CommandLogController struct {
|
||||
var _ types.IController = &CommandLogController{}
|
||||
|
||||
func NewCommandLogController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *CommandLogController {
|
||||
return &CommandLogController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ type CommitDescriptionController struct {
|
||||
var _ types.IController = &CommitMessageController{}
|
||||
|
||||
func NewCommitDescriptionController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *CommitDescriptionController {
|
||||
return &CommitDescriptionController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ type CommitMessageController struct {
|
||||
var _ types.IController = &CommitMessageController{}
|
||||
|
||||
func NewCommitMessageController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *CommitMessageController {
|
||||
return &CommitMessageController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
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/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
@@ -11,56 +12,74 @@ import (
|
||||
|
||||
type CommitFilesController struct {
|
||||
baseController
|
||||
*ListControllerTrait[*filetree.CommitFileNode]
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &CommitFilesController{}
|
||||
|
||||
func NewCommitFilesController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *CommitFilesController {
|
||||
return &CommitFilesController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
ListControllerTrait: NewListControllerTrait[*filetree.CommitFileNode](
|
||||
c,
|
||||
c.Contexts().CommitFiles,
|
||||
c.Contexts().CommitFiles.GetSelected,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.CommitFiles.CheckoutCommitFile),
|
||||
Handler: self.checkSelected(self.checkout),
|
||||
Description: self.c.Tr.CheckoutCommitFile,
|
||||
Key: opts.GetKey(opts.Config.CommitFiles.CheckoutCommitFile),
|
||||
Handler: self.withItem(self.checkout),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.CheckoutCommitFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.checkSelected(self.discard),
|
||||
Description: self.c.Tr.DiscardOldFileChange,
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.withItem(self.discard),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.DiscardOldFileChange,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.checkSelected(self.open),
|
||||
Description: self.c.Tr.OpenFile,
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.withItem(self.open),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.OpenFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.checkSelected(self.edit),
|
||||
Description: self.c.Tr.EditFile,
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.withItem(self.edit),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.EditFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.checkSelected(self.toggleForPatch),
|
||||
Description: self.c.Tr.ToggleAddToPatch,
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenDiffTool),
|
||||
Handler: self.withItem(self.openDiffTool),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.OpenDiffTool,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.withItem(self.toggleForPatch),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.ToggleAddToPatch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.ToggleStagedAll),
|
||||
Handler: self.checkSelected(self.toggleAllForPatch),
|
||||
Handler: self.withItem(self.toggleAllForPatch),
|
||||
Description: self.c.Tr.ToggleAllInPatch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
||||
Handler: self.checkSelected(self.enter),
|
||||
Description: self.c.Tr.EnterFile,
|
||||
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
||||
Handler: self.withItem(self.enter),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.EnterFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.ToggleTreeView),
|
||||
@@ -83,21 +102,6 @@ func (self *CommitFilesController) GetMouseKeybindings(opts types.KeybindingsOpt
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) checkSelected(callback func(*filetree.CommitFileNode) error) func() error {
|
||||
return func() error {
|
||||
selected := self.context().GetSelected()
|
||||
if selected == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(selected)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) context() *context.CommitFilesContext {
|
||||
return self.c.Contexts().CommitFiles
|
||||
}
|
||||
@@ -201,6 +205,22 @@ func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
|
||||
return self.c.Helpers().Files.EditFile(node.GetPath())
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) openDiffTool(node *filetree.CommitFileNode) error {
|
||||
ref := self.context().GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
_, err := self.c.RunSubprocess(self.c.Git().Diff.OpenDiffToolCmdObj(
|
||||
git_commands.DiffToolCmdOptions{
|
||||
Filepath: node.GetPath(),
|
||||
FromCommit: from,
|
||||
ToCommit: to,
|
||||
Reverse: reverse,
|
||||
IsDirectory: !node.IsFile(),
|
||||
Staged: false,
|
||||
}))
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode) error {
|
||||
toggle := func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.UpdatingPatch, func(gocui.Task) error {
|
||||
|
||||
@@ -13,11 +13,11 @@ type ConfirmationController struct {
|
||||
var _ types.IController = &ConfirmationController{}
|
||||
|
||||
func NewConfirmationController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *ConfirmationController {
|
||||
return &ConfirmationController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,11 @@ type ContextLinesController struct {
|
||||
var _ types.IController = &ContextLinesController{}
|
||||
|
||||
func NewContextLinesController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *ContextLinesController {
|
||||
return &ContextLinesController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,15 +62,22 @@ func (self *CustomPatchOptionsMenuAction) Call() error {
|
||||
if self.c.CurrentContext().GetKey() == self.c.Contexts().LocalCommits.GetKey() {
|
||||
selectedCommit := self.c.Contexts().LocalCommits.GetSelected()
|
||||
if selectedCommit != nil && self.c.Git().Patch.PatchBuilder.To != selectedCommit.Sha {
|
||||
|
||||
var disabledReason *types.DisabledReason
|
||||
if self.c.Contexts().LocalCommits.AreMultipleItemsSelected() {
|
||||
disabledReason = &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupported}
|
||||
}
|
||||
|
||||
// adding this option to index 1
|
||||
menuItems = append(
|
||||
menuItems[:1],
|
||||
append(
|
||||
[]*types.MenuItem{
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.MovePatchToSelectedCommit, selectedCommit.Sha),
|
||||
OnPress: self.handleMovePatchToSelectedCommit,
|
||||
Key: 'm',
|
||||
Label: fmt.Sprintf(self.c.Tr.MovePatchToSelectedCommit, selectedCommit.Sha),
|
||||
OnPress: self.handleMovePatchToSelectedCommit,
|
||||
Key: 'm',
|
||||
DisabledReason: disabledReason,
|
||||
},
|
||||
}, menuItems[1:]...,
|
||||
)...,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"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/filetree"
|
||||
@@ -12,31 +13,44 @@ import (
|
||||
|
||||
type FilesController struct {
|
||||
baseController // nolint: unused
|
||||
c *ControllerCommon
|
||||
*ListControllerTrait[*filetree.FileNode]
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &FilesController{}
|
||||
|
||||
func NewFilesController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *FilesController {
|
||||
return &FilesController{
|
||||
c: common,
|
||||
c: c,
|
||||
ListControllerTrait: NewListControllerTrait[*filetree.FileNode](
|
||||
c,
|
||||
c.Contexts().Files,
|
||||
c.Contexts().Files.GetSelected,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
return []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.checkSelectedFileNode(self.press),
|
||||
Description: self.c.Tr.ToggleStaged,
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.withItem(self.press),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.ToggleStaged,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.OpenStatusFilter),
|
||||
Handler: self.handleStatusFilterPressed,
|
||||
Description: self.c.Tr.FileFilter,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.CopyFileInfoToClipboard),
|
||||
Handler: self.openCopyMenu,
|
||||
Description: self.c.Tr.CopyToClipboardMenu,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.CommitChanges),
|
||||
Handler: self.c.Helpers().WorkingTree.HandleCommitPress,
|
||||
@@ -58,20 +72,29 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
Description: self.c.Tr.CommitChangesWithEditor,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.checkSelectedFileNode(self.edit),
|
||||
Description: self.c.Tr.EditFile,
|
||||
Key: opts.GetKey(opts.Config.Files.FindBaseCommitForFixup),
|
||||
Handler: self.c.Helpers().FixupHelper.HandleFindBaseCommitForFixupPress,
|
||||
Description: self.c.Tr.FindBaseCommitForFixup,
|
||||
Tooltip: self.c.Tr.FindBaseCommitForFixupTooltip,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.Open,
|
||||
Description: self.c.Tr.OpenFile,
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.withItem(self.edit),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.EditFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.IgnoreFile),
|
||||
Handler: self.checkSelectedFileNode(self.ignoreOrExcludeMenu),
|
||||
Description: self.c.Tr.Actions.IgnoreExcludeFile,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.Open,
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.OpenFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.IgnoreFile),
|
||||
Handler: self.withItem(self.ignoreOrExcludeMenu),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.Actions.IgnoreExcludeFile,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.RefreshFiles),
|
||||
@@ -95,9 +118,10 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
Description: self.c.Tr.ToggleStagedAll,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
||||
Handler: self.enter,
|
||||
Description: self.c.Tr.FileEnter,
|
||||
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
||||
Handler: self.enter,
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.FileEnter,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
|
||||
@@ -116,6 +140,12 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
Handler: self.toggleTreeView,
|
||||
Description: self.c.Tr.ToggleTreeView,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenDiffTool),
|
||||
Handler: self.withItem(self.openDiffTool),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.OpenDiffTool,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
||||
Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
|
||||
@@ -187,7 +217,7 @@ func (self *FilesController) GetOnRenderToMain() func() error {
|
||||
}
|
||||
|
||||
if hasConflicts {
|
||||
return self.c.Helpers().MergeConflicts.Render(false)
|
||||
return self.c.Helpers().MergeConflicts.Render()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +266,7 @@ func (self *FilesController) GetOnRenderToMain() func() error {
|
||||
}
|
||||
|
||||
func (self *FilesController) GetOnClick() func() error {
|
||||
return self.checkSelectedFileNode(self.press)
|
||||
return self.withItemGraceful(self.press)
|
||||
}
|
||||
|
||||
// if we are dealing with a status for which there is no key in this map,
|
||||
@@ -393,17 +423,6 @@ func (self *FilesController) press(node *filetree.FileNode) error {
|
||||
return self.context().HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *FilesController) checkSelectedFileNode(callback func(*filetree.FileNode) error) func() error {
|
||||
return func() error {
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilesController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
@@ -678,6 +697,26 @@ func (self *FilesController) Open() error {
|
||||
return self.c.Helpers().Files.OpenFile(node.GetPath())
|
||||
}
|
||||
|
||||
func (self *FilesController) openDiffTool(node *filetree.FileNode) error {
|
||||
fromCommit := ""
|
||||
reverse := false
|
||||
if self.c.Modes().Diffing.Active() {
|
||||
fromCommit = self.c.Modes().Diffing.Ref
|
||||
reverse = self.c.Modes().Diffing.Reverse
|
||||
}
|
||||
return self.c.RunSubprocessAndRefresh(
|
||||
self.c.Git().Diff.OpenDiffToolCmdObj(
|
||||
git_commands.DiffToolCmdOptions{
|
||||
Filepath: node.Path,
|
||||
FromCommit: fromCommit,
|
||||
ToCommit: "",
|
||||
Reverse: reverse,
|
||||
IsDirectory: !node.IsFile(),
|
||||
Staged: !node.GetHasUnstagedChanges(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *FilesController) switchToMerge() error {
|
||||
file := self.getSelectedFile()
|
||||
if file == nil {
|
||||
@@ -748,6 +787,109 @@ func (self *FilesController) createStashMenu() error {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *FilesController) openCopyMenu() error {
|
||||
node := self.context().GetSelected()
|
||||
|
||||
copyNameItem := &types.MenuItem{
|
||||
Label: self.c.Tr.CopyFileName,
|
||||
OnPress: func() error {
|
||||
if err := self.c.OS().CopyToClipboard(node.Name()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.c.Toast(self.c.Tr.FileNameCopiedToast)
|
||||
return nil
|
||||
},
|
||||
DisabledReason: self.require(self.singleItemSelected())(),
|
||||
Key: 'n',
|
||||
}
|
||||
copyPathItem := &types.MenuItem{
|
||||
Label: self.c.Tr.CopyFilePath,
|
||||
OnPress: func() error {
|
||||
if err := self.c.OS().CopyToClipboard(node.Path); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.c.Toast(self.c.Tr.FilePathCopiedToast)
|
||||
return nil
|
||||
},
|
||||
DisabledReason: self.require(self.singleItemSelected())(),
|
||||
Key: 'p',
|
||||
}
|
||||
copyFileDiffItem := &types.MenuItem{
|
||||
Label: self.c.Tr.CopySelectedDiff,
|
||||
Tooltip: self.c.Tr.CopyFileDiffTooltip,
|
||||
OnPress: func() error {
|
||||
path := self.context().GetSelectedPath()
|
||||
hasStaged := self.hasPathStagedChanges(node)
|
||||
diff, err := self.c.Git().Diff.GetPathDiff(path, hasStaged)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
if err := self.c.OS().CopyToClipboard(diff); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.c.Toast(self.c.Tr.FileDiffCopiedToast)
|
||||
return nil
|
||||
},
|
||||
DisabledReason: self.require(self.singleItemSelected(
|
||||
func(file *filetree.FileNode) *types.DisabledReason {
|
||||
if !node.GetHasStagedOrTrackedChanges() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
))(),
|
||||
Key: 's',
|
||||
}
|
||||
copyAllDiff := &types.MenuItem{
|
||||
Label: self.c.Tr.CopyAllFilesDiff,
|
||||
Tooltip: self.c.Tr.CopyFileDiffTooltip,
|
||||
OnPress: func() error {
|
||||
hasStaged := self.c.Helpers().WorkingTree.AnyStagedFiles()
|
||||
diff, err := self.c.Git().Diff.GetAllDiff(hasStaged)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
if err := self.c.OS().CopyToClipboard(diff); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.c.Toast(self.c.Tr.AllFilesDiffCopiedToast)
|
||||
return nil
|
||||
},
|
||||
DisabledReason: self.require(
|
||||
func() *types.DisabledReason {
|
||||
if !self.anyStagedOrTrackedFile() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)(),
|
||||
Key: 'a',
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.CopyToClipboardMenu,
|
||||
Items: []*types.MenuItem{
|
||||
copyNameItem,
|
||||
copyPathItem,
|
||||
copyFileDiffItem,
|
||||
copyAllDiff,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *FilesController) anyStagedOrTrackedFile() bool {
|
||||
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||
return self.c.Helpers().WorkingTree.AnyTrackedFiles()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *FilesController) hasPathStagedChanges(node *filetree.FileNode) bool {
|
||||
return node.SomeFile(func(t *models.File) bool {
|
||||
return t.HasStagedChanges
|
||||
})
|
||||
}
|
||||
|
||||
func (self *FilesController) stash() error {
|
||||
return self.handleStashSave(self.c.Git().Stash.Push, self.c.Tr.Actions.StashAllChanges)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package controllers
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -13,27 +12,34 @@ import (
|
||||
|
||||
type FilesRemoveController struct {
|
||||
baseController
|
||||
*ListControllerTrait[*filetree.FileNode]
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &FilesRemoveController{}
|
||||
|
||||
func NewFilesRemoveController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *FilesRemoveController {
|
||||
return &FilesRemoveController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
ListControllerTrait: NewListControllerTrait[*filetree.FileNode](
|
||||
c,
|
||||
c.Contexts().Files,
|
||||
c.Contexts().Files.GetSelected,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilesRemoveController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.checkSelectedFileNode(self.remove),
|
||||
Description: self.c.Tr.ViewDiscardOptions,
|
||||
OpensMenu: true,
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.withItem(self.remove),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.ViewDiscardOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -166,22 +172,3 @@ func (self *FilesRemoveController) ResetSubmodule(submodule *models.SubmoduleCon
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.SUBMODULES}})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *FilesRemoveController) checkSelectedFileNode(callback func(*filetree.FileNode) error) func() error {
|
||||
return func() error {
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilesRemoveController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
|
||||
func (self *FilesRemoveController) context() *context.WorkingTreeContext {
|
||||
return self.c.Contexts().Files
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (self *FilteringMenuAction) setFiltering(path string) error {
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}, Then: func() {
|
||||
self.c.Contexts().LocalCommits.SetSelectedLineIdx(0)
|
||||
self.c.Contexts().LocalCommits.SetSelection(0)
|
||||
self.c.Contexts().LocalCommits.FocusLine()
|
||||
}})
|
||||
}
|
||||
|
||||
@@ -4,24 +4,29 @@ import (
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type GitFlowController struct {
|
||||
baseController
|
||||
*ListControllerTrait[*models.Branch]
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &GitFlowController{}
|
||||
|
||||
func NewGitFlowController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *GitFlowController {
|
||||
return &GitFlowController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
ListControllerTrait: NewListControllerTrait[*models.Branch](
|
||||
c,
|
||||
c.Contexts().Branches,
|
||||
c.Contexts().Branches.GetSelected,
|
||||
),
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +34,7 @@ func (self *GitFlowController) GetKeybindings(opts types.KeybindingsOpts) []*typ
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.ViewGitFlowOptions),
|
||||
Handler: self.checkSelected(self.handleCreateGitFlowMenu),
|
||||
Handler: self.withItem(self.handleCreateGitFlowMenu),
|
||||
Description: self.c.Tr.GitFlowOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
@@ -68,6 +73,7 @@ func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) er
|
||||
OnPress: func() error {
|
||||
return self.gitFlowFinishBranch(branch.Name)
|
||||
},
|
||||
DisabledReason: self.require(self.singleItemSelected())(),
|
||||
},
|
||||
{
|
||||
Label: "start feature",
|
||||
@@ -102,22 +108,3 @@ func (self *GitFlowController) gitFlowFinishBranch(branchName string) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.GitFlowFinish)
|
||||
return self.c.RunSubprocessAndRefresh(cmdObj)
|
||||
}
|
||||
|
||||
func (self *GitFlowController) checkSelected(callback func(*models.Branch) error) func() error {
|
||||
return func() error {
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *GitFlowController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
|
||||
func (self *GitFlowController) context() *context.BranchesContext {
|
||||
return self.c.Contexts().Branches
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ type GlobalController struct {
|
||||
}
|
||||
|
||||
func NewGlobalController(
|
||||
common *ControllerCommon,
|
||||
c *ControllerCommon,
|
||||
) *GlobalController {
|
||||
return &GlobalController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,23 +5,26 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/status"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type AppStatusHelper struct {
|
||||
c *HelperCommon
|
||||
|
||||
statusMgr func() *status.StatusManager
|
||||
statusMgr func() *status.StatusManager
|
||||
modeHelper *ModeHelper
|
||||
}
|
||||
|
||||
func NewAppStatusHelper(c *HelperCommon, statusMgr func() *status.StatusManager) *AppStatusHelper {
|
||||
func NewAppStatusHelper(c *HelperCommon, statusMgr func() *status.StatusManager, modeHelper *ModeHelper) *AppStatusHelper {
|
||||
return &AppStatusHelper{
|
||||
c: c,
|
||||
statusMgr: statusMgr,
|
||||
c: c,
|
||||
statusMgr: statusMgr,
|
||||
modeHelper: modeHelper,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *AppStatusHelper) Toast(message string) {
|
||||
func (self *AppStatusHelper) Toast(message string, kind types.ToastKind) {
|
||||
if self.c.RunningIntegrationTest() {
|
||||
// Don't bother showing toasts in integration tests. You can't check for
|
||||
// them anyway, and they would only slow down the test unnecessarily by
|
||||
@@ -29,7 +32,7 @@ func (self *AppStatusHelper) Toast(message string) {
|
||||
return
|
||||
}
|
||||
|
||||
self.statusMgr().AddToastStatus(message)
|
||||
self.statusMgr().AddToastStatus(message, kind)
|
||||
|
||||
self.renderAppStatus()
|
||||
}
|
||||
@@ -68,12 +71,25 @@ func (self *AppStatusHelper) WithWaitingStatus(message string, f func(gocui.Task
|
||||
})
|
||||
}
|
||||
|
||||
func (self *AppStatusHelper) WithWaitingStatusSync(message string, f func() error) {
|
||||
self.statusMgr().WithWaitingStatus(message, func() {}, func(*status.WaitingStatusHandle) {
|
||||
stop := make(chan struct{})
|
||||
defer func() { close(stop) }()
|
||||
self.renderAppStatusSync(stop)
|
||||
|
||||
if err := f(); err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (self *AppStatusHelper) HasStatus() bool {
|
||||
return self.statusMgr().HasStatus()
|
||||
}
|
||||
|
||||
func (self *AppStatusHelper) GetStatusString() string {
|
||||
return self.statusMgr().GetStatusString()
|
||||
appStatus, _ := self.statusMgr().GetStatusString()
|
||||
return appStatus
|
||||
}
|
||||
|
||||
func (self *AppStatusHelper) renderAppStatus() {
|
||||
@@ -81,7 +97,8 @@ func (self *AppStatusHelper) renderAppStatus() {
|
||||
ticker := time.NewTicker(time.Millisecond * utils.LoaderAnimationInterval)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
appStatus := self.statusMgr().GetStatusString()
|
||||
appStatus, color := self.statusMgr().GetStatusString()
|
||||
self.c.Views().AppStatus.FgColor = color
|
||||
self.c.OnUIThread(func() error {
|
||||
self.c.SetViewContent(self.c.Views().AppStatus, appStatus)
|
||||
return nil
|
||||
@@ -93,3 +110,38 @@ func (self *AppStatusHelper) renderAppStatus() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (self *AppStatusHelper) renderAppStatusSync(stop chan struct{}) {
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Millisecond * 50)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Forcing a re-layout and redraw after we added the waiting status;
|
||||
// this is needed in case the gui.showBottomLine config is set to false,
|
||||
// to make sure the bottom line appears. It's also useful for redrawing
|
||||
// once after each of several consecutive keypresses, e.g. pressing
|
||||
// ctrl-j to move a commit down several steps.
|
||||
_ = self.c.GocuiGui().ForceLayoutAndRedraw()
|
||||
|
||||
self.modeHelper.SetSuppressRebasingMode(true)
|
||||
defer func() { self.modeHelper.SetSuppressRebasingMode(false) }()
|
||||
|
||||
outer:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
appStatus, color := self.statusMgr().GetStatusString()
|
||||
self.c.Views().AppStatus.FgColor = color
|
||||
self.c.SetViewContent(self.c.Views().AppStatus, appStatus)
|
||||
// Redraw all views of the bottom line:
|
||||
bottomLineViews := []*gocui.View{
|
||||
self.c.Views().AppStatus, self.c.Views().Options, self.c.Views().Information,
|
||||
self.c.Views().StatusSpacer1, self.c.Views().StatusSpacer2,
|
||||
}
|
||||
_ = self.c.GocuiGui().ForceRedrawViews(bottomLineViews...)
|
||||
case <-stop:
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user