Compare commits
338 Commits
box-view-p
...
v0.44.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
611fabde11 | ||
|
|
f8073c7188 | ||
|
|
4dadcd2ace | ||
|
|
b18f12ca0f | ||
|
|
c67979abbb | ||
|
|
0e489bb5cc | ||
|
|
647f533e71 | ||
|
|
b22149d832 | ||
|
|
396215a5c9 | ||
|
|
42c157a5e6 | ||
|
|
a793f709b6 | ||
|
|
f74551e464 | ||
|
|
e06b1cef60 | ||
|
|
2d0c7cb0fc | ||
|
|
f5b8619ded | ||
|
|
4c6c915a77 | ||
|
|
064fae41e7 | ||
|
|
d4ef8e53d5 | ||
|
|
b15a1c7ae7 | ||
|
|
6f0182f11c | ||
|
|
371998e635 | ||
|
|
072b465fa6 | ||
|
|
8302575078 | ||
|
|
8edcd71234 | ||
|
|
5446683881 | ||
|
|
b91beb68e1 | ||
|
|
5659f1f3e9 | ||
|
|
753b16b697 | ||
|
|
da7a28c117 | ||
|
|
c35743d7ad | ||
|
|
f0eafabd6d | ||
|
|
fc4cf5d196 | ||
|
|
4e880e56c4 | ||
|
|
e8e39f5ce2 | ||
|
|
370ab2d19c | ||
|
|
fa8cd47227 | ||
|
|
9ec77bba91 | ||
|
|
4ec9262ff6 | ||
|
|
ba21d4e651 | ||
|
|
3cffed9412 | ||
|
|
90b8fd242d | ||
|
|
1191aca60f | ||
|
|
2f01af49e8 | ||
|
|
32fef9aadb | ||
|
|
717cb40f05 | ||
|
|
ef7d1a8602 | ||
|
|
a6656e307c | ||
|
|
442592a149 | ||
|
|
ac335907ae | ||
|
|
b07ce19b9a | ||
|
|
079c5a9905 | ||
|
|
a2c4fad410 | ||
|
|
567e898e22 | ||
|
|
14a29d6d6f | ||
|
|
8e2ed8c538 | ||
|
|
6ad4ffea3b | ||
|
|
770d51634c | ||
|
|
c51e13941c | ||
|
|
05ae0800c3 | ||
|
|
4bbfe7b3cc | ||
|
|
00530477c9 | ||
|
|
90c1334535 | ||
|
|
83ef031922 | ||
|
|
820c2bc0fd | ||
|
|
a484954d74 | ||
|
|
56be1a4950 | ||
|
|
1e8eb72b2f | ||
|
|
18935bf7f0 | ||
|
|
457c4c248d | ||
|
|
8c553dcde9 | ||
|
|
a709caf138 | ||
|
|
8dea2dab88 | ||
|
|
63aa32c521 | ||
|
|
24841f22f1 | ||
|
|
d712c2f199 | ||
|
|
4525216d26 | ||
|
|
ae61da7485 | ||
|
|
fb9f6153fc | ||
|
|
a0c808842b | ||
|
|
8a8490d97d | ||
|
|
68c7f9840a | ||
|
|
8d37f48744 | ||
|
|
37f32755b5 | ||
|
|
5380fe2483 | ||
|
|
8e71df3e53 | ||
|
|
30f43a245b | ||
|
|
283ed29f10 | ||
|
|
ccd39bb8ae | ||
|
|
db40653202 | ||
|
|
d3940729eb | ||
|
|
56a6ee6afb | ||
|
|
a37a3fc4a1 | ||
|
|
af87cd1dd6 | ||
|
|
e675025411 | ||
|
|
c3d5798c6c | ||
|
|
15d17e16dd | ||
|
|
926061557b | ||
|
|
8522337f32 | ||
|
|
6114f69ee5 | ||
|
|
0aa351443f | ||
|
|
c28ecabfd8 | ||
|
|
bbd779b437 | ||
|
|
b411897a5a | ||
|
|
fb97c30080 | ||
|
|
61b59837bb | ||
|
|
e65c0a9d5e | ||
|
|
3f7674a2e9 | ||
|
|
fb81fc6057 | ||
|
|
e1acb6a547 | ||
|
|
250eb14de1 | ||
|
|
61ae5e16ae | ||
|
|
e1efbfc842 | ||
|
|
7d486cabeb | ||
|
|
c2e953a09f | ||
|
|
59450c7d12 | ||
|
|
ca9e006cca | ||
|
|
387bdb1b84 | ||
|
|
5fb98655b3 | ||
|
|
addfa2f961 | ||
|
|
7679b109cb | ||
|
|
6a418c65ca | ||
|
|
aa55995924 | ||
|
|
5431c269d3 | ||
|
|
73696629d9 | ||
|
|
38b1255119 | ||
|
|
57de11b709 | ||
|
|
ce50533689 | ||
|
|
18ad975573 | ||
|
|
fd8e480363 | ||
|
|
3d6d677453 | ||
|
|
aef8e71b82 | ||
|
|
5872779faa | ||
|
|
04d7907864 | ||
|
|
65a2eccfdb | ||
|
|
4a272afc67 | ||
|
|
2499a6c8a3 | ||
|
|
74ed1ac584 | ||
|
|
d6d48f2866 | ||
|
|
f98b57aa5e | ||
|
|
f114321322 | ||
|
|
940700dc56 | ||
|
|
be0fa98d11 | ||
|
|
e71fc43952 | ||
|
|
cd0f72d66a | ||
|
|
48bb3a9dce | ||
|
|
55d8e801f1 | ||
|
|
54765d2236 | ||
|
|
dbf6716b9b | ||
|
|
a19bb0bbfe | ||
|
|
75a95865ff | ||
|
|
07dd8a2b07 | ||
|
|
8bcfa3660a | ||
|
|
0e4d266a52 | ||
|
|
7676572358 | ||
|
|
7fb758cc1d | ||
|
|
22f0d9cdd3 | ||
|
|
1cb29cea15 | ||
|
|
1eb5d89f1d | ||
|
|
d570552206 | ||
|
|
41f41ee4ee | ||
|
|
94d6f4dae7 | ||
|
|
f30387e7f5 | ||
|
|
3a8b97841f | ||
|
|
df3afb1b89 | ||
|
|
7f935c1ea8 | ||
|
|
7ed94c0410 | ||
|
|
8e15451117 | ||
|
|
111d10fe5c | ||
|
|
98335361fd | ||
|
|
bd36b8a95e | ||
|
|
d89dc967b8 | ||
|
|
dbca9306de | ||
|
|
0cbe08b105 | ||
|
|
af5b19a9da | ||
|
|
da8e4e44b7 | ||
|
|
69666d1089 | ||
|
|
b37d6dcd1c | ||
|
|
c72be6cbf3 | ||
|
|
5a3049485c | ||
|
|
39e77d1823 | ||
|
|
da94ee7a9a | ||
|
|
62ca873ddd | ||
|
|
9404c2309c | ||
|
|
4a2508e960 | ||
|
|
ce6388bdfa | ||
|
|
58d7467180 | ||
|
|
cb53e377a8 | ||
|
|
a3560eb451 | ||
|
|
ef4fd70f9c | ||
|
|
74fe069da9 | ||
|
|
542030f190 | ||
|
|
206b2c6f0b | ||
|
|
dc87592876 | ||
|
|
37f35da436 | ||
|
|
f598da0df2 | ||
|
|
20ccb03a45 | ||
|
|
891362dfb2 | ||
|
|
463cf35e64 | ||
|
|
1919a2d2d6 | ||
|
|
71ad3fac63 | ||
|
|
ae4a579153 | ||
|
|
da86096e19 | ||
|
|
e1d973d62a | ||
|
|
0489b11c0c | ||
|
|
c7a9b9ca07 | ||
|
|
f2db9fa3f9 | ||
|
|
b9107d5fc8 | ||
|
|
bfe2dd4ed8 | ||
|
|
7652d579f5 | ||
|
|
73bcbe4ee2 | ||
|
|
cd01e4e7c2 | ||
|
|
c5de9cfd8e | ||
|
|
1129e0e4a0 | ||
|
|
981f1fa7aa | ||
|
|
cad4581d05 | ||
|
|
f0af42270e | ||
|
|
e0377f2bce | ||
|
|
968060a5ec | ||
|
|
07fe828f60 | ||
|
|
b004b2e275 | ||
|
|
7a670964cd | ||
|
|
a138a31c72 | ||
|
|
31456a8caa | ||
|
|
22764cd207 | ||
|
|
be21328c69 | ||
|
|
3d14893c65 | ||
|
|
8813587961 | ||
|
|
13bd4b964f | ||
|
|
eae76a97e9 | ||
|
|
34214af41f | ||
|
|
38aa5b89ab | ||
|
|
2e5b570bb6 | ||
|
|
a5eec48b4b | ||
|
|
94cf2cc7df | ||
|
|
ac30aee1b8 | ||
|
|
d3780fd57d | ||
|
|
5959f7bc8e | ||
|
|
436240bbeb | ||
|
|
2317dac730 | ||
|
|
08bd36ea78 | ||
|
|
ab31991e13 | ||
|
|
4df8f87715 | ||
|
|
62a018c1d4 | ||
|
|
b26ff43d9e | ||
|
|
a047fba9f7 | ||
|
|
974016cfba | ||
|
|
5b6dbe57b1 | ||
|
|
92d4073b2a | ||
|
|
c27a7e0816 | ||
|
|
f3c9443ec8 | ||
|
|
71a62b7573 | ||
|
|
61313e5dfa | ||
|
|
94a1c27916 | ||
|
|
6da4f9a787 | ||
|
|
398ceb1dd9 | ||
|
|
6788825ed3 | ||
|
|
2335772db6 | ||
|
|
1285554cb2 | ||
|
|
232be05785 | ||
|
|
8990026358 | ||
|
|
bd782f16dd | ||
|
|
7be82d4713 | ||
|
|
5dbdbd8425 | ||
|
|
9b73b68f95 | ||
|
|
c3715d0f86 | ||
|
|
696b8ba457 | ||
|
|
e205c6ba7f | ||
|
|
c6df856079 | ||
|
|
26c3e0d333 | ||
|
|
4b6479b25f | ||
|
|
1ab1fb3599 | ||
|
|
1d502d3245 | ||
|
|
bb01648521 | ||
|
|
32cfe7a5c3 | ||
|
|
b795d91fa8 | ||
|
|
ccc620e5fc | ||
|
|
34d7afc0e9 | ||
|
|
22dc7fece9 | ||
|
|
c3d1a79a89 | ||
|
|
63a523c2fc | ||
|
|
d146d834c2 | ||
|
|
68edfa20b4 | ||
|
|
bfe9f233ac | ||
|
|
741d9a26b6 | ||
|
|
98e5fd70fb | ||
|
|
b34de70c99 | ||
|
|
1e123e2124 | ||
|
|
02aeb6101c | ||
|
|
2ccd9980e3 | ||
|
|
db0a1586d9 | ||
|
|
4e441127f3 | ||
|
|
cf40a5b077 | ||
|
|
26132cf5bd | ||
|
|
a67eda39a5 | ||
|
|
8e1464f720 | ||
|
|
a62a5089d6 | ||
|
|
4967e5136e | ||
|
|
68c966567c | ||
|
|
20a4aeab6e | ||
|
|
a5620ebe3a | ||
|
|
1b245ef5f6 | ||
|
|
a08c86c182 | ||
|
|
4cd15a36e3 | ||
|
|
13a35408e6 | ||
|
|
1a76a7da09 | ||
|
|
8a16f24ecb | ||
|
|
e2b4d9cff3 | ||
|
|
b2c457366a | ||
|
|
8b8343b8a9 | ||
|
|
f98da780de | ||
|
|
c401f34530 | ||
|
|
ddd6323aa5 | ||
|
|
7e92dbfd3d | ||
|
|
dbc21af3b1 | ||
|
|
6f8244e3fe | ||
|
|
cf27fd827b | ||
|
|
a7c97400c6 | ||
|
|
5e9fe2be80 | ||
|
|
d406ec06af | ||
|
|
7ec784e2a0 | ||
|
|
93af0016f7 | ||
|
|
a171ec4294 | ||
|
|
deee5fa957 | ||
|
|
44160ef844 | ||
|
|
dd2bffc278 | ||
|
|
9eb9b369ff | ||
|
|
fafa4280f5 | ||
|
|
5a5cd849d1 | ||
|
|
6a6316cfb6 | ||
|
|
779e6f95a3 | ||
|
|
44ad36bb39 | ||
|
|
08a8b067a5 | ||
|
|
15b25b5afb | ||
|
|
27ad75de16 | ||
|
|
3af545daf7 | ||
|
|
57f9493770 | ||
|
|
629b7ba1b8 | ||
|
|
43e04febee |
7
.codespellrc
Normal file
7
.codespellrc
Normal file
@@ -0,0 +1,7 @@
|
||||
[codespell]
|
||||
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
|
||||
skip = .git*,go.sum,*.lock,.codespellrc,vendor,translations,Keybindings_*.md
|
||||
check-hidden = true
|
||||
# camel-cased
|
||||
ignore-regex = (\b[A-Za-z][a-z]*[A-Z]\S+\b|\.edn\b|\S+…|\\nd\b)
|
||||
ignore-words-list = fomrat,inbetween
|
||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.go text
|
||||
*.md text eol=lf
|
||||
*.json text eol=lf
|
||||
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -6,6 +6,7 @@
|
||||
* [ ] 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))
|
||||
* [ ] If a new UserConfig entry was added, make sure it can be hot-reloaded (see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
|
||||
* [ ] Docs have been updated if necessary
|
||||
* [ ] You've read through your own file changes for silly mistakes etc
|
||||
|
||||
|
||||
4
.github/workflows/cd.yml
vendored
4
.github/workflows/cd.yml
vendored
@@ -10,11 +10,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Unshallow repo
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: Run goreleaser
|
||||
|
||||
51
.github/workflows/ci.yml
vendored
51
.github/workflows/ci.yml
vendored
@@ -28,9 +28,9 @@ jobs:
|
||||
GOFLAGS: -mod=vendor
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: Test code
|
||||
@@ -61,11 +61,11 @@ jobs:
|
||||
GOFLAGS: -mod=vendor
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Restore Git cache
|
||||
if: matrix.git-version != 'latest'
|
||||
id: cache-git-restore
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ~/git-${{matrix.git-version}}
|
||||
key: ${{runner.os}}-git-${{matrix.git-version}}
|
||||
@@ -82,12 +82,12 @@ jobs:
|
||||
run: sudo make -C "$HOME/git-${{matrix.git-version}}" -j install
|
||||
- name: Save Git cache
|
||||
if: steps.cache-git-restore.outputs.cache-hit != 'true' && matrix.git-version != 'latest'
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: ~/git-${{matrix.git-version}}
|
||||
key: ${{runner.os}}-git-${{matrix.git-version}}
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: Print git version
|
||||
@@ -111,9 +111,9 @@ jobs:
|
||||
GOARCH: amd64
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: Build linux binary
|
||||
@@ -138,9 +138,9 @@ jobs:
|
||||
GOARCH: amd64
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: Check Vendor Directory
|
||||
@@ -164,15 +164,15 @@ jobs:
|
||||
GOFLAGS: -mod=vendor
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v3.7.0
|
||||
uses: golangci/golangci-lint-action@v6.1.0
|
||||
with:
|
||||
version: v1.58
|
||||
version: v1.60
|
||||
- name: errors
|
||||
run: golangci-lint run
|
||||
if: ${{ failure() }}
|
||||
@@ -192,10 +192,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
|
||||
@@ -219,3 +219,22 @@ jobs:
|
||||
CODACY_PROJECT_TOKEN=${{ secrets.CODACY_PROJECT_TOKEN }} \
|
||||
bash <(curl -Ls https://coverage.codacy.com/get.sh) report \
|
||||
--force-coverage-parser go -r coverage.out
|
||||
|
||||
check-for-fixups:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref != 'refs/heads/master'
|
||||
steps:
|
||||
# See https://github.com/actions/checkout/issues/552#issuecomment-1167086216
|
||||
- name: "PR commits"
|
||||
run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} ))" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: "Checkout PR branch and all PR commits"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: ${{ env.PR_FETCH_DEPTH }}
|
||||
|
||||
- name: Check for fixups
|
||||
run: |
|
||||
./scripts/check_for_fixups.sh ${{ github.event.pull_request.base.ref }}
|
||||
|
||||
25
.github/workflows/codespell.yml
vendored
Normal file
25
.github/workflows/codespell.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Codespell configuration is within .codespellrc
|
||||
---
|
||||
name: Codespell
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Annotate locations with typos
|
||||
uses: codespell-project/codespell-problem-matcher@v1
|
||||
- name: Codespell
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
2
.github/workflows/sponsors.yml
vendored
2
.github/workflows/sponsors.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate Sponsors 💖
|
||||
uses: JamesIves/github-sponsors-readme-action@v1.2.2
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
|
||||
# Hidden
|
||||
.*
|
||||
!.codespellrc
|
||||
|
||||
# Notes
|
||||
*.notes
|
||||
@@ -19,6 +20,7 @@ lazygit.exe
|
||||
|
||||
# Exceptions
|
||||
!.gitignore
|
||||
!.gitattributes
|
||||
!.goreleaser.yml
|
||||
!.golangci.yml
|
||||
!.circleci/
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
linters:
|
||||
disable:
|
||||
- structcheck # gives false positives
|
||||
enable:
|
||||
- gofumpt
|
||||
- thelper
|
||||
- goimports
|
||||
- tparallel
|
||||
- wastedassign
|
||||
- exportloopref
|
||||
- unparam
|
||||
- prealloc
|
||||
- unconvert
|
||||
|
||||
@@ -9,7 +9,7 @@ RUN go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build
|
||||
|
||||
FROM alpine:3.15
|
||||
FROM alpine:3.19
|
||||
RUN apk add --no-cache -U git xdg-utils
|
||||
WORKDIR /go/src/github.com/jesseduffield/lazygit/
|
||||
COPY --from=build /go/src/github.com/jesseduffield/lazygit ./
|
||||
|
||||
26
cmd/i18n/main.go
Normal file
26
cmd/i18n/main.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
)
|
||||
|
||||
func saveLanguageFileToJson(tr *i18n.TranslationSet, filepath string) error {
|
||||
jsonData, err := json.MarshalIndent(tr, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonData = append(jsonData, '\n')
|
||||
return os.WriteFile(filepath, jsonData, 0o644)
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := saveLanguageFileToJson(i18n.EnglishTranslationSet(), "en.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# User Config
|
||||
|
||||
Default path for the config file:
|
||||
Default path for the global config file:
|
||||
|
||||
- Linux: `~/.config/lazygit/config.yml`
|
||||
- MacOS: `~/Library/Application\ Support/lazygit/config.yml`
|
||||
@@ -16,6 +16,8 @@ If you want to change the config directory:
|
||||
|
||||
- MacOS: `export XDG_CONFIG_HOME="$HOME/.config"`
|
||||
|
||||
In addition to the global config file you can create repo-specific config files in `<repo>/.git/lazygit.yml`. Settings in these files override settings in the global config file. In addition, files called `.lazygit.yml` in any of the parent directories of a repo will also be loaded; this can be useful if you have settings that you want to apply to a group of repositories.
|
||||
|
||||
JSON schema is available for `config.yml` so that IntelliSense in Visual Studio Code (completion and error checking) is automatically enabled when the [YAML Red Hat][yaml] extension is installed. However, note that automatic schema detection only works if your config file is in one of the standard paths mentioned above. If you override the path to the file, you can still make IntelliSense work by adding
|
||||
|
||||
```yaml
|
||||
@@ -122,6 +124,10 @@ gui:
|
||||
selectedLineBgColor:
|
||||
- blue
|
||||
|
||||
# Background color of selected line when view doesn't have focus.
|
||||
inactiveViewSelectedLineBgColor:
|
||||
- bold
|
||||
|
||||
# Foreground color of copied commit
|
||||
cherryPickedCommitFgColor:
|
||||
- blue
|
||||
@@ -181,6 +187,12 @@ gui:
|
||||
# If true (default), file icons are shown in the file views. Only relevant if NerdFontsVersion is not empty.
|
||||
showFileIcons: true
|
||||
|
||||
# Length of author name in (non-expanded) commits view. 2 means show initials only.
|
||||
commitAuthorShortLength: 2
|
||||
|
||||
# Length of author name in expanded commits view. 2 means show initials only.
|
||||
commitAuthorLongLength: 17
|
||||
|
||||
# Length of commit hash in commits view. 0 shows '*' if NF icons aren't on.
|
||||
commitHashLength: 8
|
||||
|
||||
@@ -234,6 +246,12 @@ gui:
|
||||
# One of 'dashboard' (default) | 'allBranchesLog'
|
||||
statusPanelView: dashboard
|
||||
|
||||
# If true, jump to the Files panel after popping a stash
|
||||
switchToFilesAfterStashPop: true
|
||||
|
||||
# If true, jump to the Files panel after applying a stash
|
||||
switchToFilesAfterStashApply: true
|
||||
|
||||
# Config relating to git
|
||||
git:
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
|
||||
@@ -273,6 +291,9 @@ git:
|
||||
# Extra args passed to `git merge`, e.g. --no-ff
|
||||
args: ""
|
||||
|
||||
# The commit message to use for a squash merge commit. Can contain "{{selectedRef}}" and "{{currentBranch}}" placeholders.
|
||||
squashMergeMessage: Squash merge {{selectedRef}} into {{currentBranch}}
|
||||
|
||||
# list of branches that are considered 'main' branches, used when displaying commits
|
||||
mainBranches:
|
||||
- master
|
||||
@@ -290,10 +311,17 @@ git:
|
||||
# If true, pass the --all arg to git fetch
|
||||
fetchAll: true
|
||||
|
||||
# If true, lazygit will automatically stage files that used to have merge
|
||||
# conflicts but no longer do; and it will also ask you if you want to
|
||||
# continue a merge or rebase if you've resolved all conflicts. If false, it
|
||||
# won't do either of these things.
|
||||
autoStageResolvedConflicts: true
|
||||
|
||||
# Command used when displaying the current branch git log in the main window
|
||||
branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --
|
||||
|
||||
# Command used to display git log of all branches in the main window
|
||||
# Command used to display git log of all branches in the main window.
|
||||
# Deprecated: User `allBranchesLogCmds` instead.
|
||||
allBranchesLogCmd: git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium
|
||||
|
||||
# If true, do not spawn a separate process when using GPG
|
||||
@@ -310,6 +338,9 @@ git:
|
||||
# Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] "
|
||||
replace: ""
|
||||
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-branch-name-prefix
|
||||
branchPrefix: ""
|
||||
|
||||
# If true, parse emoji strings in commit messages e.g. render :rocket: as 🚀
|
||||
# (This should really be under 'gui', not 'git')
|
||||
parseEmoji: false
|
||||
@@ -406,9 +437,13 @@ os:
|
||||
openLinkCommand: ""
|
||||
|
||||
# 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
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
|
||||
copyToClipboardCmd: ""
|
||||
|
||||
# ReadFromClipboardCmd is the command for reading the clipboard.
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
|
||||
readFromClipboardCmd: ""
|
||||
|
||||
# If true, don't display introductory popups upon opening Lazygit.
|
||||
disableStartupPopups: false
|
||||
|
||||
@@ -474,7 +509,7 @@ keybinding:
|
||||
scrollDownMain-alt1: J
|
||||
scrollUpMain-alt2: <c-u>
|
||||
scrollDownMain-alt2: <c-d>
|
||||
executeCustomCommand: ':'
|
||||
executeShellCommand: ':'
|
||||
createRebaseOptionsMenu: m
|
||||
|
||||
# 'Files' appended for legacy reasons
|
||||
@@ -500,6 +535,8 @@ keybinding:
|
||||
toggleWhitespaceInDiffView: <c-w>
|
||||
increaseContextInDiffView: '}'
|
||||
decreaseContextInDiffView: '{'
|
||||
increaseRenameSimilarityThreshold: )
|
||||
decreaseRenameSimilarityThreshold: (
|
||||
openDiffTool: <c-t>
|
||||
status:
|
||||
checkForUpdate: u
|
||||
@@ -611,7 +648,7 @@ os:
|
||||
open: 'open {{filename}}'
|
||||
```
|
||||
|
||||
## Custom Command for Copying to Clipboard
|
||||
## Custom Command for Copying to and Pasting from Clipboard
|
||||
```yaml
|
||||
os:
|
||||
copyToClipboardCmd: ''
|
||||
@@ -624,6 +661,12 @@ os:
|
||||
copyToClipboardCmd: printf "\033]52;c;$(printf {{text}} | base64)\a" > /dev/tty
|
||||
```
|
||||
|
||||
A custom command for reading from the clipboard can be set using
|
||||
```yaml
|
||||
os:
|
||||
readFromClipboardCmd: ''
|
||||
```
|
||||
It is used, for example, when pasting a commit message into the commit message panel. The command is supposed to output the clipboard content to stdout.
|
||||
|
||||
## Configuring File Editing
|
||||
|
||||
@@ -636,7 +679,7 @@ os:
|
||||
editPreset: 'vscode'
|
||||
```
|
||||
|
||||
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.
|
||||
Supported presets are `vim`, `nvim`, `nvim-remote`, `lvim`, `emacs`, `nano`, `micro`, `vscode`, `sublime`, `bbedit`, `kakoune`, `helix`, `xcode`, and `zed`. 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.
|
||||
|
||||
@@ -862,6 +905,21 @@ git:
|
||||
replace: '[$1] '
|
||||
```
|
||||
|
||||
## Predefined branch name prefix
|
||||
|
||||
In situations where certain naming pattern is used for branches, this can be used to populate new branch creation with a static prefix.
|
||||
|
||||
Example:
|
||||
|
||||
Some branches:
|
||||
- jsmith/AB-123
|
||||
- cwilson/AB-125
|
||||
|
||||
```yaml
|
||||
git:
|
||||
branchPrefix: "firstlast/"
|
||||
```
|
||||
|
||||
## Custom git log command
|
||||
|
||||
You can override the `git log` command that's used to render the log of the selected branch like so:
|
||||
|
||||
@@ -87,6 +87,11 @@ The permitted contexts are:
|
||||
| stash | The 'Stash' tab |
|
||||
| global | This keybinding will take affect everywhere |
|
||||
|
||||
> **Bonus**
|
||||
>
|
||||
> You can use a comma-separated string, such as `context: 'commits, subCommits'`, to make it effective in multiple contexts.
|
||||
|
||||
|
||||
## Prompts
|
||||
|
||||
### Common fields
|
||||
@@ -291,9 +296,7 @@ Here's an example using a command but not specifying anything else: so each line
|
||||
Your commands can contain placeholder strings using Go's [template syntax](https://jan.newmarch.name/golang/template/chapter-template.html). The template syntax is pretty powerful, letting you do things like conditionals if you want, but for the most part you'll simply want to be accessing the fields on the following objects:
|
||||
|
||||
```
|
||||
SelectedLocalCommit
|
||||
SelectedReflogCommit
|
||||
SelectedSubCommit
|
||||
SelectedCommit
|
||||
SelectedFile
|
||||
SelectedPath
|
||||
SelectedLocalBranch
|
||||
@@ -306,6 +309,9 @@ SelectedWorktree
|
||||
CheckedOutBranch
|
||||
```
|
||||
|
||||
(For legacy reasons, `SelectedLocalCommit`, `SelectedReflogCommit`, and `SelectedSubCommit` are also available, but they are deprecated.)
|
||||
|
||||
|
||||
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/services/custom_commands/models.go) (all the modelling lives in the same file).
|
||||
|
||||
## Keybinding collisions
|
||||
|
||||
@@ -26,6 +26,8 @@ git:
|
||||
|
||||

|
||||
|
||||
A cool feature of delta is --hyperlinks, which renders clickable links for the line numbers in the left margin, and lazygit supports these. To use them, set the `pager:` config to `delta --dark --paging=never --line-numbers --hyperlinks --hyperlinks-file-link-format="lazygit-edit://{path}:{line}"`; this allows you to click on an underlined line number in the diff to jump right to that same line in your editor.
|
||||
|
||||
## Diff-so-fancy
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Documentation Overview
|
||||
|
||||
* [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)
|
||||
# Documentation Overview
|
||||
|
||||
* [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)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 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`: Contains startup code, initialises 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`.
|
||||
@@ -12,7 +12,7 @@
|
||||
* `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/config`: Contains code relating to the Lazygit user config. Specifically `pkg/config/user_config/go` defines the user config struct and its default values. See [below](#using-userconfig) for some important information about using it.
|
||||
* `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
|
||||
@@ -86,6 +86,12 @@ The event loop is managed in the `MainLoop` function of `vendor/github.com/jesse
|
||||
|
||||
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)`.
|
||||
|
||||
## Using UserConfig
|
||||
|
||||
The UserConfig struct is loaded from lazygit's global config file (and possibly repo-specific config files). It can be re-loaded while lazygit is running, e.g. when the user edits one of the config files. In this case we should make sure that any new or changed config values take effect immediately. The easiest way to achieve this is what we do in most controllers or helpers: these have a pointer to the `common.Common` struct, which contains the UserConfig, and access it from there. Since the UserConfig instance in `common.Common` is updated whenever we reload the config, the code can be sure that it always uses an up-to-date value, and there's nothing else to do.
|
||||
|
||||
If that's not possible for some reason, see if you can add code to `Gui.onUserConfigLoaded` to update things from the new config; there are some examples in that function to use as a guide. If that's too hard to do too, add the config to the list in `Gui.checkForChangedConfigsThatDontAutoReload` so that the user is asked to quit and restart lazygit.
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -122,7 +122,7 @@ is this:
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
+ "github.com/samber/lo"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -308,9 +309,5 @@ func (self *FixupHelper) blameAddedLines(addedLineHunks []*hunk) ([]string, erro
|
||||
@@ -308,9 +309,5 @@ func (self *FixupHelper) blameAddedLines(addedLineHunks []*hunk) ([]string, error
|
||||
func findCommit(hash string) (*models.Commit, int, bool) {
|
||||
- for i, commit := range self.c.Model().Commits {
|
||||
- if commit.Hash == hash {
|
||||
|
||||
@@ -14,9 +14,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
|
||||
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
|
||||
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
|
||||
| `` : `` | Execute custom command | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | View custom patch options | |
|
||||
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
|
||||
| `` R `` | Refresh | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
@@ -162,13 +164,14 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` F `` | Force checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
||||
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` M `` | Merge | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
|
||||
| `` T `` | New tag | |
|
||||
| `` s `` | Sort order | |
|
||||
| `` g `` | Reset | |
|
||||
| `` R `` | Rename branch | |
|
||||
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | View commits | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -265,12 +268,13 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <c-o> `` | Copy branch name to clipboard | |
|
||||
| `` <space> `` | Checkout | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
|
||||
| `` n `` | New branch | |
|
||||
| `` M `` | Merge | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
||||
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
|
||||
| `` s `` | Sort order | |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | View commits | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -307,7 +311,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` e `` | Edit config file | Open file in external editor. |
|
||||
| `` u `` | Check for update | |
|
||||
| `` <enter> `` | Switch to a recent repo | |
|
||||
| `` a `` | Show all branch logs | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
|
||||
## Sub-commits
|
||||
|
||||
@@ -349,6 +353,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
||||
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | View commits | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
@@ -14,9 +14,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` @ `` | コマンドログメニューを開く | View options for the command log e.g. show/hide the command log and focus the command log. |
|
||||
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
|
||||
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
|
||||
| `` : `` | カスタムコマンドを実行 | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | View custom patch options | |
|
||||
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
|
||||
| `` R `` | リフレッシュ | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
@@ -183,6 +185,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
||||
| `` P `` | タグをpush | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | コミットを閲覧 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -232,13 +235,14 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` F `` | Force checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
||||
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` M `` | 現在のブランチにマージ | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | 現在のブランチにマージ | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
|
||||
| `` T `` | タグを作成 | |
|
||||
| `` s `` | 並び替え | |
|
||||
| `` g `` | Reset | |
|
||||
| `` R `` | ブランチ名を変更 | |
|
||||
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | コミットを閲覧 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -329,12 +333,13 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <c-o> `` | ブランチ名をクリップボードにコピー | |
|
||||
| `` <space> `` | チェックアウト | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
|
||||
| `` n `` | 新しいブランチを作成 | |
|
||||
| `` M `` | 現在のブランチにマージ | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | 現在のブランチにマージ | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
||||
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
|
||||
| `` s `` | 並び替え | |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | コミットを閲覧 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
@@ -14,9 +14,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` @ `` | 명령어 로그 메뉴 열기 | View options for the command log e.g. show/hide the command log and focus the command log. |
|
||||
| `` P `` | 푸시 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` p `` | 업데이트 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기 | Increase the amount of the context shown around changes in the diff view. |
|
||||
| `` { `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기 | Decrease the amount of the context shown around changes in the diff view. |
|
||||
| `` : `` | Execute custom command | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | 커스텀 Patch 옵션 보기 | |
|
||||
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
|
||||
| `` R `` | 새로고침 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
@@ -189,13 +191,14 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` F `` | 강제 체크아웃 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
||||
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` M `` | 현재 브랜치에 병합 | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` f `` | Fast-forward this branch from its upstream | Fast-forward selected branch from its upstream. |
|
||||
| `` T `` | 태그를 생성 | |
|
||||
| `` s `` | Sort order | |
|
||||
| `` g `` | View reset options | |
|
||||
| `` R `` | 브랜치 이름 변경 | |
|
||||
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | 커밋 보기 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -242,12 +245,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | 브랜치명을 클립보드에 복사 | |
|
||||
| `` <space> `` | 체크아웃 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
|
||||
| `` n `` | 새 브랜치 생성 | |
|
||||
| `` M `` | 현재 브랜치에 병합 | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
||||
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
|
||||
| `` s `` | Sort order | |
|
||||
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | 커밋 보기 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -322,6 +326,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
||||
| `` P `` | 태그를 push | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | 커밋 보기 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
@@ -14,9 +14,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
|
||||
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
|
||||
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
|
||||
| `` : `` | Voer aangepaste commando uit | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | Bekijk aangepaste patch opties | |
|
||||
| `` m `` | Bekijk merge/rebase opties | View options to abort/continue/skip the current merge/rebase. |
|
||||
| `` R `` | Verversen | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
@@ -101,13 +103,14 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` F `` | Forceer checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
||||
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` M `` | Merge in met huidige checked out branch | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | Merge in met huidige checked out branch | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` f `` | Fast-forward deze branch vanaf zijn upstream | Fast-forward selected branch from its upstream. |
|
||||
| `` T `` | Creëer tag | |
|
||||
| `` s `` | Sort order | |
|
||||
| `` g `` | Bekijk reset opties | |
|
||||
| `` R `` | Hernoem branch | |
|
||||
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | Bekijk commits | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -243,12 +246,13 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <c-o> `` | Kopieer branch name naar klembord | |
|
||||
| `` <space> `` | Uitchecken | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
|
||||
| `` n `` | Nieuwe branch | |
|
||||
| `` M `` | Merge in met huidige checked out branch | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | Merge in met huidige checked out branch | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
||||
| `` u `` | Set as upstream | Stel in als upstream van uitgecheckte branch |
|
||||
| `` s `` | Sort order | |
|
||||
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | Bekijk commits | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -349,6 +353,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
||||
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | Bekijk commits | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
@@ -14,9 +14,11 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` @ `` | Pokaż opcje dziennika poleceń | Pokaż opcje dla dziennika poleceń, np. pokazywanie/ukrywanie dziennika poleceń i skupienie na dzienniku poleceń. |
|
||||
| `` P `` | Wypchnij | Wypchnij bieżącą gałąź do jej gałęzi nadrzędnej. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. |
|
||||
| `` p `` | Pociągnij | Pociągnij zmiany z zdalnego dla bieżącej gałęzi. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | Zwiększ rozmiar kontekstu w widoku różnic | Zwiększ ilość kontekstu pokazywanego wokół zmian w widoku różnic. |
|
||||
| `` { `` | Zmniejsz rozmiar kontekstu w widoku różnic | Zmniejsz ilość kontekstu pokazywanego wokół zmian w widoku różnic. |
|
||||
| `` : `` | Wykonaj polecenie niestandardowe | Wyświetl monit, w którym możesz wprowadzić polecenie powłoki do wykonania. Nie należy mylić z wcześniej skonfigurowanymi poleceniami niestandardowymi. |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | Wyświetl opcje niestandardowej łatki | |
|
||||
| `` m `` | Pokaż opcje scalania/rebase | Pokaż opcje do przerwania/kontynuowania/pominięcia bieżącego scalania/rebase. |
|
||||
| `` R `` | Odśwież | Odśwież stan git (tj. uruchom `git status`, `git branch`, itp. w tle, aby zaktualizować zawartość paneli). To nie uruchamia `git fetch`. |
|
||||
@@ -134,6 +136,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
|
||||
| `` g `` | Reset | |
|
||||
| `` R `` | Zmień nazwę gałęzi | |
|
||||
| `` u `` | Pokaż opcje upstream | Pokaż opcje dotyczące upstream gałęzi, np. ustawianie/usuwanie upstream i resetowanie do upstream. |
|
||||
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
|
||||
| `` <enter> `` | Pokaż commity | |
|
||||
| `` w `` | Zobacz opcje drzewa pracy | |
|
||||
| `` / `` | Filtruj bieżący widok po tekście | |
|
||||
@@ -331,6 +334,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
|
||||
| `` d `` | Usuń | Wyświetl opcje usuwania lokalnego/odległego tagu. |
|
||||
| `` P `` | Wyślij tag | Wyślij wybrany tag do zdalnego. Zostaniesz poproszony o wybranie zdalnego. |
|
||||
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
|
||||
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
|
||||
| `` <enter> `` | Pokaż commity | |
|
||||
| `` w `` | Zobacz opcje drzewa pracy | |
|
||||
| `` / `` | Filtruj bieżący widok po tekście | |
|
||||
@@ -359,6 +363,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
|
||||
| `` u `` | Ustaw jako upstream | Ustaw wybraną gałąź zdalną jako upstream sprawdzonej gałęzi. |
|
||||
| `` s `` | Kolejność sortowania | |
|
||||
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
|
||||
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
|
||||
| `` <enter> `` | Pokaż commity | |
|
||||
| `` w `` | Zobacz opcje drzewa pracy | |
|
||||
| `` / `` | Filtruj bieżący widok po tekście | |
|
||||
|
||||
@@ -14,9 +14,11 @@ _Связки клавиш_
|
||||
| `` @ `` | Открыть меню журнала команд | View options for the command log e.g. show/hide the command log and focus the command log. |
|
||||
| `` P `` | Отправить изменения | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` p `` | Получить и слить изменения | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Increase the amount of the context shown around changes in the diff view. |
|
||||
| `` { `` | Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Decrease the amount of the context shown around changes in the diff view. |
|
||||
| `` : `` | Выполнить пользовательскую команду | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | Просмотреть пользовательские параметры патча | |
|
||||
| `` m `` | Просмотреть параметры слияния/перебазирования | View options to abort/continue/skip the current merge/rebase. |
|
||||
| `` R `` | Обновить | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
@@ -189,13 +191,14 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` F `` | Принудительное переключение | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
||||
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` M `` | Слияние с текущей переключённой веткой | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` f `` | Перемотать эту ветку вперёд из её upstream-ветки | Fast-forward selected branch from its upstream. |
|
||||
| `` T `` | Создать тег | |
|
||||
| `` s `` | Порядок сортировки | |
|
||||
| `` g `` | Просмотреть параметры сброса | |
|
||||
| `` R `` | Переименовать ветку | |
|
||||
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | Просмотреть коммиты | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -288,6 +291,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
||||
| `` P `` | Отправить тег | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | Просмотреть коммиты | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
@@ -299,12 +303,13 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <c-o> `` | Скопировать название ветки в буфер обмена | |
|
||||
| `` <space> `` | Переключить | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
|
||||
| `` n `` | Новая ветка | |
|
||||
| `` M `` | Слияние с текущей переключённой веткой | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
||||
| `` u `` | Set as upstream | Установить как upstream-ветку переключённую ветку |
|
||||
| `` s `` | Порядок сортировки | |
|
||||
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <enter> `` | Просмотреть коммиты | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
@@ -2,7 +2,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
|
||||
# Lazygit 按键绑定
|
||||
|
||||
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
_图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
|
||||
## 全局键绑定
|
||||
|
||||
@@ -11,26 +11,28 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-r> `` | 切换到最近的仓库 | |
|
||||
| `` <pgup> (fn+up/shift+k) `` | 向上滚动主面板 | |
|
||||
| `` <pgdown> (fn+down/shift+j) `` | 向下滚动主面板 | |
|
||||
| `` @ `` | 打开命令日志菜单 | View options for the command log e.g. show/hide the command log and focus the command log. |
|
||||
| `` P `` | 推送 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` p `` | 拉取 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` } `` | 扩大差异视图中显示的上下文范围 | Increase the amount of the context shown around changes in the diff view. |
|
||||
| `` { `` | 缩小差异视图中显示的上下文范围 | Decrease the amount of the context shown around changes in the diff view. |
|
||||
| `` : `` | 执行自定义命令 | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
|
||||
| `` @ `` | 打开命令日志菜单 | 查看命令日志的选项,例如显示/隐藏命令日志以及聚焦命令日志 |
|
||||
| `` P `` | 推送 | 推送当前分支到它的上游。如果上游为配置,你可以在弹窗中配置上游分支。 |
|
||||
| `` p `` | 拉取 | 从当前分支的远程分支获取改动。如果上游为配置,你可以在弹窗中配置上游分支。 |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | 扩大差异视图中显示的上下文范围 | 增加diff视图中围绕更改显示的上下文数量 |
|
||||
| `` { `` | 缩小差异视图中显示的上下文范围 | 减少diff视图中围绕更改显示的上下文数量 |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | 查看自定义补丁选项 | |
|
||||
| `` m `` | 查看 合并/变基 选项 | View options to abort/continue/skip the current merge/rebase. |
|
||||
| `` R `` | 刷新 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
| `` + `` | 下一屏模式(正常/半屏/全屏) | |
|
||||
| `` m `` | 查看 合并/变基 选项 | 查看当前合并或变基的中止、继续、跳过选项 |
|
||||
| `` R `` | 刷新 | 刷新git状态(即在后台上运行`git status`,`git branch`等命令以更新面板内容) 不会运行`git fetch` |
|
||||
| `` + `` | 下一屏模式(正常/半屏/全屏) | |
|
||||
| `` _ `` | 上一屏模式 | |
|
||||
| `` ? `` | 打开菜单 | |
|
||||
| `` <c-s> `` | 查看按路径过滤选项 | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | 打开 diff 菜单 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | 打开 diff 菜单 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-s> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 |
|
||||
| `` W `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 |
|
||||
| `` <c-e> `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 |
|
||||
| `` q `` | 退出 | |
|
||||
| `` <esc> `` | 取消 | |
|
||||
| `` <c-w> `` | 切换是否在差异视图中显示空白字符差异 | Toggle whether or not whitespace changes are shown in the diff view. |
|
||||
| `` z `` | (通过 reflog)撤销「实验功能」 | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
|
||||
| `` <c-z> `` | (通过 reflog)重做「实验功能」 | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
|
||||
| `` <c-w> `` | 切换是否在差异视图中显示空白字符差异 | 切换是否在diff视图中显示空白更改 |
|
||||
| `` z `` | (通过 reflog)撤销「实验功能」 | Reflog将用于确定运行哪个git命令来撤消最后一个git命令。这并不包括对工作树的更改,只考虑提交。 |
|
||||
| `` <c-z> `` | (通过 reflog)重做「实验功能」 | Reflog将用于确定运行哪个git命令来重做上一个git命令。这并不包括对工作树的更改,只考虑提交。 |
|
||||
|
||||
## 列表面板导航
|
||||
|
||||
@@ -41,8 +43,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` < `` | 滚动到顶部 | |
|
||||
| `` > `` | 滚动到底部 | |
|
||||
| `` v `` | 切换拖动选择 | |
|
||||
| `` <s-down> `` | Range select down | |
|
||||
| `` <s-up> `` | Range select up | |
|
||||
| `` <s-down> `` | 向下扩展选择范围 | |
|
||||
| `` <s-up> `` | 向上扩展选择范围 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
| `` H `` | 向左滚动 | |
|
||||
| `` L `` | 向右滚动 | |
|
||||
@@ -54,27 +56,17 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
||||
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如,hash、URL、diff、消息、作者)。 |
|
||||
| `` o `` | 在浏览器中打开提交 | |
|
||||
| `` n `` | 从提交创建新分支 | |
|
||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | 复制提交(拣选) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` <enter> `` | 查看提交 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Worktrees
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` n `` | New worktree | |
|
||||
| `` <space> `` | Switch | Switch to the selected worktree. |
|
||||
| `` o `` | Open in editor | |
|
||||
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` w `` | 查看工作区选项 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 分支页面
|
||||
|
||||
@@ -82,41 +74,42 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
|
||||
| `` i `` | 显示 git-flow 选项 | |
|
||||
| `` <space> `` | 检出 | Checkout selected item. |
|
||||
| `` <space> `` | 检出 | 检出选中的项目 |
|
||||
| `` n `` | 新分支 | |
|
||||
| `` o `` | 创建抓取请求 | |
|
||||
| `` O `` | 创建抓取请求选项 | |
|
||||
| `` <c-y> `` | 将抓取请求 URL 复制到剪贴板 | |
|
||||
| `` c `` | 按名称检出 | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
|
||||
| `` F `` | 强制检出 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
||||
| `` r `` | 将已检出的分支变基到该分支 | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` c `` | 按名称检出 | 按名称检出。在输入框中,您可以输入'-' 来切换到最后一个分支。 |
|
||||
| `` F `` | 强制检出 | 强制检出所选分支。这将在检出所选分支之前放弃工作目录中的所有本地更改。 |
|
||||
| `` d `` | 删除 | 查看本地/远程分支的删除选项 |
|
||||
| `` r `` | 将已检出的分支变基到该分支 | 将检出的分支变基到所选的分支上。 |
|
||||
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
|
||||
| `` f `` | 从上游快进此分支 | Fast-forward selected branch from its upstream. |
|
||||
| `` f `` | 从上游快进此分支 | 将当前分支直接移动到远程追踪分支的最新提交 |
|
||||
| `` T `` | 创建标签 | |
|
||||
| `` s `` | Sort order | |
|
||||
| `` s `` | 排序 | |
|
||||
| `` g `` | 查看重置选项 | |
|
||||
| `` R `` | 重命名分支 | |
|
||||
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
|
||||
| `` u `` | 查看上游选项 | 查看与分支上游相关的选项,例如设置/取消设置上游和重置为上游。 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` <enter> `` | 查看提交 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` w `` | 查看工作区选项 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 子提交
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
||||
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如,hash、URL、diff、消息、作者)。 |
|
||||
| `` o `` | 在浏览器中打开提交 | |
|
||||
| `` n `` | 从提交创建新分支 | |
|
||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | 复制提交(拣选) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` <enter> `` | 查看提交的文件 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` w `` | 查看工作区选项 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 子模块
|
||||
@@ -124,104 +117,114 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将子模块名称复制到剪贴板 | |
|
||||
| `` <enter> `` | Enter | 输入子模块 |
|
||||
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
|
||||
| `` u `` | Update | 更新子模块 |
|
||||
| `` <enter> `` | 进入 | 输入子模块 |
|
||||
| `` d `` | 删除 | 删除选定的子模块及其相应的目录 |
|
||||
| `` u `` | 更新 | 更新子模块 |
|
||||
| `` n `` | 添加新的子模块 | |
|
||||
| `` e `` | 更新子模块 URL | |
|
||||
| `` i `` | Initialize | 初始化子模块 |
|
||||
| `` i `` | 初始化 | 初始化子模块 |
|
||||
| `` b `` | 查看批量子模块选项 | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 工作区
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` n `` | 新建工作树 | |
|
||||
| `` <space> `` | 切换 | 切换到选中的工作树 |
|
||||
| `` o `` | 在编辑器中编写 | |
|
||||
| `` d `` | 删除 | 删除选定的工作树。这将删除工作树的目录以及 .git 目录中有关工作树的元数据。 |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 提交
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` b `` | 查看二分查找选项 | |
|
||||
| `` s `` | 压缩 | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
| `` f `` | 修正(fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
|
||||
| `` r `` | 改写提交 | Reword the selected commit's message. |
|
||||
| `` s `` | 压缩(Squash) | 将已选提交压缩到该提交之下。这些选定的提交的消息会附加到该提交的消息之下。 |
|
||||
| `` f `` | 修正(fixup) | 将选定的提交合并到其下面的提交中。与压缩类似,但所选提交的消息将被丢弃。 |
|
||||
| `` r `` | 改写提交 | 重写所选提交的消息。 |
|
||||
| `` R `` | 使用编辑器重命名提交 | |
|
||||
| `` d `` | 删除提交 | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
|
||||
| `` e `` | Edit (start interactive rebase) | 编辑提交 |
|
||||
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
|
||||
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
|
||||
| `` p `` | Pick | 选择提交(变基过程中) |
|
||||
| `` d `` | 删除提交 | 删除选中的提交。这将通过变基从分支中删除该提交,如果该提交修改的内容依赖于后续的提交,则需要解决合并冲突。 |
|
||||
| `` e `` | 编辑(开始交互式变基) | 编辑提交 |
|
||||
| `` i `` | 开始交互式变基 | 为分支上的提交启动交互式变基。这将包括从 HEAD 提交到第一个合并提交或主分支提交的所有提交。
|
||||
如果您想从所选提交启动交互式变基,请按 `e`。 |
|
||||
| `` p `` | 拣选(Pick) | 选择提交(变基过程中) |
|
||||
| `` F `` | 为此提交创建修正 | 创建修正提交 |
|
||||
| `` S `` | Apply fixup commits | 压缩在所选提交之上的所有“fixup!”提交(自动压缩) |
|
||||
| `` S `` | 应用该修复提交 | 压缩在所选提交之上的所有“fixup!”提交(自动压缩) |
|
||||
| `` <c-j> `` | 下移提交 | |
|
||||
| `` <c-k> `` | 上移提交 | |
|
||||
| `` V `` | 粘贴提交(拣选) | |
|
||||
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
|
||||
| `` A `` | Amend | 用已暂存的更改来修补提交 |
|
||||
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
|
||||
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
|
||||
| `` T `` | 标签提交 | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
|
||||
| `` <c-l> `` | 打开日志菜单 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
||||
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` V `` | 粘贴提交(拣选) | |
|
||||
| `` B `` | 标记一个主提交用于变基 | 选择下一次变基的主提交。当您变基到一个分支时,只有高于主提交的提交才会被引入。这使用“git rebase --onto”命令。 |
|
||||
| `` A `` | 修补(Amend) | 用已暂存的变更来修补提交 |
|
||||
| `` a `` | 修补提交属性 | 设置或重置提交的作者,或添加其他作者。 |
|
||||
| `` t `` | 撤销(Revert) | 为所选提交创建还原提交,这会反向应用所选提交的更改。 |
|
||||
| `` T `` | 标签提交 | 创建一个新标签指向所选提交。你可以在弹窗中输入标签名称和描述(可选)。 |
|
||||
| `` <c-l> `` | 打开日志菜单 | 查看提交日志的选项,例如更改排序顺序、隐藏 git graph、显示整个 git graph。 |
|
||||
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如,hash、URL、diff、消息、作者)。 |
|
||||
| `` o `` | 在浏览器中打开提交 | |
|
||||
| `` n `` | 从提交创建新分支 | |
|
||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | 复制提交(拣选) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` <enter> `` | 查看提交的文件 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` w `` | 查看工作区选项 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 提交信息
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <enter> `` | 确认 | |
|
||||
| `` <esc> `` | 关闭 | |
|
||||
|
||||
## 提交文件
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将文件名复制到剪贴板 | |
|
||||
| `` c `` | 检出 | 检出文件 |
|
||||
| `` d `` | Remove | 放弃对此文件的提交更改 |
|
||||
| `` o `` | 打开文件 | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <space> `` | 补丁中包含的切换文件 | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
|
||||
| `` a `` | Toggle all files | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
|
||||
| `` <enter> `` | 输入文件以将所选行添加到补丁中(或切换目录折叠) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
|
||||
| `` ` `` | 切换文件树视图 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
|
||||
| `` d `` | 删除 | 放弃对此文件的提交变更 |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` <space> `` | 补丁中包含的切换文件 | 切换文件是否包含在自定义补丁中。请参阅 https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches。 |
|
||||
| `` a `` | 操作所有文件 | 添加或删除所有提交中的文件到自定义的补丁中。请参阅 https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches。 |
|
||||
| `` <enter> `` | 输入文件以将所选行添加到补丁中(或切换目录折叠) | 如果已选择一个文件,则Enter进入该文件,以便您可以向自定义补丁添加/删除单独的行。如果选择了目录,则切换目录。 |
|
||||
| `` ` `` | 切换文件树视图 | 在平铺部署与树布局之间切换文件视图。平铺布局在一个列表中展示所有文件路径,树布局则根据目录分组展示。 |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 提交讯息
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <enter> `` | 确认 | |
|
||||
| `` <esc> `` | 关闭 | |
|
||||
|
||||
## 文件
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将文件名复制到剪贴板 | |
|
||||
| `` <space> `` | 切换暂存状态 | Toggle staged for selected file. |
|
||||
| `` <c-b> `` | Filter files by status | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | 提交更改 | Commit staged changes. |
|
||||
| `` w `` | 提交更改而无需预先提交钩子 | |
|
||||
| `` <space> `` | 切换暂存状态 | 为选定的文件切换暂存状态 |
|
||||
| `` <c-b> `` | 通过状态过滤文件 | |
|
||||
| `` y `` | 复制到剪贴板 | |
|
||||
| `` c `` | 提交变更 | 提交暂存文件 |
|
||||
| `` w `` | 提交变更而无需预先提交钩子 | |
|
||||
| `` A `` | 修补最后一次提交 | |
|
||||
| `` C `` | 提交更改(使用编辑器编辑提交信息) | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` o `` | 打开文件 | Open file in default application. |
|
||||
| `` C `` | 提交变更(使用编辑器编辑提交信息) | |
|
||||
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` i `` | 忽略文件 | |
|
||||
| `` r `` | 刷新文件 | |
|
||||
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
|
||||
| `` S `` | 查看贮藏选项 | View stash options (e.g. stash all, stash staged, stash unstaged). |
|
||||
| `` a `` | 切换所有文件的暂存状态 | Toggle staged/unstaged for all files in working tree. |
|
||||
| `` <enter> `` | 暂存单个 块/行 用于文件, 或 折叠/展开 目录 | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
|
||||
| `` d `` | 查看'放弃更改'选项 | View options for discarding changes to the selected file. |
|
||||
| `` s `` | 贮藏 | 贮藏所有变更.若要使用其他贮藏变体,请使用查看贮藏选项快捷键 |
|
||||
| `` S `` | 查看贮藏选项 | 查看贮藏选项(例如:贮藏所有、贮藏已暂存变更、贮藏未暂存变更) |
|
||||
| `` a `` | 切换所有文件的暂存状态 | 切换工作区中所有文件的已暂存/未暂存状态 |
|
||||
| `` <enter> `` | 暂存单个 块/行 用于文件, 或 折叠/展开 目录 | 如果选中的是一个文件,则会进入到暂存视图,以便可以暂存单个代码块/行。如果选中的是一个目录,则会折叠/展开这个目录 |
|
||||
| `` d `` | 查看'放弃变更'选项 | 查看选中文件的放弃变更选项 |
|
||||
| `` g `` | 查看上游重置选项 | |
|
||||
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` ` `` | 切换文件树视图 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` M `` | 打开外部合并工具 (git mergetool) | Run `git mergetool`. |
|
||||
| `` f `` | 抓取 | Fetch changes from remote. |
|
||||
| `` D `` | 重置 | 查看工作树的重置选项(例如:清除工作树)。 |
|
||||
| `` ` `` | 切换文件树视图 | 在平铺部署与树布局之间切换文件视图。平铺布局在一个列表中展示所有文件路径,树布局则根据目录分组展示。 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
|
||||
| `` f `` | 抓取 | 从远程获取变更 |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 构建补丁中
|
||||
@@ -231,10 +234,10 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <left> `` | 选择上一个区块 | |
|
||||
| `` <right> `` | 选择下一个区块 | |
|
||||
| `` v `` | 切换拖动选择 | |
|
||||
| `` a `` | 切换选择区块 | Toggle hunk selection mode. |
|
||||
| `` a `` | 切换选择代码块 | 切换代码块选择模式 |
|
||||
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
|
||||
| `` o `` | 打开文件 | Open file in default application. |
|
||||
| `` e `` | 编辑文件 | Open file in external editor. |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||
| `` <space> `` | 添加/移除 行到补丁 | |
|
||||
| `` <esc> `` | 退出逐行模式 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
@@ -243,14 +246,15 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <space> `` | 检出 | Checkout the selected tag tag as a detached HEAD. |
|
||||
| `` n `` | 创建标签 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
||||
| `` P `` | 推送标签 | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <space> `` | 检出 | 检出选择的标签作为分离的HEAD |
|
||||
| `` n `` | 创建标签 | 基于当前提交创建一个新标签。你将在弹窗中输入标签名称和描述(可选)。 |
|
||||
| `` d `` | 删除 | 查看本地/远程标签的删除选项 |
|
||||
| `` P `` | 推送标签 | 推送选择的标签到远端。你将在弹窗中选择一个远端。 |
|
||||
| `` g `` | 重置 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` <enter> `` | 查看提交 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` w `` | 查看工作区选项 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 正在合并
|
||||
|
||||
@@ -262,10 +266,10 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <down> `` | 选择底部块 | |
|
||||
| `` <left> `` | 选择上一个冲突 | |
|
||||
| `` <right> `` | 选择下一个冲突 | |
|
||||
| `` z `` | 撤销 | Undo last merge conflict resolution. |
|
||||
| `` e `` | 编辑文件 | Open file in external editor. |
|
||||
| `` o `` | 打开文件 | Open file in default application. |
|
||||
| `` M `` | 打开外部合并工具 (git mergetool) | Run `git mergetool`. |
|
||||
| `` z `` | 撤销 | 撤消上次合并冲突解决 |
|
||||
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
|
||||
| `` <esc> `` | 返回文件面板 | |
|
||||
|
||||
## 正在暂存
|
||||
@@ -275,19 +279,19 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <left> `` | 选择上一个区块 | |
|
||||
| `` <right> `` | 选择下一个区块 | |
|
||||
| `` v `` | 切换拖动选择 | |
|
||||
| `` a `` | 切换选择区块 | Toggle hunk selection mode. |
|
||||
| `` a `` | 切换选择代码块 | 切换代码块选择模式 |
|
||||
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
|
||||
| `` <space> `` | 切换暂存状态 | 切换行暂存状态 |
|
||||
| `` d `` | 取消变更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` o `` | 打开文件 | Open file in default application. |
|
||||
| `` e `` | 编辑文件 | Open file in external editor. |
|
||||
| `` d `` | 取消变更(git reset) | 当选择未暂存的变更时,使用git reset丢弃该变更。当选择已暂存的变更时,取消暂存该变更 |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||
| `` <esc> `` | 返回文件面板 | |
|
||||
| `` <tab> `` | 切换到其他面板 | Switch to other view (staged/unstaged changes). |
|
||||
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
|
||||
| `` c `` | 提交更改 | Commit staged changes. |
|
||||
| `` w `` | 提交更改而无需预先提交钩子 | |
|
||||
| `` C `` | 提交更改(使用编辑器编辑提交信息) | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` <tab> `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) |
|
||||
| `` E `` | 编辑代码块 | 在外部编辑器中编辑选中的代码块 |
|
||||
| `` c `` | 提交变更 | 提交暂存文件 |
|
||||
| `` w `` | 提交变更而无需预先提交钩子 | |
|
||||
| `` C `` | 提交变更(使用编辑器编辑提交信息) | |
|
||||
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 正常
|
||||
@@ -301,8 +305,8 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` o `` | 打开配置文件 | Open file in default application. |
|
||||
| `` e `` | 编辑配置文件 | Open file in external editor. |
|
||||
| `` o `` | 打开配置文件 | 使用默认程序打开该文件 |
|
||||
| `` e `` | 编辑配置文件 | 使用外部编辑器打开文件 |
|
||||
| `` u `` | 检查更新 | |
|
||||
| `` <enter> `` | 切换到最近的仓库 | |
|
||||
| `` a `` | 显示所有分支的日志 | |
|
||||
@@ -320,45 +324,46 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
|-----|--------|-------------|
|
||||
| `` <enter> `` | 执行 | |
|
||||
| `` <esc> `` | 关闭 | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 贮藏
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <space> `` | 应用 | Apply the stash entry to your working directory. |
|
||||
| `` g `` | 应用并删除 | Apply the stash entry to your working directory and remove the stash entry. |
|
||||
| `` d `` | 删除 | Remove the stash entry from the stash list. |
|
||||
| `` n `` | 新分支 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
|
||||
| `` r `` | Rename stash | |
|
||||
| `` <space> `` | 应用 | 将贮藏项应用到您的工作目录。 |
|
||||
| `` g `` | 应用并删除 | 将存储项应用到工作目录并删除存储项。 |
|
||||
| `` d `` | 删除 | 从贮藏列表中删除该贮藏项 |
|
||||
| `` n `` | 新分支 | 从选定的贮藏项创建一个新分支。这是通过 git 检查创建贮藏项的提交,从该提交创建一个新分支,然后将贮藏项作为附加提交应用到新分支来实现的。 |
|
||||
| `` r `` | 重命名贮藏 | |
|
||||
| `` <enter> `` | 查看提交的文件 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` w `` | 查看工作区选项 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 远程分支
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
|
||||
| `` <space> `` | 检出 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
|
||||
| `` <space> `` | 检出 | 基于当前选中的远程分支检出一个新的本地分支,或者将远程分支作分离的HEAD。 |
|
||||
| `` n `` | 新分支 | |
|
||||
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
|
||||
| `` r `` | 将已检出的分支变基到该分支 | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
||||
| `` u `` | Set as upstream | 设置为检出分支的上游 |
|
||||
| `` s `` | Sort order | |
|
||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` r `` | 将已检出的分支变基到该分支 | 将检出的分支变基到所选的分支上。 |
|
||||
| `` d `` | 删除 | 从远程删除远程分支。 |
|
||||
| `` u `` | 设置为上游 | 设置为检出分支的上游 |
|
||||
| `` s `` | 排序 | |
|
||||
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` <enter> `` | 查看提交 | |
|
||||
| `` w `` | View worktree options | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` w `` | 查看工作区选项 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 远程页面
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <enter> `` | View branches | |
|
||||
| `` <enter> `` | 查看分支 | |
|
||||
| `` n `` | 添加新的远程仓库 | |
|
||||
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
|
||||
| `` e `` | Edit | 编辑远程仓库 |
|
||||
| `` d `` | 删除 | 删除选中的远程。从远程跟踪远程分支的任何本地分支都不会受到影响。 |
|
||||
| `` e `` | 编辑 | 编辑远程仓库 |
|
||||
| `` f `` | 抓取 | 抓取远程仓库 |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
@@ -12,11 +12,13 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` <pgup> (fn+up/shift+k) `` | 向上捲動主面板 | |
|
||||
| `` <pgdown> (fn+down/shift+j) `` | 向下捲動主面板 | |
|
||||
| `` @ `` | 開啟命令記錄選單 | View options for the command log e.g. show/hide the command log and focus the command log. |
|
||||
| `` P `` | 推送 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` p `` | 拉取 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
|
||||
| `` P `` | 推送 | 推送到遠端。如果沒有設定遠端,會開啟設定視窗。 |
|
||||
| `` p `` | 拉取 | 從遠端同步當前分支。如果沒有設定遠端,會開啟設定視窗。 |
|
||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
||||
| `` } `` | 增加差異檢視中顯示變更周圍上下文的大小 | Increase the amount of the context shown around changes in the diff view. |
|
||||
| `` { `` | 減小差異檢視中顯示變更周圍上下文的大小 | Decrease the amount of the context shown around changes in the diff view. |
|
||||
| `` : `` | 執行自訂命令 | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
|
||||
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
|
||||
| `` <c-p> `` | 檢視自訂補丁選項 | |
|
||||
| `` m `` | 查看合併/變基選項 | View options to abort/continue/skip the current merge/rebase. |
|
||||
| `` R `` | 重新整理 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
@@ -58,8 +60,8 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` v `` | 切換拖曳選擇 | |
|
||||
| `` a `` | 切換選擇程式碼塊 | Toggle hunk selection mode. |
|
||||
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
|
||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
||||
| `` e `` | 編輯檔案 | Open file in external editor. |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
|
||||
| `` <esc> `` | 退出自訂補丁建立器 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
@@ -82,9 +84,9 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` <left> `` | 選擇上一個衝突 | |
|
||||
| `` <right> `` | 選擇下一個衝突 | |
|
||||
| `` z `` | 復原 | Undo last merge conflict resolution. |
|
||||
| `` e `` | 編輯檔案 | Open file in external editor. |
|
||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
||||
| `` M `` | 開啟外部合併工具 (git mergetool) | Run `git mergetool`. |
|
||||
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
|
||||
| `` <esc> `` | 返回檔案面板 | |
|
||||
|
||||
## 主面板(預存)
|
||||
@@ -98,12 +100,12 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
|
||||
| `` <space> `` | 切換預存 | 切換現有行的狀態 (已預存/未預存) |
|
||||
| `` d `` | 刪除變更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
||||
| `` e `` | 編輯檔案 | Open file in external editor. |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||
| `` <esc> `` | 返回檔案面板 | |
|
||||
| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
|
||||
| `` E `` | 編輯程式碼塊 | Edit selected hunk in external editor. |
|
||||
| `` c `` | 提交變更 | Commit staged changes. |
|
||||
| `` c `` | 提交變更 | 提交暫存區變更 |
|
||||
| `` w `` | 沒有預提交 hook 就提交更改 | |
|
||||
| `` C `` | 使用 git 編輯器提交變更 | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
@@ -129,7 +131,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` <enter> `` | 檢視所選項目的檔案 | |
|
||||
| `` w `` | 檢視工作目錄選項 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
@@ -154,7 +156,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
|-----|--------|-------------|
|
||||
| `` n `` | New worktree | |
|
||||
| `` <space> `` | Switch | Switch to the selected worktree. |
|
||||
| `` o `` | Open in editor | |
|
||||
| `` o `` | 在編輯器中開啟 | |
|
||||
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
|
||||
| `` / `` | 搜尋 | |
|
||||
|
||||
@@ -167,22 +169,22 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` b `` | 查看二分選項 | |
|
||||
| `` s `` | 壓縮 (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
| `` f `` | 修復 (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
|
||||
| `` r `` | 改寫提交 | Reword the selected commit's message. |
|
||||
| `` r `` | 改寫提交 | 改寫選中的提交訊息 |
|
||||
| `` R `` | 使用編輯器改寫提交 | |
|
||||
| `` d `` | 刪除提交 | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
|
||||
| `` e `` | Edit (start interactive rebase) | 編輯提交 |
|
||||
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
|
||||
| `` e `` | 編輯(開始互動變基) | 編輯提交 |
|
||||
| `` i `` | 開始互動變基 | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
|
||||
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
|
||||
| `` p `` | Pick | 挑選提交 (於變基過程中) |
|
||||
| `` p `` | 挑選 | 挑選提交 (於變基過程中) |
|
||||
| `` F `` | 建立修復提交 | 為此提交建立修復提交 |
|
||||
| `` S `` | 壓縮上方所有「fixup」提交(自動壓縮) | 是否壓縮上方 {{.commit}} 所有「fixup」提交? |
|
||||
| `` <c-j> `` | 向下移動提交 | |
|
||||
| `` <c-k> `` | 向上移動提交 | |
|
||||
| `` V `` | 貼上提交 (揀選) | |
|
||||
| `` B `` | 為了變基已標注提交為基準提交 | 請為了下一次變基選擇一項基準提交;此將執行 `git rebase --onto`。 |
|
||||
| `` A `` | Amend | 使用已預存的更改修正提交 |
|
||||
| `` A `` | 修改 | 使用已預存的更改修正提交 |
|
||||
| `` a `` | 設定/重設提交作者 | Set/Reset commit author or set co-author. |
|
||||
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
|
||||
| `` t `` | 還原 | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
|
||||
| `` T `` | 打標籤到提交 | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
|
||||
| `` <c-l> `` | 開啟記錄選單 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
||||
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
|
||||
@@ -191,7 +193,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` n `` | 從提交建立新分支 | |
|
||||
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` <enter> `` | 檢視所選項目的檔案 | |
|
||||
| `` w `` | 檢視工作目錄選項 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
@@ -210,9 +212,9 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
||||
| `` c `` | 檢出 | 檢出檔案 |
|
||||
| `` d `` | Remove | Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file. |
|
||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` e `` | 編輯 | 使用外部編輯器開啟 |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` <space> `` | 切換檔案是否包含在補丁中 | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
|
||||
| `` a `` | 切換所有檔案是否包含在補丁中 | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
|
||||
| `` <enter> `` | 輸入檔案以將選定的行添加至補丁(或切換目錄折疊) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
|
||||
@@ -244,7 +246,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` <enter> `` | 檢視提交 | |
|
||||
| `` w `` | 檢視工作目錄選項 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
@@ -255,22 +257,23 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
|
||||
| `` i `` | 顯示 git-flow 選項 | |
|
||||
| `` <space> `` | 檢出 | Checkout selected item. |
|
||||
| `` <space> `` | 檢出 | 檢出選定的項目。 |
|
||||
| `` n `` | 新分支 | |
|
||||
| `` o `` | 建立拉取請求 | |
|
||||
| `` O `` | 建立拉取請求選項 | |
|
||||
| `` <c-y> `` | 複製拉取請求的 URL 到剪貼板 | |
|
||||
| `` c `` | 根據名稱檢出 | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
|
||||
| `` F `` | 強制檢出 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
||||
| `` d `` | 刪除 | View delete options for local/remote branch. |
|
||||
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` M `` | 合併到當前檢出的分支 | Merge selected branch into currently checked out branch. |
|
||||
| `` f `` | 從上游快進此分支 | Fast-forward selected branch from its upstream. |
|
||||
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` f `` | 從上游快進此分支 | 從遠端快進所選的分支 |
|
||||
| `` T `` | 建立標籤 | |
|
||||
| `` s `` | Sort order | |
|
||||
| `` s `` | 排序規則 | |
|
||||
| `` g `` | 檢視重設選項 | |
|
||||
| `` R `` | 重新命名分支 | |
|
||||
| `` u `` | 檢視上游設定 | 檢視有關上游分支的設定(例如重設至上游) |
|
||||
| `` u `` | 檢視遠端設定 | 檢視有關遠端分支的設定(例如重設至遠端) |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` <enter> `` | 檢視提交 | |
|
||||
| `` w `` | 檢視工作目錄選項 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
@@ -281,9 +284,10 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
|-----|--------|-------------|
|
||||
| `` <space> `` | 檢出 | Checkout the selected tag tag as a detached HEAD. |
|
||||
| `` n `` | 建立標籤 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
||||
| `` d `` | 刪除 | View delete options for local/remote tag. |
|
||||
| `` P `` | 推送標籤 | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` g `` | 重設 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` <enter> `` | 檢視提交 | |
|
||||
| `` w `` | 檢視工作目錄選項 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
@@ -295,35 +299,35 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
||||
| `` <space> `` | 切換預存 | Toggle staged for selected file. |
|
||||
| `` <c-b> `` | 篩選檔案 (預存/未預存) | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | 提交變更 | Commit staged changes. |
|
||||
| `` y `` | 複製到剪貼簿 | |
|
||||
| `` c `` | 提交變更 | 提交暫存區變更 |
|
||||
| `` w `` | 沒有預提交 hook 就提交更改 | |
|
||||
| `` A `` | 修改上次提交 | |
|
||||
| `` C `` | 使用 git 編輯器提交變更 | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
||||
| `` e `` | 編輯 | 使用外部編輯器開啟 |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` i `` | 忽略或排除檔案 | |
|
||||
| `` r `` | 重新整理檔案 | |
|
||||
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
|
||||
| `` s `` | 收藏 | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
|
||||
| `` S `` | 檢視收藏選項 | View stash options (e.g. stash all, stash staged, stash unstaged). |
|
||||
| `` a `` | 全部預存/取消預存 | Toggle staged/unstaged for all files in working tree. |
|
||||
| `` <enter> `` | 選擇檔案中的單個程式碼塊/行,或展開/折疊目錄 | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
|
||||
| `` d `` | Discard | View options for discarding changes to the selected file. |
|
||||
| `` g `` | 檢視上游重設選項 | |
|
||||
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` d `` | 捨棄 | 檢視選中變動進行捨棄復原 |
|
||||
| `` g `` | 檢視遠端重設選項 | |
|
||||
| `` D `` | 重設 | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` M `` | 開啟外部合併工具 (git mergetool) | Run `git mergetool`. |
|
||||
| `` f `` | 擷取 | Fetch changes from remote. |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
|
||||
| `` f `` | 擷取 | 同步遠端異動 |
|
||||
| `` / `` | 搜尋 | |
|
||||
|
||||
## 狀態
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` o `` | 開啟設定檔案 | Open file in default application. |
|
||||
| `` e `` | 編輯設定檔案 | Open file in external editor. |
|
||||
| `` o `` | 開啟設定檔案 | 使用預設軟體開啟 |
|
||||
| `` e `` | 編輯設定檔案 | 使用外部編輯器開啟 |
|
||||
| `` u `` | 檢查更新 | |
|
||||
| `` <enter> `` | 切換到最近使用的版本庫 | |
|
||||
| `` a `` | 顯示所有分支日誌 | |
|
||||
@@ -342,7 +346,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <enter> `` | View branches | |
|
||||
| `` n `` | 新增遠端 | |
|
||||
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
|
||||
| `` e `` | Edit | 編輯遠端 |
|
||||
| `` e `` | 編輯 | 編輯遠端 |
|
||||
| `` f `` | 擷取 | 擷取遠端 |
|
||||
| `` / `` | 搜尋 | |
|
||||
|
||||
@@ -353,12 +357,13 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
|
||||
| `` <space> `` | 檢出 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
|
||||
| `` n `` | 新分支 | |
|
||||
| `` M `` | 合併到當前檢出的分支 | Merge selected branch into currently checked out branch. |
|
||||
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
|
||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
||||
| `` u `` | Set as upstream | 將此分支設為當前分支之上游 |
|
||||
| `` s `` | Sort order | |
|
||||
| `` d `` | 刪除 | Delete the remote branch from the remote. |
|
||||
| `` u `` | 設置為遠端 | 將此分支設為當前分支之遠端 |
|
||||
| `` s `` | 排序規則 | |
|
||||
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` <enter> `` | 檢視提交 | |
|
||||
| `` w `` | 檢視工作目錄選項 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
|
||||
12
go.mod
12
go.mod
@@ -16,7 +16,7 @@ require (
|
||||
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.20240418080333-8cd33929c513
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240906064314-bfab49c720d7
|
||||
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
|
||||
@@ -24,7 +24,7 @@ require (
|
||||
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/mattn/go-runewidth v0.0.16
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
@@ -38,7 +38,7 @@ require (
|
||||
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
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sync v0.8.0
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -75,8 +75,8 @@ require (
|
||||
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.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
22
go.sum
22
go.sum
@@ -188,8 +188,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
||||
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.20240418080333-8cd33929c513 h1:Y1bw5iItrsDCumATc/rklIJ/6K+68ieiWZJedhrNuXo=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240906064314-bfab49c720d7 h1:QeLCKRAt4T6sBg5tSrOc4OojCuAcPxUA+4vNMPY4aH4=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240906064314-bfab49c720d7/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
|
||||
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=
|
||||
@@ -235,8 +235,9 @@ 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.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -423,8 +424,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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=
|
||||
@@ -474,14 +475,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
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=
|
||||
@@ -491,8 +492,9 @@ 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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
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=
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/spf13/afero"
|
||||
|
||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
@@ -64,22 +63,20 @@ func Run(
|
||||
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
||||
userConfig := config.GetUserConfig()
|
||||
appState := config.GetAppState()
|
||||
|
||||
var err error
|
||||
log := newLogger(config)
|
||||
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Initialize with English for the time being; the real translation set for
|
||||
// the configured language will be read after reading the user config
|
||||
tr := i18n.EnglishTranslationSet()
|
||||
|
||||
return &common.Common{
|
||||
Log: log,
|
||||
Tr: tr,
|
||||
UserConfig: userConfig,
|
||||
AppState: appState,
|
||||
Debug: config.GetDebug(),
|
||||
Fs: afero.NewOsFs(),
|
||||
}, nil
|
||||
cmn := &common.Common{
|
||||
Log: log,
|
||||
Tr: tr,
|
||||
AppState: appState,
|
||||
Debug: config.GetDebug(),
|
||||
Fs: afero.NewOsFs(),
|
||||
}
|
||||
cmn.SetUserConfig(userConfig)
|
||||
return cmn, nil
|
||||
}
|
||||
|
||||
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
|
||||
@@ -119,7 +116,14 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
|
||||
return app, err
|
||||
}
|
||||
|
||||
showRecentRepos, err := app.setupRepo()
|
||||
// If we're not in a repo, GetRepoPaths will return an error. The error is moot for us
|
||||
// at this stage, since we'll try to init a new repo in setupRepo(), below
|
||||
repoPaths, err := git_commands.GetRepoPaths(app.OSCommand.Cmd, gitVersion)
|
||||
if err != nil {
|
||||
common.Log.Infof("Error getting repo paths: %v", err)
|
||||
}
|
||||
|
||||
showRecentRepos, err := app.setupRepo(repoPaths)
|
||||
if err != nil {
|
||||
return app, err
|
||||
}
|
||||
@@ -168,14 +172,16 @@ func openRecentRepo(app *App) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (app *App) setupRepo() (bool, error) {
|
||||
func (app *App) setupRepo(
|
||||
repoPaths *git_commands.RepoPaths,
|
||||
) (bool, error) {
|
||||
if env.GetGitDirEnv() != "" {
|
||||
// we've been given the git dir directly. We'll verify this dir when initializing our Git object
|
||||
// we've been given the git dir directly. Skip setup
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// if we are not in a git repo, we ask if we want to `git init`
|
||||
if err := commands.VerifyInGitRepo(app.OSCommand); err != nil {
|
||||
if repoPaths == nil {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -187,7 +193,7 @@ func (app *App) setupRepo() (bool, error) {
|
||||
|
||||
var shouldInitRepo bool
|
||||
initialBranchArg := ""
|
||||
switch app.UserConfig.NotARepository {
|
||||
switch app.UserConfig().NotARepository {
|
||||
case "prompt":
|
||||
// Offer to initialize a new repository in current directory.
|
||||
fmt.Print(app.Tr.CreateRepo)
|
||||
@@ -221,6 +227,7 @@ func (app *App) setupRepo() (bool, error) {
|
||||
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -238,10 +245,7 @@ func (app *App) setupRepo() (bool, error) {
|
||||
}
|
||||
|
||||
// Run this afterward so that the previous repo creation steps can run without this interfering
|
||||
if isBare, err := git_commands.IsBareRepo(app.OSCommand); isBare {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if repoPaths.IsBareRepo() {
|
||||
|
||||
fmt.Print(app.Tr.BareRepo)
|
||||
|
||||
|
||||
@@ -246,16 +246,18 @@ func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
|
||||
|
||||
// Takes the hash of some commit, and the hash of a fixup commit that was created
|
||||
// at the end of the branch, then moves the fixup commit down to right after the
|
||||
// original commit, changing its type to "fixup"
|
||||
// original commit, changing its type to "fixup" (only if ChangeToFixup is true)
|
||||
type MoveFixupCommitDownInstruction struct {
|
||||
OriginalHash string
|
||||
FixupHash string
|
||||
OriginalHash string
|
||||
FixupHash string
|
||||
ChangeToFixup bool
|
||||
}
|
||||
|
||||
func NewMoveFixupCommitDownInstruction(originalHash string, fixupHash string) Instruction {
|
||||
func NewMoveFixupCommitDownInstruction(originalHash string, fixupHash string, changeToFixup bool) Instruction {
|
||||
return &MoveFixupCommitDownInstruction{
|
||||
OriginalHash: originalHash,
|
||||
FixupHash: fixupHash,
|
||||
OriginalHash: originalHash,
|
||||
FixupHash: fixupHash,
|
||||
ChangeToFixup: changeToFixup,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +271,7 @@ func (self *MoveFixupCommitDownInstruction) SerializedInstructions() string {
|
||||
|
||||
func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
|
||||
return handleInteractiveRebase(common, func(path string) error {
|
||||
return utils.MoveFixupCommitDown(path, self.OriginalHash, self.FixupHash, getCommentChar())
|
||||
return utils.MoveFixupCommitDown(path, self.OriginalHash, self.FixupHash, self.ChangeToFixup, getCommentChar())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,12 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
||||
|
||||
if integrationTest != nil {
|
||||
integrationTest.SetupConfig(appConfig)
|
||||
|
||||
// Preserve the changes that the test setup just made to the config, so
|
||||
// they don't get lost when we reload the config while running the test
|
||||
// (which happens when switching between repos, going in and out of
|
||||
// submodules, etc).
|
||||
appConfig.SaveGlobalUserConfig()
|
||||
}
|
||||
|
||||
common, err := NewCommon(appConfig)
|
||||
|
||||
@@ -51,7 +51,10 @@ func GetKeybindingsDir() string {
|
||||
}
|
||||
|
||||
func generateAtDir(cheatsheetDir string) {
|
||||
translationSetsByLang := i18n.GetTranslationSets()
|
||||
translationSetsByLang, err := i18n.GetTranslationSets()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
mConfig := config.NewDummyAppConfig()
|
||||
|
||||
for lang := range translationSetsByLang {
|
||||
@@ -60,6 +63,11 @@ func generateAtDir(cheatsheetDir string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tr, err := i18n.NewTranslationSetFromConfig(common.Log, lang)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
common.Tr = tr
|
||||
mApp, _ := app.NewApp(mConfig, nil, common)
|
||||
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
||||
file, err := os.Create(path)
|
||||
|
||||
@@ -262,7 +262,7 @@ func TestGetBindingSections(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.testName, func(t *testing.T) {
|
||||
actual := getBindingSections(test.bindings, &tr)
|
||||
actual := getBindingSections(test.bindings, tr)
|
||||
assert.EqualValues(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
|
||||
return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar)
|
||||
}
|
||||
|
||||
func (self *gitCmdObjBuilder) NewInteractiveShell(cmdStr string) oscommands.ICmdObj {
|
||||
return self.innerBuilder.NewInteractiveShell(cmdStr).AddEnvVars(defaultEnvVar)
|
||||
}
|
||||
|
||||
func (self *gitCmdObjBuilder) Quote(str string) string {
|
||||
return self.innerBuilder.Quote(str)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/mgutz/str"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type BranchCommands struct {
|
||||
*GitCommon
|
||||
allBranchesLogCmdIndex uint8 // keeps track of current all branches log command
|
||||
}
|
||||
|
||||
func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
|
||||
@@ -28,6 +30,15 @@ func (self *BranchCommands) New(name string, base string) error {
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *BranchCommands) NewWithoutTracking(name string, base string) error {
|
||||
cmdArgs := NewGitCmd("checkout").
|
||||
Arg("-b", name, base).
|
||||
Arg("--no-track").
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
// CreateWithUpstream creates a new branch with a given upstream, but without
|
||||
// checking it out
|
||||
func (self *BranchCommands) CreateWithUpstream(name string, upstream string) error {
|
||||
@@ -134,7 +145,7 @@ func (self *BranchCommands) GetGraph(branchName string) (string, error) {
|
||||
}
|
||||
|
||||
func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj {
|
||||
branchLogCmdTemplate := self.UserConfig.Git.BranchLogCmd
|
||||
branchLogCmdTemplate := self.UserConfig().Git.BranchLogCmd
|
||||
templateValues := map[string]string{
|
||||
"branchName": self.cmd.Quote(branchName),
|
||||
}
|
||||
@@ -216,13 +227,18 @@ func (self *BranchCommands) Rename(oldName string, newName string) error {
|
||||
|
||||
type MergeOpts struct {
|
||||
FastForwardOnly bool
|
||||
Squash bool
|
||||
}
|
||||
|
||||
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
||||
if opts.Squash && opts.FastForwardOnly {
|
||||
panic("Squash and FastForwardOnly can't both be true")
|
||||
}
|
||||
cmdArgs := NewGitCmd("merge").
|
||||
Arg("--no-edit").
|
||||
Arg(strings.Fields(self.UserConfig.Git.Merging.Args)...).
|
||||
Arg(strings.Fields(self.UserConfig().Git.Merging.Args)...).
|
||||
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||
ArgIf(opts.Squash, "--squash", "--ff").
|
||||
Arg(branchName).
|
||||
ToArgv()
|
||||
|
||||
@@ -230,5 +246,17 @@ func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
||||
}
|
||||
|
||||
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New(str.ToArgv(self.UserConfig.Git.AllBranchesLogCmd)).DontLog()
|
||||
// Only choose between non-empty, non-identical commands
|
||||
candidates := lo.Uniq(lo.WithoutEmpty(append([]string{
|
||||
self.UserConfig().Git.AllBranchesLogCmd,
|
||||
},
|
||||
self.UserConfig().Git.AllBranchesLogCmds...,
|
||||
)))
|
||||
|
||||
n := len(candidates)
|
||||
|
||||
i := self.allBranchesLogCmdIndex
|
||||
self.allBranchesLogCmdIndex = uint8((int(i) + 1) % n)
|
||||
|
||||
return self.cmd.New(str.ToArgv(candidates[i])).DontLog()
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit,
|
||||
}
|
||||
}
|
||||
|
||||
if loadBehindCounts && self.UserConfig.Gui.ShowDivergenceFromBaseBranch != "none" {
|
||||
if loadBehindCounts && self.UserConfig().Gui.ShowDivergenceFromBaseBranch != "none" {
|
||||
onWorker(func() error {
|
||||
return self.GetBehindBaseBranchValuesForAllBranches(branches, mainBranches, renderFunc)
|
||||
})
|
||||
|
||||
@@ -88,7 +88,7 @@ func (self *CommitCommands) ResetToCommit(hash string, strength string, envVars
|
||||
func (self *CommitCommands) CommitCmdObj(summary string, description string) oscommands.ICmdObj {
|
||||
messageArgs := self.commitMessageArgs(summary, description)
|
||||
|
||||
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
|
||||
skipHookPrefix := self.UserConfig().Git.SkipHookPrefix
|
||||
|
||||
cmdArgs := NewGitCmd("commit").
|
||||
ArgIf(skipHookPrefix != "" && strings.HasPrefix(summary, skipHookPrefix), "--no-verify").
|
||||
@@ -117,7 +117,7 @@ func (self *CommitCommands) CommitInEditorWithMessageFileCmdObj(tmpMessageFile s
|
||||
}
|
||||
|
||||
// RewordLastCommit rewords the topmost commit with the given message
|
||||
func (self *CommitCommands) RewordLastCommit(summary string, description string) error {
|
||||
func (self *CommitCommands) RewordLastCommit(summary string, description string) oscommands.ICmdObj {
|
||||
messageArgs := self.commitMessageArgs(summary, description)
|
||||
|
||||
cmdArgs := NewGitCmd("commit").
|
||||
@@ -125,7 +125,7 @@ func (self *CommitCommands) RewordLastCommit(summary string, description string)
|
||||
Arg(messageArgs...).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
return self.cmd.New(cmdArgs)
|
||||
}
|
||||
|
||||
func (self *CommitCommands) commitMessageArgs(summary string, description string) []string {
|
||||
@@ -148,7 +148,7 @@ func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
|
||||
}
|
||||
|
||||
func (self *CommitCommands) signoffFlag() string {
|
||||
if self.UserConfig.Git.Commit.SignOff {
|
||||
if self.UserConfig().Git.Commit.SignOff {
|
||||
return "--signoff"
|
||||
} else {
|
||||
return ""
|
||||
@@ -258,19 +258,20 @@ func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
|
||||
func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommands.ICmdObj {
|
||||
contextSize := self.AppState.DiffContextSize
|
||||
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Config("diff.noprefix=false").
|
||||
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
|
||||
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
|
||||
Arg("--color="+self.UserConfig().Git.Paging.ColorArg).
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg("--stat").
|
||||
Arg("--decorate").
|
||||
Arg("-p").
|
||||
Arg(hash).
|
||||
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
|
||||
ArgIf(filterPath != "", "--", filterPath).
|
||||
Dir(self.repoPaths.worktreePath).
|
||||
ToArgv()
|
||||
|
||||
@@ -322,9 +322,9 @@ func TestGetCommits(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
common.UserConfig.Git.MainBranches = scenario.mainBranches
|
||||
common.UserConfig().Git.MainBranches = scenario.mainBranches
|
||||
opts := scenario.opts
|
||||
opts.MainBranches = NewMainBranches(scenario.mainBranches, cmd)
|
||||
opts.MainBranches = NewMainBranches(common, cmd)
|
||||
commits, err := builder.GetCommits(opts)
|
||||
|
||||
assert.Equal(t, scenario.expectedCommits, commits)
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestCommitRewordCommit(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildCommitCommands(commonDeps{runner: s.runner})
|
||||
|
||||
assert.NoError(t, instance.RewordLastCommit(s.summary, s.description))
|
||||
assert.NoError(t, instance.RewordLastCommit(s.summary, s.description).Run())
|
||||
s.runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
@@ -228,54 +228,69 @@ func TestCommitCreateAmendCommit(t *testing.T) {
|
||||
|
||||
func TestCommitShowCmdObj(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
filterPath string
|
||||
contextSize int
|
||||
ignoreWhitespace bool
|
||||
extDiffCmd string
|
||||
expected []string
|
||||
testName string
|
||||
filterPath string
|
||||
contextSize int
|
||||
similarityThreshold int
|
||||
ignoreWhitespace bool
|
||||
extDiffCmd string
|
||||
expected []string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "Default case without filter path",
|
||||
filterPath: "",
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
testName: "Default case without filter path",
|
||||
filterPath: "",
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
|
||||
},
|
||||
{
|
||||
testName: "Default case with filter path",
|
||||
filterPath: "file.txt",
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
|
||||
testName: "Default case with filter path",
|
||||
filterPath: "file.txt",
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--", "file.txt"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
filterPath: "",
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
|
||||
testName: "Show diff with custom context size",
|
||||
filterPath: "",
|
||||
contextSize: 77,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff, ignoring whitespace",
|
||||
filterPath: "",
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: true,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
|
||||
testName: "Show diff with custom similarity threshold",
|
||||
filterPath: "",
|
||||
contextSize: 3,
|
||||
similarityThreshold: 33,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with external diff command",
|
||||
filterPath: "",
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "difft --color=always",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
testName: "Show diff, ignoring whitespace",
|
||||
filterPath: "",
|
||||
contextSize: 77,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: true,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with external diff command",
|
||||
filterPath: "",
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "difft --color=always",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -286,6 +301,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
appState := &config.AppState{}
|
||||
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
appState.DiffContextSize = s.contextSize
|
||||
appState.RenameSimilarityThreshold = s.similarityThreshold
|
||||
|
||||
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
|
||||
repoPaths := RepoPaths{
|
||||
|
||||
@@ -43,7 +43,7 @@ func (self *ConfigCommands) ConfiguredPager() string {
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetPager(width int) string {
|
||||
useConfig := self.UserConfig.Git.Paging.UseConfig
|
||||
useConfig := self.UserConfig().Git.Paging.UseConfig
|
||||
if useConfig {
|
||||
pager := self.ConfiguredPager()
|
||||
return strings.Split(pager, "| less")[0]
|
||||
@@ -53,14 +53,14 @@ func (self *ConfigCommands) GetPager(width int) string {
|
||||
"columnWidth": strconv.Itoa(width/2 - 6),
|
||||
}
|
||||
|
||||
pagerTemplate := string(self.UserConfig.Git.Paging.Pager)
|
||||
pagerTemplate := string(self.UserConfig().Git.Paging.Pager)
|
||||
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
|
||||
}
|
||||
|
||||
// UsingGpg tells us whether the user has gpg enabled so that we can know
|
||||
// whether we need to run a subprocess to allow them to enter their password
|
||||
func (self *ConfigCommands) UsingGpg() bool {
|
||||
overrideGpg := self.UserConfig.Git.OverrideGpg
|
||||
overrideGpg := self.UserConfig().Git.OverrideGpg
|
||||
if overrideGpg {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -58,9 +58,9 @@ func buildGitCommon(deps commonDeps) *GitCommon {
|
||||
}
|
||||
gitCommon.cmd = cmd
|
||||
|
||||
gitCommon.Common.UserConfig = deps.userConfig
|
||||
if gitCommon.Common.UserConfig == nil {
|
||||
gitCommon.Common.UserConfig = config.GetDefaultConfig()
|
||||
gitCommon.Common.SetUserConfig(deps.userConfig)
|
||||
if gitCommon.Common.UserConfig() == nil {
|
||||
gitCommon.Common.SetUserConfig(config.GetDefaultConfig())
|
||||
}
|
||||
|
||||
gitCommon.version = deps.gitVersion
|
||||
|
||||
@@ -17,7 +17,7 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
|
||||
}
|
||||
|
||||
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
useExtDiff := extDiffCmd != ""
|
||||
|
||||
return self.cmd.New(
|
||||
@@ -26,7 +26,7 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
|
||||
Arg(diffArgs...).
|
||||
Dir(self.repoPaths.worktreePath).
|
||||
ToArgv(),
|
||||
|
||||
@@ -31,7 +31,7 @@ func (self *FileCommands) Cat(fileName string) (string, error) {
|
||||
}
|
||||
|
||||
func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (string, error) {
|
||||
editor := self.UserConfig.OS.EditCommand
|
||||
editor := self.UserConfig().OS.EditCommand
|
||||
|
||||
if editor == "" {
|
||||
editor = self.config.GetCoreEditor()
|
||||
@@ -60,7 +60,7 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
|
||||
"line": strconv.Itoa(lineNumber),
|
||||
}
|
||||
|
||||
editCmdTemplate := self.UserConfig.OS.EditCommandTemplate
|
||||
editCmdTemplate := self.UserConfig().OS.EditCommandTemplate
|
||||
if len(editCmdTemplate) == 0 {
|
||||
switch editor {
|
||||
case "emacs", "nano", "vi", "vim", "nvim":
|
||||
@@ -78,7 +78,7 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
|
||||
|
||||
func (self *FileCommands) GetEditCmdStr(filenames []string) (string, bool) {
|
||||
// Legacy support for old config; to be removed at some point
|
||||
if self.UserConfig.OS.Edit == "" && self.UserConfig.OS.EditCommandTemplate != "" {
|
||||
if self.UserConfig().OS.Edit == "" && self.UserConfig().OS.EditCommandTemplate != "" {
|
||||
// If multiple files are selected, we'll simply edit just the first one.
|
||||
// It's not worth fixing this for the legacy support.
|
||||
if cmdStr, err := self.GetEditCmdStrLegacy(filenames[0], 1); err == nil {
|
||||
@@ -86,7 +86,7 @@ func (self *FileCommands) GetEditCmdStr(filenames []string) (string, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
template, suspend := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
template, suspend := config.GetEditTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
|
||||
quotedFilenames := lo.Map(filenames, func(filename string, _ int) string { return self.cmd.Quote(filename) })
|
||||
|
||||
templateValues := map[string]string{
|
||||
@@ -99,13 +99,13 @@ func (self *FileCommands) GetEditCmdStr(filenames []string) (string, bool) {
|
||||
|
||||
func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (string, bool) {
|
||||
// Legacy support for old config; to be removed at some point
|
||||
if self.UserConfig.OS.EditAtLine == "" && self.UserConfig.OS.EditCommandTemplate != "" {
|
||||
if self.UserConfig().OS.EditAtLine == "" && self.UserConfig().OS.EditCommandTemplate != "" {
|
||||
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
|
||||
return cmdStr, true
|
||||
}
|
||||
}
|
||||
|
||||
template, suspend := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
template, suspend := config.GetEditAtLineTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
|
||||
|
||||
templateValues := map[string]string{
|
||||
"filename": self.cmd.Quote(filename),
|
||||
@@ -118,13 +118,13 @@ func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (
|
||||
|
||||
func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber int) string {
|
||||
// Legacy support for old config; to be removed at some point
|
||||
if self.UserConfig.OS.EditAtLineAndWait == "" && self.UserConfig.OS.EditCommandTemplate != "" {
|
||||
if self.UserConfig().OS.EditAtLineAndWait == "" && self.UserConfig().OS.EditCommandTemplate != "" {
|
||||
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
|
||||
return cmdStr
|
||||
}
|
||||
}
|
||||
|
||||
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
|
||||
|
||||
templateValues := map[string]string{
|
||||
"filename": self.cmd.Quote(filename),
|
||||
@@ -136,7 +136,7 @@ func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber
|
||||
}
|
||||
|
||||
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) (string, bool) {
|
||||
template, suspend := config.GetOpenDirInEditorTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
template, suspend := config.GetOpenDirInEditorTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
|
||||
|
||||
templateValues := map[string]string{
|
||||
"dir": self.cmd.Quote(path),
|
||||
|
||||
@@ -100,15 +100,19 @@ type FileStatus struct {
|
||||
PreviousName string
|
||||
}
|
||||
|
||||
func (c *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||
func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||
cmdArgs := NewGitCmd("status").
|
||||
Arg(opts.UntrackedFilesArg).
|
||||
Arg("--porcelain").
|
||||
Arg("-z").
|
||||
ArgIf(opts.NoRenames, "--no-renames").
|
||||
ArgIfElse(
|
||||
opts.NoRenames,
|
||||
"--no-renames",
|
||||
fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold),
|
||||
).
|
||||
ToArgv()
|
||||
|
||||
statusLines, _, err := c.cmd.New(cmdArgs).DontLog().RunWithOutputs()
|
||||
statusLines, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
|
||||
if err != nil {
|
||||
return []FileStatus{}, err
|
||||
}
|
||||
|
||||
@@ -5,27 +5,31 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFileGetStatusFiles(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
runner oscommands.ICmdObjRunner
|
||||
expectedFiles []*models.File
|
||||
testName string
|
||||
similarityThreshold int
|
||||
runner oscommands.ICmdObjRunner
|
||||
expectedFiles []*models.File
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"No files found",
|
||||
50,
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "", nil),
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
|
||||
[]*models.File{},
|
||||
},
|
||||
{
|
||||
"Several files found",
|
||||
50,
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
|
||||
nil,
|
||||
),
|
||||
@@ -94,8 +98,9 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"File with new line char",
|
||||
50,
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "MM a\nb.txt", nil),
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
|
||||
[]*models.File{
|
||||
{
|
||||
Name: "a\nb.txt",
|
||||
@@ -113,8 +118,9 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"Renamed files",
|
||||
50,
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
|
||||
nil,
|
||||
),
|
||||
@@ -149,8 +155,9 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"File with arrow in name",
|
||||
50,
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||
`?? a -> b.txt`,
|
||||
nil,
|
||||
),
|
||||
@@ -175,8 +182,11 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)
|
||||
|
||||
appState := &config.AppState{}
|
||||
appState.RenameSimilarityThreshold = s.similarityThreshold
|
||||
|
||||
loader := &FileLoader{
|
||||
GitCommon: buildGitCommon(commonDeps{}),
|
||||
GitCommon: buildGitCommon(commonDeps{appState: appState}),
|
||||
cmd: cmd,
|
||||
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
|
||||
getFileType: func(string) string { return "file" },
|
||||
|
||||
@@ -5,32 +5,34 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
type MainBranches struct {
|
||||
// List of main branches configured by the user. Just the bare names.
|
||||
configuredMainBranches []string
|
||||
// Which of these actually exist in the repository. Full ref names, and it
|
||||
// could be either "refs/heads/..." or "refs/remotes/origin/..." depending
|
||||
// on which one exists for a given bare name.
|
||||
c *common.Common
|
||||
// Which of the configured main branches actually exist in the repository. Full
|
||||
// ref names, and it could be either "refs/heads/..." or "refs/remotes/origin/..."
|
||||
// depending on which one exists for a given bare name.
|
||||
existingMainBranches []string
|
||||
|
||||
previousMainBranches []string
|
||||
|
||||
cmd oscommands.ICmdObjBuilder
|
||||
mutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
func NewMainBranches(
|
||||
configuredMainBranches []string,
|
||||
cmn *common.Common,
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
) *MainBranches {
|
||||
return &MainBranches{
|
||||
configuredMainBranches: configuredMainBranches,
|
||||
existingMainBranches: nil,
|
||||
cmd: cmd,
|
||||
mutex: &deadlock.Mutex{},
|
||||
c: cmn,
|
||||
existingMainBranches: nil,
|
||||
cmd: cmd,
|
||||
mutex: &deadlock.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +42,11 @@ func (self *MainBranches) Get() []string {
|
||||
self.mutex.Lock()
|
||||
defer self.mutex.Unlock()
|
||||
|
||||
if self.existingMainBranches == nil {
|
||||
self.existingMainBranches = self.determineMainBranches()
|
||||
configuredMainBranches := self.c.UserConfig().Git.MainBranches
|
||||
|
||||
if self.existingMainBranches == nil || !utils.EqualSlices(self.previousMainBranches, configuredMainBranches) {
|
||||
self.existingMainBranches = self.determineMainBranches(configuredMainBranches)
|
||||
self.previousMainBranches = configuredMainBranches
|
||||
}
|
||||
|
||||
return self.existingMainBranches
|
||||
@@ -71,13 +76,13 @@ func (self *MainBranches) GetMergeBase(refName string) string {
|
||||
return ignoringWarnings(output)
|
||||
}
|
||||
|
||||
func (self *MainBranches) determineMainBranches() []string {
|
||||
func (self *MainBranches) determineMainBranches(configuredMainBranches []string) []string {
|
||||
var existingBranches []string
|
||||
var wg sync.WaitGroup
|
||||
|
||||
existingBranches = make([]string, len(self.configuredMainBranches))
|
||||
existingBranches = make([]string, len(configuredMainBranches))
|
||||
|
||||
for i, branchName := range self.configuredMainBranches {
|
||||
for i, branchName := range configuredMainBranches {
|
||||
wg.Add(1)
|
||||
go utils.Safe(func() {
|
||||
defer wg.Done()
|
||||
|
||||
@@ -47,8 +47,8 @@ type ApplyPatchOpts struct {
|
||||
Reverse bool
|
||||
}
|
||||
|
||||
func (self *PatchCommands) ApplyCustomPatch(reverse bool) error {
|
||||
patch := self.PatchBuilder.PatchToApply(reverse)
|
||||
func (self *PatchCommands) ApplyCustomPatch(reverse bool, turnAddedFilesIntoDiffAgainstEmptyFile bool) error {
|
||||
patch := self.PatchBuilder.PatchToApply(reverse, turnAddedFilesIntoDiffAgainstEmptyFile)
|
||||
|
||||
return self.ApplyPatch(patch, ApplyPatchOpts{
|
||||
Index: true,
|
||||
@@ -94,7 +94,7 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com
|
||||
}
|
||||
|
||||
// apply each patch in reverse
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true, true); err != nil {
|
||||
_ = self.rebase.AbortRebase()
|
||||
return err
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
||||
}
|
||||
|
||||
// apply each patch forward
|
||||
if err := self.ApplyCustomPatch(false); err != nil {
|
||||
if err := self.ApplyCustomPatch(false, false); err != nil {
|
||||
// Don't abort the rebase here; this might cause conflicts, so give
|
||||
// the user a chance to resolve them
|
||||
return err
|
||||
@@ -172,7 +172,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
||||
}
|
||||
|
||||
// apply each patch in reverse
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true, true); err != nil {
|
||||
_ = self.rebase.AbortRebase()
|
||||
return err
|
||||
}
|
||||
@@ -228,7 +228,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true, true); err != nil {
|
||||
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||
_ = self.rebase.AbortRebase()
|
||||
}
|
||||
@@ -282,7 +282,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true, true); err != nil {
|
||||
_ = self.rebase.AbortRebase()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -35,9 +35,8 @@ func NewRebaseCommands(
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, summary string, description string) error {
|
||||
if models.IsHeadCommit(commits, index) {
|
||||
// we've selected the top commit so no rebase is required
|
||||
return self.commit.RewordLastCommit(summary, description)
|
||||
if self.config.UsingGpg() {
|
||||
return errors.New(self.Tr.DisabledForGPG)
|
||||
}
|
||||
|
||||
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
|
||||
@@ -46,7 +45,7 @@ func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, su
|
||||
}
|
||||
|
||||
// now the selected commit should be our head so we'll amend it with the new message
|
||||
err = self.commit.RewordLastCommit(summary, description)
|
||||
err = self.commit.RewordLastCommit(summary, description).Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -285,6 +284,11 @@ func (self *RebaseCommands) GitRebaseEditTodo(todosFileContent []byte) error {
|
||||
return cmdObj.Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) getHashOfLastCommitMade() (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
|
||||
return self.cmd.New(cmdArgs).RunWithOutput()
|
||||
}
|
||||
|
||||
// AmendTo amends the given commit with whatever files are staged
|
||||
func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) error {
|
||||
commit := commits[commitIndex]
|
||||
@@ -293,9 +297,7 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the hash of the commit we just created
|
||||
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
|
||||
fixupHash, err := self.cmd.New(cmdArgs).RunWithOutput()
|
||||
fixupHash, err := self.getHashOfLastCommitMade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -303,7 +305,20 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
|
||||
overrideEditor: true,
|
||||
instruction: daemon.NewMoveFixupCommitDownInstruction(commit.Hash, fixupHash),
|
||||
instruction: daemon.NewMoveFixupCommitDownInstruction(commit.Hash, fixupHash, true),
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveFixupCommitDown(commits []*models.Commit, targetCommitIndex int) error {
|
||||
fixupHash, err := self.getHashOfLastCommitMade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseHashOrRoot: getBaseHashOrRoot(commits, targetCommitIndex+1),
|
||||
overrideEditor: true,
|
||||
instruction: daemon.NewMoveFixupCommitDownInstruction(commits[targetCommitIndex].Hash, fixupHash, false),
|
||||
}).Run()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package git_commands
|
||||
|
||||
import (
|
||||
ioFs "io/fs"
|
||||
"path"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -18,6 +18,7 @@ type RepoPaths struct {
|
||||
repoPath string
|
||||
repoGitDirPath string
|
||||
repoName string
|
||||
isBareRepo bool
|
||||
}
|
||||
|
||||
var gitPathFormatVersion GitVersion = GitVersion{2, 31, 0, ""}
|
||||
@@ -54,14 +55,19 @@ func (self *RepoPaths) RepoName() string {
|
||||
return self.repoName
|
||||
}
|
||||
|
||||
func (self *RepoPaths) IsBareRepo() bool {
|
||||
return self.isBareRepo
|
||||
}
|
||||
|
||||
// Returns the repo paths for a typical repo
|
||||
func MockRepoPaths(currentPath string) *RepoPaths {
|
||||
return &RepoPaths{
|
||||
worktreePath: currentPath,
|
||||
worktreeGitDirPath: path.Join(currentPath, ".git"),
|
||||
worktreeGitDirPath: filepath.Join(currentPath, ".git"),
|
||||
repoPath: currentPath,
|
||||
repoGitDirPath: path.Join(currentPath, ".git"),
|
||||
repoGitDirPath: filepath.Join(currentPath, ".git"),
|
||||
repoName: "lazygit",
|
||||
isBareRepo: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +75,19 @@ func GetRepoPaths(
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
version *GitVersion,
|
||||
) (*RepoPaths, error) {
|
||||
gitDirOutput, err := callGitRevParse(cmd, version, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree")
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetRepoPathsForDir(cwd, cmd, version)
|
||||
}
|
||||
|
||||
func GetRepoPathsForDir(
|
||||
dir string,
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
version *GitVersion,
|
||||
) (*RepoPaths, error) {
|
||||
gitDirOutput, err := callGitRevParseWithDir(cmd, version, dir, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -84,21 +102,22 @@ func GetRepoPaths(
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
isBareRepo := gitDirResults[3] == "true"
|
||||
|
||||
// If we're in a submodule, --show-superproject-working-tree will return
|
||||
// a value, meaning gitDirResults will be length 4. In that case
|
||||
// a value, meaning gitDirResults will be length 5. In that case
|
||||
// return the worktree path as the repoPath. Otherwise we're in a
|
||||
// normal repo or a worktree so return the parent of the git common
|
||||
// dir (repoGitDirPath)
|
||||
isSubmodule := len(gitDirResults) == 4
|
||||
isSubmodule := len(gitDirResults) == 5
|
||||
|
||||
var repoPath string
|
||||
if isSubmodule {
|
||||
repoPath = worktreePath
|
||||
} else {
|
||||
repoPath = path.Dir(repoGitDirPath)
|
||||
repoPath = filepath.Dir(repoGitDirPath)
|
||||
}
|
||||
repoName := path.Base(repoPath)
|
||||
repoName := filepath.Base(repoPath)
|
||||
|
||||
return &RepoPaths{
|
||||
worktreePath: worktreePath,
|
||||
@@ -106,17 +125,10 @@ func GetRepoPaths(
|
||||
repoPath: repoPath,
|
||||
repoGitDirPath: repoGitDirPath,
|
||||
repoName: repoName,
|
||||
isBareRepo: isBareRepo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func callGitRevParse(
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
version *GitVersion,
|
||||
gitRevArgs ...string,
|
||||
) (string, error) {
|
||||
return callGitRevParseWithDir(cmd, version, "", gitRevArgs...)
|
||||
}
|
||||
|
||||
func callGitRevParseWithDir(
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
version *GitVersion,
|
||||
@@ -141,7 +153,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
|
||||
result := []string{}
|
||||
// For each directory in this path we're going to cat the `gitdir` file and append its contents to our result
|
||||
// That file points us to the `.git` file in the worktree.
|
||||
worktreeGitDirsPath := path.Join(repoGitDirPath, "worktrees")
|
||||
worktreeGitDirsPath := filepath.Join(repoGitDirPath, "worktrees")
|
||||
|
||||
// ensure the directory exists
|
||||
_, err := fs.Stat(worktreeGitDirsPath)
|
||||
@@ -158,7 +170,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
gitDirPath := path.Join(currPath, "gitdir")
|
||||
gitDirPath := filepath.Join(currPath, "gitdir")
|
||||
gitDirBytes, err := afero.ReadFile(fs, gitDirPath)
|
||||
if err != nil {
|
||||
// ignoring error
|
||||
@@ -166,7 +178,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
|
||||
}
|
||||
trimmedGitDir := strings.TrimSpace(string(gitDirBytes))
|
||||
// removing the .git part
|
||||
worktreeDir := path.Dir(trimmedGitDir)
|
||||
worktreeDir := filepath.Dir(trimmedGitDir)
|
||||
result = append(result, worktreeDir)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -2,11 +2,13 @@ package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -29,63 +31,152 @@ func TestGetRepoPaths(t *testing.T) {
|
||||
Name: "typical case",
|
||||
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
||||
// setup for main worktree
|
||||
expectedOutput := []string{
|
||||
mockOutput := lo.Ternary(runtime.GOOS == "windows", []string{
|
||||
// --show-toplevel
|
||||
`C:\path\to\repo`,
|
||||
// --git-dir
|
||||
`C:\path\to\repo\.git`,
|
||||
// --git-common-dir
|
||||
`C:\path\to\repo\.git`,
|
||||
// --is-bare-repository
|
||||
"false",
|
||||
// --show-superproject-working-tree
|
||||
}, []string{
|
||||
// --show-toplevel
|
||||
"/path/to/repo",
|
||||
// --git-dir
|
||||
"/path/to/repo/.git",
|
||||
// --git-common-dir
|
||||
"/path/to/repo/.git",
|
||||
// --is-bare-repository
|
||||
"false",
|
||||
// --show-superproject-working-tree
|
||||
}
|
||||
})
|
||||
runner.ExpectGitArgs(
|
||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
|
||||
strings.Join(expectedOutput, "\n"),
|
||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
||||
strings.Join(mockOutput, "\n"),
|
||||
nil)
|
||||
},
|
||||
Path: "/path/to/repo",
|
||||
Expected: &RepoPaths{
|
||||
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
|
||||
worktreePath: `C:\path\to\repo`,
|
||||
worktreeGitDirPath: `C:\path\to\repo\.git`,
|
||||
repoPath: `C:\path\to\repo`,
|
||||
repoGitDirPath: `C:\path\to\repo\.git`,
|
||||
repoName: `repo`,
|
||||
isBareRepo: false,
|
||||
}, &RepoPaths{
|
||||
worktreePath: "/path/to/repo",
|
||||
worktreeGitDirPath: "/path/to/repo/.git",
|
||||
repoPath: "/path/to/repo",
|
||||
repoGitDirPath: "/path/to/repo/.git",
|
||||
repoName: "repo",
|
||||
isBareRepo: false,
|
||||
}),
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "bare repo",
|
||||
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
||||
// setup for main worktree
|
||||
mockOutput := lo.Ternary(runtime.GOOS == "windows", []string{
|
||||
// --show-toplevel
|
||||
`C:\path\to\repo`,
|
||||
// --git-dir
|
||||
`C:\path\to\bare_repo\bare.git`,
|
||||
// --git-common-dir
|
||||
`C:\path\to\bare_repo\bare.git`,
|
||||
// --is-bare-repository
|
||||
`true`,
|
||||
// --show-superproject-working-tree
|
||||
}, []string{
|
||||
// --show-toplevel
|
||||
"/path/to/repo",
|
||||
// --git-dir
|
||||
"/path/to/bare_repo/bare.git",
|
||||
// --git-common-dir
|
||||
"/path/to/bare_repo/bare.git",
|
||||
// --is-bare-repository
|
||||
"true",
|
||||
// --show-superproject-working-tree
|
||||
})
|
||||
runner.ExpectGitArgs(
|
||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
||||
strings.Join(mockOutput, "\n"),
|
||||
nil)
|
||||
},
|
||||
Path: "/path/to/repo",
|
||||
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
|
||||
worktreePath: `C:\path\to\repo`,
|
||||
worktreeGitDirPath: `C:\path\to\bare_repo\bare.git`,
|
||||
repoPath: `C:\path\to\bare_repo`,
|
||||
repoGitDirPath: `C:\path\to\bare_repo\bare.git`,
|
||||
repoName: `bare_repo`,
|
||||
isBareRepo: true,
|
||||
}, &RepoPaths{
|
||||
worktreePath: "/path/to/repo",
|
||||
worktreeGitDirPath: "/path/to/bare_repo/bare.git",
|
||||
repoPath: "/path/to/bare_repo",
|
||||
repoGitDirPath: "/path/to/bare_repo/bare.git",
|
||||
repoName: "bare_repo",
|
||||
isBareRepo: true,
|
||||
}),
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "submodule",
|
||||
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
||||
expectedOutput := []string{
|
||||
mockOutput := lo.Ternary(runtime.GOOS == "windows", []string{
|
||||
// --show-toplevel
|
||||
`C:\path\to\repo\submodule1`,
|
||||
// --git-dir
|
||||
`C:\path\to\repo\.git\modules\submodule1`,
|
||||
// --git-common-dir
|
||||
`C:\path\to\repo\.git\modules\submodule1`,
|
||||
// --is-bare-repository
|
||||
`false`,
|
||||
// --show-superproject-working-tree
|
||||
`C:\path\to\repo`,
|
||||
}, []string{
|
||||
// --show-toplevel
|
||||
"/path/to/repo/submodule1",
|
||||
// --git-dir
|
||||
"/path/to/repo/.git/modules/submodule1",
|
||||
// --git-common-dir
|
||||
"/path/to/repo/.git/modules/submodule1",
|
||||
// --is-bare-repository
|
||||
"false",
|
||||
// --show-superproject-working-tree
|
||||
"/path/to/repo",
|
||||
}
|
||||
})
|
||||
runner.ExpectGitArgs(
|
||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
|
||||
strings.Join(expectedOutput, "\n"),
|
||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
||||
strings.Join(mockOutput, "\n"),
|
||||
nil)
|
||||
},
|
||||
Path: "/path/to/repo/submodule1",
|
||||
Expected: &RepoPaths{
|
||||
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
|
||||
worktreePath: `C:\path\to\repo\submodule1`,
|
||||
worktreeGitDirPath: `C:\path\to\repo\.git\modules\submodule1`,
|
||||
repoPath: `C:\path\to\repo\submodule1`,
|
||||
repoGitDirPath: `C:\path\to\repo\.git\modules\submodule1`,
|
||||
repoName: `submodule1`,
|
||||
isBareRepo: false,
|
||||
}, &RepoPaths{
|
||||
worktreePath: "/path/to/repo/submodule1",
|
||||
worktreeGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
||||
repoPath: "/path/to/repo/submodule1",
|
||||
repoGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
||||
repoName: "submodule1",
|
||||
},
|
||||
isBareRepo: false,
|
||||
}),
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "git rev-parse returns an error",
|
||||
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
||||
runner.ExpectGitArgs(
|
||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
|
||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
||||
"",
|
||||
errors.New("fatal: invalid gitfile format: /path/to/repo/worktree2/.git"))
|
||||
},
|
||||
@@ -94,7 +185,7 @@ func TestGetRepoPaths(t *testing.T) {
|
||||
Err: func(getRevParseArgs argFn) error {
|
||||
args := strings.Join(getRevParseArgs(), " ")
|
||||
return errors.New(
|
||||
fmt.Sprintf("'git %v --show-toplevel --absolute-git-dir --git-common-dir --show-superproject-working-tree' failed: fatal: invalid gitfile format: /path/to/repo/worktree2/.git", args),
|
||||
fmt.Sprintf("'git %v --show-toplevel --absolute-git-dir --git-common-dir --is-bare-repository --show-superproject-working-tree' failed: fatal: invalid gitfile format: /path/to/repo/worktree2/.git", args),
|
||||
)
|
||||
},
|
||||
},
|
||||
@@ -120,7 +211,7 @@ func TestGetRepoPaths(t *testing.T) {
|
||||
// prepare the filesystem for the scenario
|
||||
s.BeforeFunc(runner, getRevParseArgs)
|
||||
|
||||
repoPaths, err := GetRepoPaths(cmd, version)
|
||||
repoPaths, err := GetRepoPathsForDir("", cmd, version)
|
||||
|
||||
// check the error and the paths
|
||||
if s.Err != nil {
|
||||
|
||||
@@ -84,9 +84,10 @@ func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("stash").Arg("show").
|
||||
Arg("-p").
|
||||
Arg("--stat").
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
|
||||
Arg(fmt.Sprintf("--unified=%d", self.AppState.DiffContextSize)).
|
||||
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
|
||||
Arg(fmt.Sprintf("stash@{%d}", index)).
|
||||
Dir(self.repoPaths.worktreePath).
|
||||
ToArgv()
|
||||
|
||||
@@ -98,34 +98,46 @@ func TestStashHash(t *testing.T) {
|
||||
|
||||
func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
index int
|
||||
contextSize int
|
||||
ignoreWhitespace bool
|
||||
expected []string
|
||||
testName string
|
||||
index int
|
||||
contextSize int
|
||||
similarityThreshold int
|
||||
ignoreWhitespace bool
|
||||
expected []string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "Default case",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "stash@{5}"},
|
||||
testName: "Default case",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--find-renames=50%", "stash@{5}"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
index: 5,
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "stash@{5}"},
|
||||
testName: "Show diff with custom context size",
|
||||
index: 5,
|
||||
contextSize: 77,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "--find-renames=50%", "stash@{5}"},
|
||||
},
|
||||
{
|
||||
testName: "Default case",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: true,
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "stash@{5}"},
|
||||
testName: "Show diff with custom similarity threshold",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 33,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--find-renames=33%", "stash@{5}"},
|
||||
},
|
||||
{
|
||||
testName: "Default case",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: true,
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "--find-renames=50%", "stash@{5}"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -135,6 +147,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
appState := &config.AppState{}
|
||||
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
appState.DiffContextSize = s.contextSize
|
||||
appState.RenameSimilarityThreshold = s.similarityThreshold
|
||||
repoPaths := RepoPaths{
|
||||
worktreePath: "/path/to/worktree",
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@ package git_commands
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
)
|
||||
|
||||
@@ -49,20 +47,8 @@ func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
|
||||
return enums.REBASE_MODE_NONE
|
||||
}
|
||||
|
||||
func (self *StatusCommands) IsBareRepo() (bool, error) {
|
||||
return IsBareRepo(self.os)
|
||||
}
|
||||
|
||||
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
|
||||
res, err := osCommand.Cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToArgv(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// The command returns output with a newline, so we need to strip
|
||||
return strconv.ParseBool(strings.TrimSpace(res))
|
||||
func (self *StatusCommands) IsBareRepo() bool {
|
||||
return self.repoPaths.isBareRepo
|
||||
}
|
||||
|
||||
func (self *StatusCommands) IsInNormalRebase() (bool, error) {
|
||||
|
||||
@@ -60,7 +60,7 @@ func (self *SyncCommands) fetchCommandBuilder(fetchAll bool) *GitCommandBuilder
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchCmdObj(task gocui.Task) oscommands.ICmdObj {
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig.Git.FetchAll).ToArgv()
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig().Git.FetchAll).ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs)
|
||||
cmdObj.PromptOnCredentialRequest(task)
|
||||
@@ -72,7 +72,7 @@ func (self *SyncCommands) Fetch(task gocui.Task) error {
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchBackgroundCmdObj() oscommands.ICmdObj {
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig.Git.FetchAll).ToArgv()
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig().Git.FetchAll).ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs)
|
||||
cmdObj.DontLog().FailOnCredentialRequest()
|
||||
@@ -95,7 +95,7 @@ func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
||||
Arg("--no-edit").
|
||||
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||
ArgIf(opts.RemoteName != "", opts.RemoteName).
|
||||
ArgIf(opts.BranchName != "", opts.BranchName).
|
||||
ArgIf(opts.BranchName != "", "refs/heads/"+opts.BranchName).
|
||||
GitDirIf(opts.WorktreeGitDir != "", opts.WorktreeGitDir).
|
||||
ToArgv()
|
||||
|
||||
@@ -112,7 +112,7 @@ func (self *SyncCommands) FastForward(
|
||||
) error {
|
||||
cmdArgs := self.fetchCommandBuilder(false).
|
||||
Arg(remoteName).
|
||||
Arg(remoteBranchName + ":" + branchName).
|
||||
Arg("refs/heads/" + remoteBranchName + ":" + branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
|
||||
@@ -133,7 +133,7 @@ func TestSyncFetch(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildSyncCommands(commonDeps{})
|
||||
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
|
||||
instance.UserConfig().Git.FetchAll = s.fetchAllConfig
|
||||
task := gocui.NewFakeTask()
|
||||
s.test(instance.FetchCmdObj(task))
|
||||
})
|
||||
@@ -171,7 +171,7 @@ func TestSyncFetchBackground(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildSyncCommands(commonDeps{})
|
||||
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
|
||||
instance.UserConfig().Git.FetchAll = s.fetchAllConfig
|
||||
s.test(instance.FetchBackgroundCmdObj())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package git_commands
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
@@ -233,7 +233,7 @@ func (self *WorkingTreeCommands) Ignore(filename string) error {
|
||||
|
||||
// Exclude adds a file to the .git/info/exclude for the repo
|
||||
func (self *WorkingTreeCommands) Exclude(filename string) error {
|
||||
excludeFile := path.Join(self.repoPaths.repoGitDirPath, "info", "exclude")
|
||||
excludeFile := filepath.Join(self.repoPaths.repoGitDirPath, "info", "exclude")
|
||||
return self.os.AppendLineToFile(excludeFile, filename)
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool,
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool) oscommands.ICmdObj {
|
||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||
colorArg := self.UserConfig().Git.Paging.ColorArg
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
@@ -253,7 +253,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
|
||||
contextSize := self.AppState.DiffContextSize
|
||||
prevPath := node.GetPreviousPath()
|
||||
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
useExtDiff := extDiffCmd != "" && !plain
|
||||
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
@@ -263,6 +263,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg(fmt.Sprintf("--color=%s", colorArg)).
|
||||
ArgIf(!plain && self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
|
||||
ArgIf(cached, "--cached").
|
||||
ArgIf(noIndex, "--no-index").
|
||||
Arg("--").
|
||||
@@ -284,12 +285,12 @@ func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bo
|
||||
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj {
|
||||
contextSize := self.AppState.DiffContextSize
|
||||
|
||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||
colorArg := self.UserConfig().Git.Paging.ColorArg
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
useExtDiff := extDiffCmd != "" && !plain
|
||||
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
|
||||
@@ -205,13 +205,14 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
||||
|
||||
func TestWorkingTreeDiff(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
file *models.File
|
||||
plain bool
|
||||
cached bool
|
||||
ignoreWhitespace bool
|
||||
contextSize int
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
testName string
|
||||
file *models.File
|
||||
plain bool
|
||||
cached bool
|
||||
ignoreWhitespace bool
|
||||
contextSize int
|
||||
similarityThreshold int
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
}
|
||||
|
||||
const expectedResult = "pretend this is an actual git diff"
|
||||
@@ -224,12 +225,13 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
HasStagedChanges: false,
|
||||
Tracked: true,
|
||||
},
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "cached",
|
||||
@@ -238,12 +240,13 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
HasStagedChanges: false,
|
||||
Tracked: true,
|
||||
},
|
||||
plain: false,
|
||||
cached: true,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
plain: false,
|
||||
cached: true,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--cached", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "plain",
|
||||
@@ -252,12 +255,13 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
HasStagedChanges: false,
|
||||
Tracked: true,
|
||||
},
|
||||
plain: true,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
plain: true,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "File not tracked and file has no staged changes",
|
||||
@@ -266,12 +270,13 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
HasStagedChanges: false,
|
||||
Tracked: false,
|
||||
},
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Default case (ignore whitespace)",
|
||||
@@ -280,12 +285,13 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
HasStagedChanges: false,
|
||||
Tracked: true,
|
||||
},
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: true,
|
||||
contextSize: 3,
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: true,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
@@ -294,12 +300,28 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
HasStagedChanges: false,
|
||||
Tracked: true,
|
||||
},
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 17,
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 17,
|
||||
similarityThreshold: 50,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom similarity threshold",
|
||||
file: &models.File{
|
||||
Name: "test.txt",
|
||||
HasStagedChanges: false,
|
||||
Tracked: true,
|
||||
},
|
||||
plain: false,
|
||||
cached: false,
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
similarityThreshold: 33,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=33%", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -309,6 +331,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
appState := &config.AppState{}
|
||||
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
appState.DiffContextSize = s.contextSize
|
||||
appState.RenameSimilarityThreshold = s.similarityThreshold
|
||||
repoPaths := RepoPaths{
|
||||
worktreePath: "/path/to/worktree",
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (self *CachedGitConfig) Get(key string) string {
|
||||
defer self.mutex.Unlock()
|
||||
|
||||
if value, ok := self.cache[key]; ok {
|
||||
self.log.Debugf("using cache for key " + key)
|
||||
self.log.Debug("using cache for key " + key)
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func (self *CachedGitConfig) GetGeneral(args string) string {
|
||||
defer self.mutex.Unlock()
|
||||
|
||||
if value, ok := self.cache[args]; ok {
|
||||
self.log.Debugf("using cache for args " + args)
|
||||
self.log.Debug("using cache for args " + args)
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func (self *CachedGitConfig) getGeneralAux(args string) string {
|
||||
cmd := getGitConfigGeneralCmd(args)
|
||||
value, err := self.runGitConfigCmd(cmd)
|
||||
if err != nil {
|
||||
self.log.Debugf("Error getting git config value for args: " + args + ". Error: " + err.Error())
|
||||
self.log.Debugf("Error getting git config value for args: %s. Error: %v", args, err.Error())
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(value)
|
||||
@@ -79,7 +79,7 @@ func (self *CachedGitConfig) getAux(key string) string {
|
||||
cmd := getGitConfigCmd(key)
|
||||
value, err := self.runGitConfigCmd(cmd)
|
||||
if err != nil {
|
||||
self.log.Debugf("Error getting git config value for key: " + key + ". Error: " + err.Error())
|
||||
self.log.Debugf("Error getting git config value for key: %s. Error: %v", key, err.Error())
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(value)
|
||||
|
||||
@@ -416,7 +416,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
tr := i18n.EnglishTranslationSet()
|
||||
log := &fakes.FakeFieldLogger{}
|
||||
hostingServiceMgr := NewHostingServiceMgr(log, &tr, s.remoteUrl, s.configServiceDomains)
|
||||
hostingServiceMgr := NewHostingServiceMgr(log, tr, s.remoteUrl, s.configServiceDomains)
|
||||
s.test(hostingServiceMgr.GetPullRequestURL(s.from, s.to))
|
||||
log.AssertErrors(t, s.expectedLoggedErrors)
|
||||
})
|
||||
|
||||
@@ -53,6 +53,10 @@ func (b *Branch) RefName() string {
|
||||
return b.Name
|
||||
}
|
||||
|
||||
func (b *Branch) ShortRefName() string {
|
||||
return b.RefName()
|
||||
}
|
||||
|
||||
func (b *Branch) ParentRefName() string {
|
||||
return b.RefName() + "^"
|
||||
}
|
||||
|
||||
@@ -70,6 +70,10 @@ func (c *Commit) RefName() string {
|
||||
return c.Hash
|
||||
}
|
||||
|
||||
func (c *Commit) ShortRefName() string {
|
||||
return c.Hash[:7]
|
||||
}
|
||||
|
||||
func (c *Commit) ParentRefName() string {
|
||||
if c.IsFirstCommit() {
|
||||
return EmptyTreeCommitHash
|
||||
|
||||
@@ -18,6 +18,10 @@ func (r *RemoteBranch) RefName() string {
|
||||
return r.FullName()
|
||||
}
|
||||
|
||||
func (r *RemoteBranch) ShortRefName() string {
|
||||
return r.RefName()
|
||||
}
|
||||
|
||||
func (r *RemoteBranch) ParentRefName() string {
|
||||
return r.RefName() + "^"
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ func (s *StashEntry) RefName() string {
|
||||
return fmt.Sprintf("stash@{%d}", s.Index)
|
||||
}
|
||||
|
||||
func (s *StashEntry) ShortRefName() string {
|
||||
return s.RefName()
|
||||
}
|
||||
|
||||
func (s *StashEntry) ParentRefName() string {
|
||||
return s.RefName() + "^"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ func (t *Tag) RefName() string {
|
||||
return t.Name
|
||||
}
|
||||
|
||||
func (t *Tag) ShortRefName() string {
|
||||
return t.RefName()
|
||||
}
|
||||
|
||||
func (t *Tag) ParentRefName() string {
|
||||
return t.RefName() + "^"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ type ICmdObjBuilder interface {
|
||||
New(args []string) ICmdObj
|
||||
// NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
|
||||
NewShell(commandStr string) ICmdObj
|
||||
// Like NewShell, but uses the user's shell rather than "bash", and passes -i to it
|
||||
NewInteractiveShell(commandStr string) ICmdObj
|
||||
// Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects.
|
||||
Quote(str string) string
|
||||
}
|
||||
@@ -43,10 +45,23 @@ func (self *CmdObjBuilder) NewWithEnviron(args []string, env []string) ICmdObj {
|
||||
}
|
||||
|
||||
func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
|
||||
var quotedCommand string
|
||||
quotedCommand := self.quotedCommandString(commandStr)
|
||||
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
|
||||
|
||||
return self.New(cmdArgs)
|
||||
}
|
||||
|
||||
func (self *CmdObjBuilder) NewInteractiveShell(commandStr string) ICmdObj {
|
||||
quotedCommand := self.quotedCommandString(commandStr)
|
||||
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s %s", self.platform.InteractiveShell, self.platform.InteractiveShellArg, self.platform.ShellArg, quotedCommand))
|
||||
|
||||
return self.New(cmdArgs)
|
||||
}
|
||||
|
||||
func (self *CmdObjBuilder) quotedCommandString(commandStr string) string {
|
||||
// Windows does not seem to like quotes around the command
|
||||
if self.platform.OS == "windows" {
|
||||
quotedCommand = strings.NewReplacer(
|
||||
return strings.NewReplacer(
|
||||
"^", "^^",
|
||||
"&", "^&",
|
||||
"|", "^|",
|
||||
@@ -54,13 +69,9 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
|
||||
">", "^>",
|
||||
"%", "^%",
|
||||
).Replace(commandStr)
|
||||
} else {
|
||||
quotedCommand = self.Quote(commandStr)
|
||||
}
|
||||
|
||||
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
|
||||
|
||||
return self.New(cmdArgs)
|
||||
return self.Quote(commandStr)
|
||||
}
|
||||
|
||||
func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {
|
||||
|
||||
@@ -284,6 +284,7 @@ const (
|
||||
Username
|
||||
Passphrase
|
||||
PIN
|
||||
Token
|
||||
)
|
||||
|
||||
// Whenever we're asked for a password we just enter a newline, which will
|
||||
@@ -318,7 +319,7 @@ func (self *cmdObjRunner) getCredentialPromptFn(cmdObj ICmdObj) (func(Credential
|
||||
}
|
||||
|
||||
// runAndDetectCredentialRequest detect a username / password / passphrase question in a command
|
||||
// promptUserForCredential is a function that gets executed when this function detect you need to fillin a password or passphrase
|
||||
// promptUserForCredential is a function that gets executed when this function detect you need to fill in a password or passphrase
|
||||
// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
|
||||
func (self *cmdObjRunner) runAndDetectCredentialRequest(
|
||||
cmdObj ICmdObj,
|
||||
@@ -376,6 +377,7 @@ func (self *cmdObjRunner) getCheckForCredentialRequestFunc() func([]byte) (Crede
|
||||
`Username\s*for\s*'.+':`: Username,
|
||||
`Enter\s*passphrase\s*for\s*key\s*'.+':`: Passphrase,
|
||||
`Enter\s*PIN\s*for\s*.+\s*key\s*.+:`: PIN,
|
||||
`.*2FA Token.*`: Token,
|
||||
}
|
||||
|
||||
compiledPrompts := map[*regexp.Regexp]CredentialType{}
|
||||
|
||||
@@ -39,6 +39,8 @@ func TestProcessOutput(t *testing.T) {
|
||||
return "passphrase"
|
||||
case PIN:
|
||||
return "pin"
|
||||
case Token:
|
||||
return "token"
|
||||
default:
|
||||
panic("unexpected credential type")
|
||||
}
|
||||
@@ -92,6 +94,12 @@ func TestProcessOutput(t *testing.T) {
|
||||
output: "Enter PIN for key '123':",
|
||||
expectedToWrite: "pin",
|
||||
},
|
||||
{
|
||||
name: "2FA token prompt",
|
||||
promptUserForCredential: defaultPromptUserForCredential,
|
||||
output: "testuser 2FA Token (citadel)",
|
||||
expectedToWrite: "token",
|
||||
},
|
||||
{
|
||||
name: "username and password prompt",
|
||||
promptUserForCredential: defaultPromptUserForCredential,
|
||||
|
||||
@@ -51,11 +51,13 @@ func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder {
|
||||
}
|
||||
|
||||
var dummyPlatform = &Platform{
|
||||
OS: "darwin",
|
||||
Shell: "bash",
|
||||
ShellArg: "-c",
|
||||
OpenCommand: "open {{filename}}",
|
||||
OpenLinkCommand: "open {{link}}",
|
||||
OS: "darwin",
|
||||
Shell: "bash",
|
||||
InteractiveShell: "bash",
|
||||
ShellArg: "-c",
|
||||
InteractiveShellArg: "-i",
|
||||
OpenCommand: "open {{filename}}",
|
||||
OpenLinkCommand: "open {{link}}",
|
||||
}
|
||||
|
||||
func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand {
|
||||
|
||||
@@ -35,11 +35,13 @@ type OSCommand struct {
|
||||
|
||||
// Platform stores the os state
|
||||
type Platform struct {
|
||||
OS string
|
||||
Shell string
|
||||
ShellArg string
|
||||
OpenCommand string
|
||||
OpenLinkCommand string
|
||||
OS string
|
||||
Shell string
|
||||
InteractiveShell string
|
||||
ShellArg string
|
||||
InteractiveShellArg string
|
||||
OpenCommand string
|
||||
OpenLinkCommand string
|
||||
}
|
||||
|
||||
// NewOSCommand os command runner
|
||||
@@ -78,10 +80,10 @@ func FileType(path string) string {
|
||||
}
|
||||
|
||||
func (c *OSCommand) OpenFile(filename string) error {
|
||||
commandTemplate := c.UserConfig.OS.Open
|
||||
commandTemplate := c.UserConfig().OS.Open
|
||||
if commandTemplate == "" {
|
||||
// Legacy support
|
||||
commandTemplate = c.UserConfig.OS.OpenCommand
|
||||
commandTemplate = c.UserConfig().OS.OpenCommand
|
||||
}
|
||||
if commandTemplate == "" {
|
||||
commandTemplate = config.GetPlatformDefaultConfig().Open
|
||||
@@ -94,10 +96,10 @@ func (c *OSCommand) OpenFile(filename string) error {
|
||||
}
|
||||
|
||||
func (c *OSCommand) OpenLink(link string) error {
|
||||
commandTemplate := c.UserConfig.OS.OpenLink
|
||||
commandTemplate := c.UserConfig().OS.OpenLink
|
||||
if commandTemplate == "" {
|
||||
// Legacy support
|
||||
commandTemplate = c.UserConfig.OS.OpenLinkCommand
|
||||
commandTemplate = c.UserConfig().OS.OpenLinkCommand
|
||||
}
|
||||
if commandTemplate == "" {
|
||||
commandTemplate = config.GetPlatformDefaultConfig().OpenLink
|
||||
@@ -292,8 +294,8 @@ func (c *OSCommand) CopyToClipboard(str string) error {
|
||||
},
|
||||
)
|
||||
c.LogCommand(msg, false)
|
||||
if c.UserConfig.OS.CopyToClipboardCmd != "" {
|
||||
cmdStr := utils.ResolvePlaceholderString(c.UserConfig.OS.CopyToClipboardCmd, map[string]string{
|
||||
if c.UserConfig().OS.CopyToClipboardCmd != "" {
|
||||
cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{
|
||||
"text": c.Cmd.Quote(str),
|
||||
})
|
||||
return c.Cmd.NewShell(cmdStr).Run()
|
||||
@@ -302,6 +304,23 @@ func (c *OSCommand) CopyToClipboard(str string) error {
|
||||
return clipboard.WriteAll(str)
|
||||
}
|
||||
|
||||
func (c *OSCommand) PasteFromClipboard() (string, error) {
|
||||
var s string
|
||||
var err error
|
||||
if c.UserConfig().OS.CopyToClipboardCmd != "" {
|
||||
cmdStr := c.UserConfig().OS.ReadFromClipboardCmd
|
||||
s, err = c.Cmd.NewShell(cmdStr).RunWithOutput()
|
||||
} else {
|
||||
s, err = clipboard.ReadAll()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.ReplaceAll(s, "\r\n", "\n"), nil
|
||||
}
|
||||
|
||||
func (c *OSCommand) RemoveFile(path string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
c.Tr.Log.RemoveFile,
|
||||
|
||||
@@ -4,15 +4,26 @@
|
||||
package oscommands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func GetPlatform() *Platform {
|
||||
return &Platform{
|
||||
OS: runtime.GOOS,
|
||||
Shell: "bash",
|
||||
ShellArg: "-c",
|
||||
OpenCommand: "open {{filename}}",
|
||||
OpenLinkCommand: "open {{link}}",
|
||||
OS: runtime.GOOS,
|
||||
Shell: "bash",
|
||||
InteractiveShell: getUserShell(),
|
||||
ShellArg: "-c",
|
||||
InteractiveShellArg: "-i",
|
||||
OpenCommand: "open {{filename}}",
|
||||
OpenLinkCommand: "open {{link}}",
|
||||
}
|
||||
}
|
||||
|
||||
func getUserShell() string {
|
||||
if shell := os.Getenv("SHELL"); shell != "" {
|
||||
return shell
|
||||
}
|
||||
|
||||
return "bash"
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func TestOSCommandOpenFileDarwin(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
||||
oSCmd.Platform.OS = "darwin"
|
||||
oSCmd.UserConfig.OS.Open = "open {{filename}}"
|
||||
oSCmd.UserConfig().OS.Open = "open {{filename}}"
|
||||
|
||||
s.test(oSCmd.OpenFile(s.filename))
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func TestOSCommandOpenFileLinux(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
||||
oSCmd.Platform.OS = "linux"
|
||||
oSCmd.UserConfig.OS.Open = `xdg-open {{filename}} > /dev/null`
|
||||
oSCmd.UserConfig().OS.Open = `xdg-open {{filename}} > /dev/null`
|
||||
|
||||
s.test(oSCmd.OpenFile(s.filename))
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package oscommands
|
||||
|
||||
func GetPlatform() *Platform {
|
||||
return &Platform{
|
||||
OS: "windows",
|
||||
Shell: "cmd",
|
||||
ShellArg: "/c",
|
||||
OS: "windows",
|
||||
Shell: "cmd",
|
||||
InteractiveShell: "cmd",
|
||||
ShellArg: "/c",
|
||||
InteractiveShellArg: "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
||||
}
|
||||
oSCmd.Platform = platform
|
||||
oSCmd.Cmd.platform = platform
|
||||
oSCmd.UserConfig.OS.OpenCommand = `start "" {{filename}}`
|
||||
oSCmd.UserConfig().OS.OpenCommand = `start "" {{filename}}`
|
||||
|
||||
s.test(oSCmd.OpenFile(s.filename))
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (p *PatchBuilder) Start(from, to string, reverse bool, canRebase bool) {
|
||||
p.fileInfoMap = map[string]*fileInfo{}
|
||||
}
|
||||
|
||||
func (p *PatchBuilder) PatchToApply(reverse bool) string {
|
||||
func (p *PatchBuilder) PatchToApply(reverse bool, turnAddedFilesIntoDiffAgainstEmptyFile bool) string {
|
||||
patch := ""
|
||||
|
||||
for filename, info := range p.fileInfoMap {
|
||||
@@ -73,7 +73,12 @@ func (p *PatchBuilder) PatchToApply(reverse bool) string {
|
||||
continue
|
||||
}
|
||||
|
||||
patch += p.RenderPatchForFile(filename, true, reverse)
|
||||
patch += p.RenderPatchForFile(RenderPatchForFileOpts{
|
||||
Filename: filename,
|
||||
Plain: true,
|
||||
Reverse: reverse,
|
||||
TurnAddedFilesIntoDiffAgainstEmptyFile: turnAddedFilesIntoDiffAgainstEmptyFile,
|
||||
})
|
||||
}
|
||||
|
||||
return patch
|
||||
@@ -172,8 +177,15 @@ func (p *PatchBuilder) RemoveFileLineRange(filename string, firstLineIdx, lastLi
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PatchBuilder) RenderPatchForFile(filename string, plain bool, reverse bool) string {
|
||||
info, err := p.getFileInfo(filename)
|
||||
type RenderPatchForFileOpts struct {
|
||||
Filename string
|
||||
Plain bool
|
||||
Reverse bool
|
||||
TurnAddedFilesIntoDiffAgainstEmptyFile bool
|
||||
}
|
||||
|
||||
func (p *PatchBuilder) RenderPatchForFile(opts RenderPatchForFileOpts) string {
|
||||
info, err := p.getFileInfo(opts.Filename)
|
||||
if err != nil {
|
||||
p.Log.Error(err)
|
||||
return ""
|
||||
@@ -183,7 +195,7 @@ func (p *PatchBuilder) RenderPatchForFile(filename string, plain bool, reverse b
|
||||
return ""
|
||||
}
|
||||
|
||||
if info.mode == WHOLE && plain {
|
||||
if info.mode == WHOLE && opts.Plain {
|
||||
// Use the whole diff (spares us parsing it and then formatting it).
|
||||
// TODO: see if this is actually noticeably faster.
|
||||
// The reverse flag is only for part patches so we're ignoring it here.
|
||||
@@ -192,11 +204,12 @@ func (p *PatchBuilder) RenderPatchForFile(filename string, plain bool, reverse b
|
||||
|
||||
patch := Parse(info.diff).
|
||||
Transform(TransformOpts{
|
||||
Reverse: reverse,
|
||||
IncludedLineIndices: info.includedLineIndices,
|
||||
Reverse: opts.Reverse,
|
||||
TurnAddedFilesIntoDiffAgainstEmptyFile: opts.TurnAddedFilesIntoDiffAgainstEmptyFile,
|
||||
IncludedLineIndices: info.includedLineIndices,
|
||||
})
|
||||
|
||||
if plain {
|
||||
if opts.Plain {
|
||||
return patch.FormatPlain()
|
||||
} else {
|
||||
return patch.FormatView(FormatViewOpts{})
|
||||
@@ -209,7 +222,12 @@ func (p *PatchBuilder) renderEachFilePatch(plain bool) []string {
|
||||
|
||||
sort.Strings(filenames)
|
||||
patches := lo.Map(filenames, func(filename string, _ int) string {
|
||||
return p.RenderPatchForFile(filename, plain, false)
|
||||
return p.RenderPatchForFile(RenderPatchForFileOpts{
|
||||
Filename: filename,
|
||||
Plain: plain,
|
||||
Reverse: false,
|
||||
TurnAddedFilesIntoDiffAgainstEmptyFile: true,
|
||||
})
|
||||
})
|
||||
output := lo.Filter(patches, func(patch string, _ int) bool {
|
||||
return patch != ""
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package patch
|
||||
|
||||
import "github.com/samber/lo"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type patchTransformer struct {
|
||||
patch *Patch
|
||||
@@ -22,6 +26,13 @@ type TransformOpts struct {
|
||||
// information it needs to cleanly apply patches
|
||||
FileNameOverride string
|
||||
|
||||
// Custom patches tend to work better when treating new files as diffs
|
||||
// against an empty file. The only case where we need this to be false is
|
||||
// when moving a custom patch to an earlier commit; in that case the patch
|
||||
// command would fail with the error "file does not exist in index" if we
|
||||
// treat it as a diff against an empty file.
|
||||
TurnAddedFilesIntoDiffAgainstEmptyFile bool
|
||||
|
||||
// The indices of lines that should be included in the patch.
|
||||
IncludedLineIndices []int
|
||||
}
|
||||
@@ -61,6 +72,18 @@ func (self *patchTransformer) transformHeader() []string {
|
||||
"--- a/" + self.opts.FileNameOverride,
|
||||
"+++ b/" + self.opts.FileNameOverride,
|
||||
}
|
||||
} else if self.opts.TurnAddedFilesIntoDiffAgainstEmptyFile {
|
||||
result := make([]string, 0, len(self.patch.header))
|
||||
for idx, line := range self.patch.header {
|
||||
if strings.HasPrefix(line, "new file mode") {
|
||||
continue
|
||||
}
|
||||
if line == "--- /dev/null" && strings.HasPrefix(self.patch.header[idx+1], "+++ b/") {
|
||||
line = "--- a/" + self.patch.header[idx+1][6:]
|
||||
}
|
||||
result = append(result, line)
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
return self.patch.header
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -11,10 +13,18 @@ import (
|
||||
type Common struct {
|
||||
Log *logrus.Entry
|
||||
Tr *i18n.TranslationSet
|
||||
UserConfig *config.UserConfig
|
||||
userConfig atomic.Pointer[config.UserConfig]
|
||||
AppState *config.AppState
|
||||
Debug bool
|
||||
// for interacting with the filesystem. We use afero rather than the default
|
||||
// `os` package for the sake of mocking the filesystem in tests
|
||||
Fs afero.Fs
|
||||
}
|
||||
|
||||
func (c *Common) UserConfig() *config.UserConfig {
|
||||
return c.userConfig.Load()
|
||||
}
|
||||
|
||||
func (c *Common) SetUserConfig(userConfig *config.UserConfig) {
|
||||
c.userConfig.Store(userConfig)
|
||||
}
|
||||
|
||||
@@ -2,29 +2,31 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
|
||||
"github.com/samber/lo"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// AppConfig contains the base configuration fields required for lazygit.
|
||||
type AppConfig struct {
|
||||
Debug bool `long:"debug" env:"DEBUG" default:"false"`
|
||||
Version string `long:"version" env:"VERSION" default:"unversioned"`
|
||||
BuildDate string `long:"build-date" env:"BUILD_DATE"`
|
||||
Name string `long:"name" env:"NAME" default:"lazygit"`
|
||||
BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
|
||||
UserConfig *UserConfig
|
||||
UserConfigPaths []string
|
||||
DeafultConfFiles bool
|
||||
UserConfigDir string
|
||||
TempDir string
|
||||
AppState *AppState
|
||||
IsNewRepo bool
|
||||
debug bool `long:"debug" env:"DEBUG" default:"false"`
|
||||
version string `long:"version" env:"VERSION" default:"unversioned"`
|
||||
buildDate string `long:"build-date" env:"BUILD_DATE"`
|
||||
name string `long:"name" env:"NAME" default:"lazygit"`
|
||||
buildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
|
||||
userConfig *UserConfig
|
||||
globalUserConfigFiles []*ConfigFile
|
||||
userConfigFiles []*ConfigFile
|
||||
userConfigDir string
|
||||
tempDir string
|
||||
appState *AppState
|
||||
}
|
||||
|
||||
type AppConfigurer interface {
|
||||
@@ -38,13 +40,29 @@ type AppConfigurer interface {
|
||||
GetUserConfig() *UserConfig
|
||||
GetUserConfigPaths() []string
|
||||
GetUserConfigDir() string
|
||||
ReloadUserConfig() error
|
||||
ReloadUserConfigForRepo(repoConfigFiles []*ConfigFile) error
|
||||
ReloadChangedUserConfigFiles() (error, bool)
|
||||
GetTempDir() string
|
||||
|
||||
GetAppState() *AppState
|
||||
SaveAppState() error
|
||||
}
|
||||
|
||||
type ConfigFilePolicy int
|
||||
|
||||
const (
|
||||
ConfigFilePolicyCreateIfMissing ConfigFilePolicy = iota
|
||||
ConfigFilePolicyErrorIfMissing
|
||||
ConfigFilePolicySkipIfMissing
|
||||
)
|
||||
|
||||
type ConfigFile struct {
|
||||
Path string
|
||||
Policy ConfigFilePolicy
|
||||
modDate time.Time
|
||||
exists bool
|
||||
}
|
||||
|
||||
// NewAppConfig makes a new app config
|
||||
func NewAppConfig(
|
||||
name string,
|
||||
@@ -60,17 +78,22 @@ func NewAppConfig(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var userConfigPaths []string
|
||||
var configFiles []*ConfigFile
|
||||
customConfigFiles := os.Getenv("LG_CONFIG_FILE")
|
||||
if customConfigFiles != "" {
|
||||
// Load user defined config files
|
||||
userConfigPaths = strings.Split(customConfigFiles, ",")
|
||||
userConfigPaths := strings.Split(customConfigFiles, ",")
|
||||
configFiles = lo.Map(userConfigPaths, func(path string, _ int) *ConfigFile {
|
||||
return &ConfigFile{Path: path, Policy: ConfigFilePolicyErrorIfMissing}
|
||||
})
|
||||
} else {
|
||||
// Load default config files
|
||||
userConfigPaths = []string{filepath.Join(configDir, ConfigFilename)}
|
||||
path := filepath.Join(configDir, ConfigFilename)
|
||||
configFile := &ConfigFile{Path: path, Policy: ConfigFilePolicyCreateIfMissing}
|
||||
configFiles = []*ConfigFile{configFile}
|
||||
}
|
||||
|
||||
userConfig, err := loadUserConfigWithDefaults(userConfigPaths)
|
||||
userConfig, err := loadUserConfigWithDefaults(configFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,26 +115,22 @@ func NewAppConfig(
|
||||
}
|
||||
|
||||
appConfig := &AppConfig{
|
||||
Name: name,
|
||||
Version: version,
|
||||
BuildDate: date,
|
||||
Debug: debuggingFlag,
|
||||
BuildSource: buildSource,
|
||||
UserConfig: userConfig,
|
||||
UserConfigPaths: userConfigPaths,
|
||||
UserConfigDir: configDir,
|
||||
TempDir: tempDir,
|
||||
AppState: appState,
|
||||
IsNewRepo: false,
|
||||
name: name,
|
||||
version: version,
|
||||
buildDate: date,
|
||||
debug: debuggingFlag,
|
||||
buildSource: buildSource,
|
||||
userConfig: userConfig,
|
||||
globalUserConfigFiles: configFiles,
|
||||
userConfigFiles: configFiles,
|
||||
userConfigDir: configDir,
|
||||
tempDir: tempDir,
|
||||
appState: appState,
|
||||
}
|
||||
|
||||
return appConfig, nil
|
||||
}
|
||||
|
||||
func isCustomConfigFile(path string) bool {
|
||||
return path != filepath.Join(ConfigDir(), ConfigFilename)
|
||||
}
|
||||
|
||||
func ConfigDir() string {
|
||||
_, filePath := findConfigFile("config.yml")
|
||||
|
||||
@@ -123,32 +142,48 @@ func findOrCreateConfigDir() (string, error) {
|
||||
return folder, os.MkdirAll(folder, 0o755)
|
||||
}
|
||||
|
||||
func loadUserConfigWithDefaults(configFiles []string) (*UserConfig, error) {
|
||||
func loadUserConfigWithDefaults(configFiles []*ConfigFile) (*UserConfig, error) {
|
||||
return loadUserConfig(configFiles, GetDefaultConfig())
|
||||
}
|
||||
|
||||
func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error) {
|
||||
for _, path := range configFiles {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
func loadUserConfig(configFiles []*ConfigFile, base *UserConfig) (*UserConfig, error) {
|
||||
for _, configFile := range configFiles {
|
||||
path := configFile.Path
|
||||
statInfo, err := os.Stat(path)
|
||||
if err == nil {
|
||||
configFile.exists = true
|
||||
configFile.modDate = statInfo.ModTime()
|
||||
} else {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if use has supplied their own custom config file path(s), we assume
|
||||
// the files have already been created, so we won't go and create them here.
|
||||
if isCustomConfigFile(path) {
|
||||
switch configFile.Policy {
|
||||
case ConfigFilePolicyErrorIfMissing:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
// apparently when people have read-only permissions they prefer us to fail silently
|
||||
continue
|
||||
case ConfigFilePolicySkipIfMissing:
|
||||
configFile.exists = false
|
||||
continue
|
||||
|
||||
case ConfigFilePolicyCreateIfMissing:
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
// apparently when people have read-only permissions they prefer us to fail silently
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
file.Close()
|
||||
|
||||
configFile.exists = true
|
||||
statInfo, err := os.Stat(configFile.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configFile.modDate = statInfo.ModTime()
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(path)
|
||||
@@ -161,10 +196,14 @@ func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existingCustomCommands := base.CustomCommands
|
||||
|
||||
if err := yaml.Unmarshal(content, base); err != nil {
|
||||
return nil, fmt.Errorf("The config at `%s` couldn't be parsed, please inspect it before opening up an issue.\n%w", path, err)
|
||||
}
|
||||
|
||||
base.CustomCommands = append(base.CustomCommands, existingCustomCommands...)
|
||||
|
||||
if err := base.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("The config at `%s` has a validation error.\n%w", path, err)
|
||||
}
|
||||
@@ -184,6 +223,12 @@ func migrateUserConfig(path string, content []byte) ([]byte, error) {
|
||||
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
|
||||
}
|
||||
|
||||
changedContent, err = yaml_utils.RenameYamlKey(changedContent, []string{"keybinding", "universal", "executeCustomCommand"},
|
||||
"executeShellCommand")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
|
||||
}
|
||||
|
||||
changedContent, err = changeNullKeybindingsToDisabled(changedContent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
|
||||
@@ -214,53 +259,81 @@ func changeNullKeybindingsToDisabled(changedContent []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetDebug() bool {
|
||||
return c.Debug
|
||||
return c.debug
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetVersion() string {
|
||||
return c.Version
|
||||
return c.version
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetName() string {
|
||||
return c.Name
|
||||
return c.name
|
||||
}
|
||||
|
||||
// GetBuildSource returns the source of the build. For builds from goreleaser
|
||||
// this will be binaryBuild
|
||||
func (c *AppConfig) GetBuildSource() string {
|
||||
return c.BuildSource
|
||||
return c.buildSource
|
||||
}
|
||||
|
||||
// GetUserConfig returns the user config
|
||||
func (c *AppConfig) GetUserConfig() *UserConfig {
|
||||
return c.UserConfig
|
||||
return c.userConfig
|
||||
}
|
||||
|
||||
// GetAppState returns the app state
|
||||
func (c *AppConfig) GetAppState() *AppState {
|
||||
return c.AppState
|
||||
return c.appState
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetUserConfigPaths() []string {
|
||||
return c.UserConfigPaths
|
||||
return lo.FilterMap(c.userConfigFiles, func(f *ConfigFile, _ int) (string, bool) {
|
||||
return f.Path, f.exists
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetUserConfigDir() string {
|
||||
return c.UserConfigDir
|
||||
return c.userConfigDir
|
||||
}
|
||||
|
||||
func (c *AppConfig) ReloadUserConfig() error {
|
||||
userConfig, err := loadUserConfigWithDefaults(c.UserConfigPaths)
|
||||
func (c *AppConfig) ReloadUserConfigForRepo(repoConfigFiles []*ConfigFile) error {
|
||||
configFiles := append(c.globalUserConfigFiles, repoConfigFiles...)
|
||||
userConfig, err := loadUserConfigWithDefaults(configFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.UserConfig = userConfig
|
||||
c.userConfig = userConfig
|
||||
c.userConfigFiles = configFiles
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AppConfig) ReloadChangedUserConfigFiles() (error, bool) {
|
||||
fileHasChanged := func(f *ConfigFile) bool {
|
||||
info, err := os.Stat(f.Path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
// If we can't stat the file, assume it hasn't changed
|
||||
return false
|
||||
}
|
||||
exists := err == nil
|
||||
return exists != f.exists || (exists && info.ModTime() != f.modDate)
|
||||
}
|
||||
|
||||
if lo.NoneBy(c.userConfigFiles, fileHasChanged) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
userConfig, err := loadUserConfigWithDefaults(c.userConfigFiles)
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
|
||||
c.userConfig = userConfig
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetTempDir() string {
|
||||
return c.TempDir
|
||||
return c.tempDir
|
||||
}
|
||||
|
||||
// findConfigFile looks for a possibly existing config file.
|
||||
@@ -299,14 +372,9 @@ func stateFilePath(filename string) (string, error) {
|
||||
return xdg.StateFile(filepath.Join("lazygit", filename))
|
||||
}
|
||||
|
||||
// ConfigFilename returns the filename of the default config file
|
||||
func (c *AppConfig) ConfigFilename() string {
|
||||
return filepath.Join(c.UserConfigDir, ConfigFilename)
|
||||
}
|
||||
|
||||
// SaveAppState marshalls the AppState struct and writes it to the disk
|
||||
func (c *AppConfig) SaveAppState() error {
|
||||
marshalledAppState, err := yaml.Marshal(c.AppState)
|
||||
marshalledAppState, err := yaml.Marshal(c.appState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -357,6 +425,24 @@ func loadAppState() (*AppState, error) {
|
||||
return appState, nil
|
||||
}
|
||||
|
||||
// SaveGlobalUserConfig saves the UserConfig back to disk. This is only used in
|
||||
// integration tests, so we are a bit sloppy with error handling.
|
||||
func (c *AppConfig) SaveGlobalUserConfig() {
|
||||
if len(c.globalUserConfigFiles) != 1 {
|
||||
panic("expected exactly one global user config file")
|
||||
}
|
||||
|
||||
yamlContent, err := yaml.Marshal(c.userConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("error marshalling user config: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(c.globalUserConfigFiles[0].Path, yamlContent, 0o644)
|
||||
if err != nil {
|
||||
log.Fatalf("error saving user config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// AppState stores data between runs of the app like when the last update check
|
||||
// was performed and which other repos have been checked out
|
||||
type AppState struct {
|
||||
@@ -365,11 +451,14 @@ type AppState struct {
|
||||
StartupPopupVersion int
|
||||
LastVersion string // this is the last version the user was using, for the purpose of showing release notes
|
||||
|
||||
// these are for custom commands typed in directly, not for custom commands in the lazygit config
|
||||
CustomCommandsHistory []string
|
||||
// these are for shell commands typed in directly, not for custom commands in the lazygit config.
|
||||
// For backwards compatibility we keep the old name in yaml files.
|
||||
ShellCommandsHistory []string `yaml:"customcommandshistory"`
|
||||
|
||||
HideCommandLog bool
|
||||
IgnoreWhitespaceInDiffView bool
|
||||
DiffContextSize int
|
||||
RenameSimilarityThreshold int
|
||||
LocalBranchSortOrder string
|
||||
RemoteBranchSortOrder string
|
||||
|
||||
@@ -385,15 +474,16 @@ type AppState struct {
|
||||
|
||||
func getDefaultAppState() *AppState {
|
||||
return &AppState{
|
||||
LastUpdateCheck: 0,
|
||||
RecentRepos: []string{},
|
||||
StartupPopupVersion: 0,
|
||||
LastVersion: "",
|
||||
DiffContextSize: 3,
|
||||
LocalBranchSortOrder: "recency",
|
||||
RemoteBranchSortOrder: "alphabetical",
|
||||
GitLogOrder: "", // should be "topo-order" eventually
|
||||
GitLogShowGraph: "", // should be "always" eventually
|
||||
LastUpdateCheck: 0,
|
||||
RecentRepos: []string{},
|
||||
StartupPopupVersion: 0,
|
||||
LastVersion: "",
|
||||
DiffContextSize: 3,
|
||||
RenameSimilarityThreshold: 50,
|
||||
LocalBranchSortOrder: "recency",
|
||||
RemoteBranchSortOrder: "alphabetical",
|
||||
GitLogOrder: "", // should be "topo-order" eventually
|
||||
GitLogShowGraph: "", // should be "always" eventually
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ import (
|
||||
// NewDummyAppConfig creates a new dummy AppConfig for testing
|
||||
func NewDummyAppConfig() *AppConfig {
|
||||
appConfig := &AppConfig{
|
||||
Name: "lazygit",
|
||||
Version: "unversioned",
|
||||
Debug: false,
|
||||
UserConfig: GetDefaultConfig(),
|
||||
AppState: &AppState{},
|
||||
name: "lazygit",
|
||||
version: "unversioned",
|
||||
debug: false,
|
||||
userConfig: GetDefaultConfig(),
|
||||
appState: &AppState{},
|
||||
}
|
||||
_ = yaml.Unmarshal([]byte{}, appConfig.AppState)
|
||||
_ = yaml.Unmarshal([]byte{}, appConfig.appState)
|
||||
return appConfig
|
||||
}
|
||||
|
||||
@@ -113,6 +113,13 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
||||
openDirInEditorTemplate: "xed -- {{dir}}",
|
||||
suspend: returnBool(false),
|
||||
},
|
||||
"zed": {
|
||||
editTemplate: "zed -- {{filename}}",
|
||||
editAtLineTemplate: "zed -- {{filename}}:{{line}}",
|
||||
editAtLineAndWaitTemplate: "zed --wait -- {{filename}}:{{line}}",
|
||||
openDirInEditorTemplate: "zed -- {{dir}}",
|
||||
suspend: returnBool(false),
|
||||
},
|
||||
}
|
||||
|
||||
// Some of our presets have a different name than the editor they are using.
|
||||
|
||||
@@ -125,6 +125,10 @@ type GuiConfig struct {
|
||||
NerdFontsVersion string `yaml:"nerdFontsVersion" jsonschema:"enum=2,enum=3,enum="`
|
||||
// If true (default), file icons are shown in the file views. Only relevant if NerdFontsVersion is not empty.
|
||||
ShowFileIcons bool `yaml:"showFileIcons"`
|
||||
// Length of author name in (non-expanded) commits view. 2 means show initials only.
|
||||
CommitAuthorShortLength int `yaml:"commitAuthorShortLength"`
|
||||
// Length of author name in expanded commits view. 2 means show initials only.
|
||||
CommitAuthorLongLength int `yaml:"commitAuthorLongLength"`
|
||||
// Length of commit hash in commits view. 0 shows '*' if NF icons aren't on.
|
||||
CommitHashLength int `yaml:"commitHashLength" jsonschema:"minimum=0"`
|
||||
// If true, show commit hashes alongside branch names in the branches view.
|
||||
@@ -157,8 +161,10 @@ type GuiConfig struct {
|
||||
// Status panel view.
|
||||
// One of 'dashboard' (default) | 'allBranchesLog'
|
||||
StatusPanelView string `yaml:"statusPanelView" jsonschema:"enum=dashboard,enum=allBranchesLog"`
|
||||
// Horizontal padding between views that are next to each other.
|
||||
HorizontalPadding int `yaml:"horizontalPadding"`
|
||||
// If true, jump to the Files panel after popping a stash
|
||||
SwitchToFilesAfterStashPop bool `yaml:"switchToFilesAfterStashPop"`
|
||||
// If true, jump to the Files panel after applying a stash
|
||||
SwitchToFilesAfterStashApply bool `yaml:"switchToFilesAfterStashApply"`
|
||||
}
|
||||
|
||||
func (c *GuiConfig) UseFuzzySearch() bool {
|
||||
@@ -177,6 +183,8 @@ type ThemeConfig struct {
|
||||
// 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"`
|
||||
// Background color of selected line when view doesn't have focus.
|
||||
InactiveViewSelectedLineBgColor []string `yaml:"inactiveViewSelectedLineBgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Foreground color of copied commit
|
||||
CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Background color of copied commit
|
||||
@@ -220,10 +228,18 @@ type GitConfig struct {
|
||||
AutoRefresh bool `yaml:"autoRefresh"`
|
||||
// If true, pass the --all arg to git fetch
|
||||
FetchAll bool `yaml:"fetchAll"`
|
||||
// If true, lazygit will automatically stage files that used to have merge
|
||||
// conflicts but no longer do; and it will also ask you if you want to
|
||||
// continue a merge or rebase if you've resolved all conflicts. If false, it
|
||||
// won't do either of these things.
|
||||
AutoStageResolvedConflicts bool `yaml:"autoStageResolvedConflicts"`
|
||||
// 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
|
||||
// Command used to display git log of all branches in the main window.
|
||||
// Deprecated: User `allBranchesLogCmds` instead.
|
||||
AllBranchesLogCmd string `yaml:"allBranchesLogCmd"`
|
||||
// Commands used to display git log of all branches in the main window, they will be cycled in order of appearance
|
||||
AllBranchesLogCmds []string `yaml:"allBranchesLogCmds"`
|
||||
// If true, do not spawn a separate process when using GPG
|
||||
OverrideGpg bool `yaml:"overrideGpg"`
|
||||
// If true, do not allow force pushes
|
||||
@@ -232,6 +248,8 @@ type GitConfig struct {
|
||||
CommitPrefix *CommitPrefixConfig `yaml:"commitPrefix"`
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix
|
||||
CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"`
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-branch-name-prefix
|
||||
BranchPrefix string `yaml:"branchPrefix"`
|
||||
// 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"`
|
||||
@@ -281,6 +299,8 @@ type MergingConfig struct {
|
||||
ManualCommit bool `yaml:"manualCommit"`
|
||||
// Extra args passed to `git merge`, e.g. --no-ff
|
||||
Args string `yaml:"args" jsonschema:"example=--no-ff"`
|
||||
// The commit message to use for a squash merge commit. Can contain "{{selectedRef}}" and "{{currentBranch}}" placeholders.
|
||||
SquashMergeMessage string `yaml:"squashMergeMessage"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
@@ -330,73 +350,75 @@ type KeybindingConfig struct {
|
||||
|
||||
// damn looks like we have some inconsistencies here with -alt and -alt1
|
||||
type KeybindingUniversalConfig struct {
|
||||
Quit string `yaml:"quit"`
|
||||
QuitAlt1 string `yaml:"quit-alt1"`
|
||||
Return string `yaml:"return"`
|
||||
QuitWithoutChangingDirectory string `yaml:"quitWithoutChangingDirectory"`
|
||||
TogglePanel string `yaml:"togglePanel"`
|
||||
PrevItem string `yaml:"prevItem"`
|
||||
NextItem string `yaml:"nextItem"`
|
||||
PrevItemAlt string `yaml:"prevItem-alt"`
|
||||
NextItemAlt string `yaml:"nextItem-alt"`
|
||||
PrevPage string `yaml:"prevPage"`
|
||||
NextPage string `yaml:"nextPage"`
|
||||
ScrollLeft string `yaml:"scrollLeft"`
|
||||
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"`
|
||||
NextBlockAlt string `yaml:"nextBlock-alt"`
|
||||
NextBlockAlt2 string `yaml:"nextBlock-alt2"`
|
||||
PrevBlockAlt2 string `yaml:"prevBlock-alt2"`
|
||||
JumpToBlock []string `yaml:"jumpToBlock"`
|
||||
NextMatch string `yaml:"nextMatch"`
|
||||
PrevMatch string `yaml:"prevMatch"`
|
||||
StartSearch string `yaml:"startSearch"`
|
||||
OptionMenu string `yaml:"optionMenu"`
|
||||
OptionMenuAlt1 string `yaml:"optionMenu-alt1"`
|
||||
Select string `yaml:"select"`
|
||||
GoInto string `yaml:"goInto"`
|
||||
Confirm string `yaml:"confirm"`
|
||||
ConfirmInEditor string `yaml:"confirmInEditor"`
|
||||
Remove string `yaml:"remove"`
|
||||
New string `yaml:"new"`
|
||||
Edit string `yaml:"edit"`
|
||||
OpenFile string `yaml:"openFile"`
|
||||
ScrollUpMain string `yaml:"scrollUpMain"`
|
||||
ScrollDownMain string `yaml:"scrollDownMain"`
|
||||
ScrollUpMainAlt1 string `yaml:"scrollUpMain-alt1"`
|
||||
ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"`
|
||||
ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"`
|
||||
ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"`
|
||||
ExecuteCustomCommand string `yaml:"executeCustomCommand"`
|
||||
CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"`
|
||||
Push string `yaml:"pushFiles"` // 'Files' appended for legacy reasons
|
||||
Pull string `yaml:"pullFiles"` // 'Files' appended for legacy reasons
|
||||
Refresh string `yaml:"refresh"`
|
||||
CreatePatchOptionsMenu string `yaml:"createPatchOptionsMenu"`
|
||||
NextTab string `yaml:"nextTab"`
|
||||
PrevTab string `yaml:"prevTab"`
|
||||
NextScreenMode string `yaml:"nextScreenMode"`
|
||||
PrevScreenMode string `yaml:"prevScreenMode"`
|
||||
Undo string `yaml:"undo"`
|
||||
Redo string `yaml:"redo"`
|
||||
FilteringMenu string `yaml:"filteringMenu"`
|
||||
DiffingMenu string `yaml:"diffingMenu"`
|
||||
DiffingMenuAlt string `yaml:"diffingMenu-alt"`
|
||||
CopyToClipboard string `yaml:"copyToClipboard"`
|
||||
OpenRecentRepos string `yaml:"openRecentRepos"`
|
||||
SubmitEditorText string `yaml:"submitEditorText"`
|
||||
ExtrasMenu string `yaml:"extrasMenu"`
|
||||
ToggleWhitespaceInDiffView string `yaml:"toggleWhitespaceInDiffView"`
|
||||
IncreaseContextInDiffView string `yaml:"increaseContextInDiffView"`
|
||||
DecreaseContextInDiffView string `yaml:"decreaseContextInDiffView"`
|
||||
OpenDiffTool string `yaml:"openDiffTool"`
|
||||
Quit string `yaml:"quit"`
|
||||
QuitAlt1 string `yaml:"quit-alt1"`
|
||||
Return string `yaml:"return"`
|
||||
QuitWithoutChangingDirectory string `yaml:"quitWithoutChangingDirectory"`
|
||||
TogglePanel string `yaml:"togglePanel"`
|
||||
PrevItem string `yaml:"prevItem"`
|
||||
NextItem string `yaml:"nextItem"`
|
||||
PrevItemAlt string `yaml:"prevItem-alt"`
|
||||
NextItemAlt string `yaml:"nextItem-alt"`
|
||||
PrevPage string `yaml:"prevPage"`
|
||||
NextPage string `yaml:"nextPage"`
|
||||
ScrollLeft string `yaml:"scrollLeft"`
|
||||
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"`
|
||||
NextBlockAlt string `yaml:"nextBlock-alt"`
|
||||
NextBlockAlt2 string `yaml:"nextBlock-alt2"`
|
||||
PrevBlockAlt2 string `yaml:"prevBlock-alt2"`
|
||||
JumpToBlock []string `yaml:"jumpToBlock"`
|
||||
NextMatch string `yaml:"nextMatch"`
|
||||
PrevMatch string `yaml:"prevMatch"`
|
||||
StartSearch string `yaml:"startSearch"`
|
||||
OptionMenu string `yaml:"optionMenu"`
|
||||
OptionMenuAlt1 string `yaml:"optionMenu-alt1"`
|
||||
Select string `yaml:"select"`
|
||||
GoInto string `yaml:"goInto"`
|
||||
Confirm string `yaml:"confirm"`
|
||||
ConfirmInEditor string `yaml:"confirmInEditor"`
|
||||
Remove string `yaml:"remove"`
|
||||
New string `yaml:"new"`
|
||||
Edit string `yaml:"edit"`
|
||||
OpenFile string `yaml:"openFile"`
|
||||
ScrollUpMain string `yaml:"scrollUpMain"`
|
||||
ScrollDownMain string `yaml:"scrollDownMain"`
|
||||
ScrollUpMainAlt1 string `yaml:"scrollUpMain-alt1"`
|
||||
ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"`
|
||||
ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"`
|
||||
ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"`
|
||||
ExecuteShellCommand string `yaml:"executeShellCommand"`
|
||||
CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"`
|
||||
Push string `yaml:"pushFiles"` // 'Files' appended for legacy reasons
|
||||
Pull string `yaml:"pullFiles"` // 'Files' appended for legacy reasons
|
||||
Refresh string `yaml:"refresh"`
|
||||
CreatePatchOptionsMenu string `yaml:"createPatchOptionsMenu"`
|
||||
NextTab string `yaml:"nextTab"`
|
||||
PrevTab string `yaml:"prevTab"`
|
||||
NextScreenMode string `yaml:"nextScreenMode"`
|
||||
PrevScreenMode string `yaml:"prevScreenMode"`
|
||||
Undo string `yaml:"undo"`
|
||||
Redo string `yaml:"redo"`
|
||||
FilteringMenu string `yaml:"filteringMenu"`
|
||||
DiffingMenu string `yaml:"diffingMenu"`
|
||||
DiffingMenuAlt string `yaml:"diffingMenu-alt"`
|
||||
CopyToClipboard string `yaml:"copyToClipboard"`
|
||||
OpenRecentRepos string `yaml:"openRecentRepos"`
|
||||
SubmitEditorText string `yaml:"submitEditorText"`
|
||||
ExtrasMenu string `yaml:"extrasMenu"`
|
||||
ToggleWhitespaceInDiffView string `yaml:"toggleWhitespaceInDiffView"`
|
||||
IncreaseContextInDiffView string `yaml:"increaseContextInDiffView"`
|
||||
DecreaseContextInDiffView string `yaml:"decreaseContextInDiffView"`
|
||||
IncreaseRenameSimilarityThreshold string `yaml:"increaseRenameSimilarityThreshold"`
|
||||
DecreaseRenameSimilarityThreshold string `yaml:"decreaseRenameSimilarityThreshold"`
|
||||
OpenDiffTool string `yaml:"openDiffTool"`
|
||||
}
|
||||
|
||||
type KeybindingStatusConfig struct {
|
||||
@@ -528,7 +550,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" jsonschema:"example=vim,example=nvim,example=emacs,example=nano,example=vscode,example=sublime,example=kakoune,example=helix,example=xcode"`
|
||||
EditPreset string `yaml:"editPreset,omitempty" jsonschema:"example=vim,example=nvim,example=emacs,example=nano,example=vscode,example=sublime,example=kakoune,example=helix,example=xcode,example=zed"`
|
||||
|
||||
// Command for opening a file, as if the file is double-clicked. Should
|
||||
// contain "{{filename}}", but doesn't support "{{line}}".
|
||||
@@ -561,8 +583,12 @@ type OSConfig struct {
|
||||
OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
|
||||
|
||||
// CopyToClipboardCmd is the command for copying to clipboard.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
|
||||
CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"`
|
||||
|
||||
// ReadFromClipboardCmd is the command for reading the clipboard.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
|
||||
ReadFromClipboardCmd string `yaml:"readFromClipboardCmd,omitempty"`
|
||||
}
|
||||
|
||||
type CustomCommandAfterHook struct {
|
||||
@@ -666,17 +692,18 @@ func GetDefaultConfig() *UserConfig {
|
||||
TimeFormat: "02 Jan 06",
|
||||
ShortTimeFormat: time.Kitchen,
|
||||
Theme: ThemeConfig{
|
||||
ActiveBorderColor: []string{"green", "bold"},
|
||||
SearchingActiveBorderColor: []string{"cyan", "bold"},
|
||||
InactiveBorderColor: []string{"default"},
|
||||
OptionsTextColor: []string{"blue"},
|
||||
SelectedLineBgColor: []string{"blue"},
|
||||
CherryPickedCommitBgColor: []string{"cyan"},
|
||||
CherryPickedCommitFgColor: []string{"blue"},
|
||||
MarkedBaseCommitBgColor: []string{"yellow"},
|
||||
MarkedBaseCommitFgColor: []string{"blue"},
|
||||
UnstagedChangesColor: []string{"red"},
|
||||
DefaultFgColor: []string{"default"},
|
||||
ActiveBorderColor: []string{"green", "bold"},
|
||||
SearchingActiveBorderColor: []string{"cyan", "bold"},
|
||||
InactiveBorderColor: []string{"default"},
|
||||
OptionsTextColor: []string{"blue"},
|
||||
SelectedLineBgColor: []string{"blue"},
|
||||
InactiveViewSelectedLineBgColor: []string{"bold"},
|
||||
CherryPickedCommitBgColor: []string{"cyan"},
|
||||
CherryPickedCommitFgColor: []string{"blue"},
|
||||
MarkedBaseCommitBgColor: []string{"yellow"},
|
||||
MarkedBaseCommitFgColor: []string{"blue"},
|
||||
UnstagedChangesColor: []string{"red"},
|
||||
DefaultFgColor: []string{"default"},
|
||||
},
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
@@ -689,6 +716,8 @@ func GetDefaultConfig() *UserConfig {
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ShowFileIcons: true,
|
||||
CommitAuthorShortLength: 2,
|
||||
CommitAuthorLongLength: 17,
|
||||
CommitHashLength: 8,
|
||||
ShowBranchCommitHash: false,
|
||||
ShowDivergenceFromBaseBranch: "none",
|
||||
@@ -704,8 +733,9 @@ func GetDefaultConfig() *UserConfig {
|
||||
Frames: []string{"|", "/", "-", "\\"},
|
||||
Rate: 50,
|
||||
},
|
||||
StatusPanelView: "dashboard",
|
||||
HorizontalPadding: 0,
|
||||
StatusPanelView: "dashboard",
|
||||
SwitchToFilesAfterStashPop: true,
|
||||
SwitchToFilesAfterStashApply: true,
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
@@ -720,8 +750,9 @@ func GetDefaultConfig() *UserConfig {
|
||||
AutoWrapWidth: 72,
|
||||
},
|
||||
Merging: MergingConfig{
|
||||
ManualCommit: false,
|
||||
Args: "",
|
||||
ManualCommit: false,
|
||||
Args: "",
|
||||
SquashMergeMessage: "Squash merge {{selectedRef}} into {{currentBranch}}",
|
||||
},
|
||||
Log: LogConfig{
|
||||
Order: "topo-order",
|
||||
@@ -733,10 +764,12 @@ func GetDefaultConfig() *UserConfig {
|
||||
AutoFetch: true,
|
||||
AutoRefresh: true,
|
||||
FetchAll: true,
|
||||
AutoStageResolvedConflicts: true,
|
||||
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
|
||||
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium",
|
||||
DisableForcePushing: false,
|
||||
CommitPrefixes: map[string]CommitPrefixConfig(nil),
|
||||
BranchPrefix: "",
|
||||
ParseEmoji: false,
|
||||
TruncateCopiedCommitHashesTo: 12,
|
||||
},
|
||||
@@ -758,73 +791,75 @@ func GetDefaultConfig() *UserConfig {
|
||||
PromptToReturnFromSubprocess: true,
|
||||
Keybinding: KeybindingConfig{
|
||||
Universal: KeybindingUniversalConfig{
|
||||
Quit: "q",
|
||||
QuitAlt1: "<c-c>",
|
||||
Return: "<esc>",
|
||||
QuitWithoutChangingDirectory: "Q",
|
||||
TogglePanel: "<tab>",
|
||||
PrevItem: "<up>",
|
||||
NextItem: "<down>",
|
||||
PrevItemAlt: "k",
|
||||
NextItemAlt: "j",
|
||||
PrevPage: ",",
|
||||
NextPage: ".",
|
||||
ScrollLeft: "H",
|
||||
ScrollRight: "L",
|
||||
GotoTop: "<",
|
||||
GotoBottom: ">",
|
||||
ToggleRangeSelect: "v",
|
||||
RangeSelectDown: "<s-down>",
|
||||
RangeSelectUp: "<s-up>",
|
||||
PrevBlock: "<left>",
|
||||
NextBlock: "<right>",
|
||||
PrevBlockAlt: "h",
|
||||
NextBlockAlt: "l",
|
||||
PrevBlockAlt2: "<backtab>",
|
||||
NextBlockAlt2: "<tab>",
|
||||
JumpToBlock: []string{"1", "2", "3", "4", "5"},
|
||||
NextMatch: "n",
|
||||
PrevMatch: "N",
|
||||
StartSearch: "/",
|
||||
OptionMenu: "<disabled>",
|
||||
OptionMenuAlt1: "?",
|
||||
Select: "<space>",
|
||||
GoInto: "<enter>",
|
||||
Confirm: "<enter>",
|
||||
ConfirmInEditor: "<a-enter>",
|
||||
Remove: "d",
|
||||
New: "n",
|
||||
Edit: "e",
|
||||
OpenFile: "o",
|
||||
OpenRecentRepos: "<c-r>",
|
||||
ScrollUpMain: "<pgup>",
|
||||
ScrollDownMain: "<pgdown>",
|
||||
ScrollUpMainAlt1: "K",
|
||||
ScrollDownMainAlt1: "J",
|
||||
ScrollUpMainAlt2: "<c-u>",
|
||||
ScrollDownMainAlt2: "<c-d>",
|
||||
ExecuteCustomCommand: ":",
|
||||
CreateRebaseOptionsMenu: "m",
|
||||
Push: "P",
|
||||
Pull: "p",
|
||||
Refresh: "R",
|
||||
CreatePatchOptionsMenu: "<c-p>",
|
||||
NextTab: "]",
|
||||
PrevTab: "[",
|
||||
NextScreenMode: "+",
|
||||
PrevScreenMode: "_",
|
||||
Undo: "z",
|
||||
Redo: "<c-z>",
|
||||
FilteringMenu: "<c-s>",
|
||||
DiffingMenu: "W",
|
||||
DiffingMenuAlt: "<c-e>",
|
||||
CopyToClipboard: "<c-o>",
|
||||
SubmitEditorText: "<enter>",
|
||||
ExtrasMenu: "@",
|
||||
ToggleWhitespaceInDiffView: "<c-w>",
|
||||
IncreaseContextInDiffView: "}",
|
||||
DecreaseContextInDiffView: "{",
|
||||
OpenDiffTool: "<c-t>",
|
||||
Quit: "q",
|
||||
QuitAlt1: "<c-c>",
|
||||
Return: "<esc>",
|
||||
QuitWithoutChangingDirectory: "Q",
|
||||
TogglePanel: "<tab>",
|
||||
PrevItem: "<up>",
|
||||
NextItem: "<down>",
|
||||
PrevItemAlt: "k",
|
||||
NextItemAlt: "j",
|
||||
PrevPage: ",",
|
||||
NextPage: ".",
|
||||
ScrollLeft: "H",
|
||||
ScrollRight: "L",
|
||||
GotoTop: "<",
|
||||
GotoBottom: ">",
|
||||
ToggleRangeSelect: "v",
|
||||
RangeSelectDown: "<s-down>",
|
||||
RangeSelectUp: "<s-up>",
|
||||
PrevBlock: "<left>",
|
||||
NextBlock: "<right>",
|
||||
PrevBlockAlt: "h",
|
||||
NextBlockAlt: "l",
|
||||
PrevBlockAlt2: "<backtab>",
|
||||
NextBlockAlt2: "<tab>",
|
||||
JumpToBlock: []string{"1", "2", "3", "4", "5"},
|
||||
NextMatch: "n",
|
||||
PrevMatch: "N",
|
||||
StartSearch: "/",
|
||||
OptionMenu: "<disabled>",
|
||||
OptionMenuAlt1: "?",
|
||||
Select: "<space>",
|
||||
GoInto: "<enter>",
|
||||
Confirm: "<enter>",
|
||||
ConfirmInEditor: "<a-enter>",
|
||||
Remove: "d",
|
||||
New: "n",
|
||||
Edit: "e",
|
||||
OpenFile: "o",
|
||||
OpenRecentRepos: "<c-r>",
|
||||
ScrollUpMain: "<pgup>",
|
||||
ScrollDownMain: "<pgdown>",
|
||||
ScrollUpMainAlt1: "K",
|
||||
ScrollDownMainAlt1: "J",
|
||||
ScrollUpMainAlt2: "<c-u>",
|
||||
ScrollDownMainAlt2: "<c-d>",
|
||||
ExecuteShellCommand: ":",
|
||||
CreateRebaseOptionsMenu: "m",
|
||||
Push: "P",
|
||||
Pull: "p",
|
||||
Refresh: "R",
|
||||
CreatePatchOptionsMenu: "<c-p>",
|
||||
NextTab: "]",
|
||||
PrevTab: "[",
|
||||
NextScreenMode: "+",
|
||||
PrevScreenMode: "_",
|
||||
Undo: "z",
|
||||
Redo: "<c-z>",
|
||||
FilteringMenu: "<c-s>",
|
||||
DiffingMenu: "W",
|
||||
DiffingMenuAlt: "<c-e>",
|
||||
CopyToClipboard: "<c-o>",
|
||||
SubmitEditorText: "<enter>",
|
||||
ExtrasMenu: "@",
|
||||
ToggleWhitespaceInDiffView: "<c-w>",
|
||||
IncreaseContextInDiffView: "}",
|
||||
DecreaseContextInDiffView: "{",
|
||||
IncreaseRenameSimilarityThreshold: ")",
|
||||
DecreaseRenameSimilarityThreshold: "(",
|
||||
OpenDiffTool: "<c-t>",
|
||||
},
|
||||
Status: KeybindingStatusConfig{
|
||||
CheckForUpdate: "u",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -23,7 +25,7 @@ func (self *BackgroundRoutineMgr) PauseBackgroundRefreshes(pause bool) {
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||
userConfig := self.gui.UserConfig
|
||||
userConfig := self.gui.UserConfig()
|
||||
|
||||
if userConfig.Git.AutoFetch {
|
||||
fetchInterval := userConfig.Refresher.FetchInterval
|
||||
@@ -46,19 +48,42 @@ func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||
refreshInterval)
|
||||
}
|
||||
}
|
||||
|
||||
if self.gui.Config.GetDebug() {
|
||||
self.goEvery(time.Second*time.Duration(10), self.gui.stopChan, func() error {
|
||||
formatBytes := func(b uint64) string {
|
||||
const unit = 1000
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := uint64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB",
|
||||
float64(b)/float64(div), "kMGTPE"[exp])
|
||||
}
|
||||
|
||||
m := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&m)
|
||||
self.gui.c.Log.Infof("Heap memory in use: %s", formatBytes(m.HeapAlloc))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
||||
self.gui.waitForIntro.Wait()
|
||||
|
||||
isNew := self.gui.IsNewRepo
|
||||
userConfig := self.gui.UserConfig
|
||||
userConfig := self.gui.UserConfig()
|
||||
if !isNew {
|
||||
time.After(time.Duration(userConfig.Refresher.FetchInterval) * time.Second)
|
||||
}
|
||||
err := self.backgroundFetch()
|
||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||
_ = self.gui.c.Alert(self.gui.c.Tr.NoAutomaticGitFetchTitle, self.gui.c.Tr.NoAutomaticGitFetchBody)
|
||||
self.gui.c.Alert(self.gui.c.Tr.NoAutomaticGitFetchTitle, self.gui.c.Tr.NoAutomaticGitFetchBody)
|
||||
} else {
|
||||
self.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), self.gui.stopChan, func() error {
|
||||
err := self.backgroundFetch()
|
||||
|
||||
@@ -55,11 +55,11 @@ func (gui *Gui) LogCommand(cmdStr string, commandLine bool) {
|
||||
func (gui *Gui) printCommandLogHeader() {
|
||||
introStr := fmt.Sprintf(
|
||||
gui.c.Tr.CommandLogHeader,
|
||||
keybindings.Label(gui.c.UserConfig.Keybinding.Universal.ExtrasMenu),
|
||||
keybindings.Label(gui.c.UserConfig().Keybinding.Universal.ExtrasMenu),
|
||||
)
|
||||
fmt.Fprintln(gui.Views.Extras, style.FgCyan.Sprint(introStr))
|
||||
|
||||
if gui.c.UserConfig.Gui.ShowRandomTip {
|
||||
if gui.c.UserConfig().Gui.ShowRandomTip {
|
||||
fmt.Fprintf(
|
||||
gui.Views.Extras,
|
||||
"%s: %s",
|
||||
@@ -70,7 +70,7 @@ func (gui *Gui) printCommandLogHeader() {
|
||||
}
|
||||
|
||||
func (gui *Gui) getRandomTip() string {
|
||||
config := gui.c.UserConfig.Keybinding
|
||||
config := gui.c.UserConfig().Keybinding
|
||||
|
||||
formattedKey := func(key string) string {
|
||||
return keybindings.Label(key)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
@@ -37,9 +36,9 @@ func NewContextMgr(
|
||||
|
||||
// use when you don't want to return to the original context upon
|
||||
// hitting escape: you want to go that context's parent instead.
|
||||
func (self *ContextMgr) Replace(c types.Context) error {
|
||||
func (self *ContextMgr) Replace(c types.Context) {
|
||||
if !c.IsFocusable() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
self.Lock()
|
||||
@@ -51,14 +50,14 @@ func (self *ContextMgr) Replace(c types.Context) error {
|
||||
self.ContextStack = append(self.ContextStack[0:len(self.ContextStack)-1], c)
|
||||
}
|
||||
|
||||
defer self.Unlock()
|
||||
self.Unlock()
|
||||
|
||||
return self.ActivateContext(c, types.OnFocusOpts{})
|
||||
self.Activate(c, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) {
|
||||
if len(opts) > 1 {
|
||||
return errors.New("cannot pass multiple opts to Push")
|
||||
panic("cannot pass multiple opts to Push")
|
||||
}
|
||||
|
||||
singleOpts := types.OnFocusOpts{}
|
||||
@@ -68,22 +67,18 @@ func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
}
|
||||
|
||||
if !c.IsFocusable() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
contextsToDeactivate, contextToActivate := self.pushToContextStack(c)
|
||||
|
||||
for _, contextToDeactivate := range contextsToDeactivate {
|
||||
if err := self.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
self.deactivate(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()})
|
||||
}
|
||||
|
||||
if contextToActivate == nil {
|
||||
return nil
|
||||
if contextToActivate != nil {
|
||||
self.Activate(contextToActivate, singleOpts)
|
||||
}
|
||||
|
||||
return self.ActivateContext(contextToActivate, singleOpts)
|
||||
}
|
||||
|
||||
// Adjusts the context stack based on the context that's being pushed and
|
||||
@@ -144,13 +139,13 @@ func (self *ContextMgr) pushToContextStack(c types.Context) ([]types.Context, ty
|
||||
return contextsToDeactivate, c
|
||||
}
|
||||
|
||||
func (self *ContextMgr) Pop() error {
|
||||
func (self *ContextMgr) Pop() {
|
||||
self.Lock()
|
||||
|
||||
if len(self.ContextStack) == 1 {
|
||||
// cannot escape from bottommost context
|
||||
self.Unlock()
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
var currentContext types.Context
|
||||
@@ -160,44 +155,12 @@ func (self *ContextMgr) Pop() error {
|
||||
|
||||
self.Unlock()
|
||||
|
||||
if err := self.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
self.deactivate(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()})
|
||||
|
||||
return self.ActivateContext(newContext, types.OnFocusOpts{})
|
||||
self.Activate(newContext, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *ContextMgr) RemoveContexts(contextsToRemove []types.Context) error {
|
||||
self.Lock()
|
||||
|
||||
if len(self.ContextStack) == 1 {
|
||||
self.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
rest := lo.Filter(self.ContextStack, func(context types.Context, _ int) bool {
|
||||
for _, contextToRemove := range contextsToRemove {
|
||||
if context.GetKey() == contextToRemove.GetKey() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
self.ContextStack = rest
|
||||
contextToActivate := rest[len(rest)-1]
|
||||
self.Unlock()
|
||||
|
||||
for _, context := range contextsToRemove {
|
||||
if err := self.deactivateContext(context, types.OnFocusLostOpts{NewContextKey: contextToActivate.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// activate the item at the top of the stack
|
||||
return self.ActivateContext(contextToActivate, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
|
||||
func (self *ContextMgr) deactivate(c types.Context, opts types.OnFocusLostOpts) {
|
||||
view, _ := self.gui.c.GocuiGui().View(c.GetViewName())
|
||||
|
||||
if opts.NewContextKey != context.SEARCH_CONTEXT_KEY {
|
||||
@@ -213,25 +176,25 @@ func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLos
|
||||
view.Visible = false
|
||||
}
|
||||
|
||||
if err := c.HandleFocusLost(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
c.HandleFocusLost(opts)
|
||||
}
|
||||
|
||||
func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
func (self *ContextMgr) Activate(c types.Context, opts types.OnFocusOpts) {
|
||||
viewName := c.GetViewName()
|
||||
v, err := self.gui.c.GocuiGui().View(viewName)
|
||||
if err != nil {
|
||||
return err
|
||||
panic(err)
|
||||
}
|
||||
|
||||
self.gui.helpers.Window.SetWindowContext(c)
|
||||
|
||||
self.gui.helpers.Window.MoveToTopOfWindow(c)
|
||||
oldView := self.gui.c.GocuiGui().CurrentView()
|
||||
if oldView != nil && oldView.Name() != viewName {
|
||||
oldView.HighlightInactive = true
|
||||
}
|
||||
if _, err := self.gui.c.GocuiGui().SetCurrentView(viewName); err != nil {
|
||||
return err
|
||||
panic(err)
|
||||
}
|
||||
|
||||
self.gui.helpers.Search.RenderSearchStatus(c)
|
||||
@@ -245,11 +208,7 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts)
|
||||
|
||||
self.gui.c.GocuiGui().Cursor = v.Editable
|
||||
|
||||
if err := c.HandleFocus(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
c.HandleFocus(opts)
|
||||
}
|
||||
|
||||
func (self *ContextMgr) Current() types.Context {
|
||||
@@ -326,6 +285,18 @@ func (self *ContextMgr) IsCurrent(c types.Context) bool {
|
||||
return self.Current().GetKey() == c.GetKey()
|
||||
}
|
||||
|
||||
func (self *ContextMgr) IsCurrentOrParent(c types.Context) bool {
|
||||
current := self.Current()
|
||||
for current != nil {
|
||||
if current.GetKey() == c.GetKey() {
|
||||
return true
|
||||
}
|
||||
current = current.GetParentContext()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *ContextMgr) AllFilterable() []types.IFilterableContext {
|
||||
var result []types.IFilterableContext
|
||||
|
||||
@@ -387,3 +358,12 @@ func (self *ContextMgr) ContextForKey(key types.ContextKey) types.Context {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextMgr) CurrentPopup() []types.Context {
|
||||
self.RLock()
|
||||
defer self.RUnlock()
|
||||
|
||||
return lo.Filter(self.ContextStack, func(context types.Context, _ int) bool {
|
||||
return context.GetKind() == types.TEMPORARY_POPUP || context.GetKind() == types.PERSISTENT_POPUP
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,36 +16,38 @@ type BaseContext struct {
|
||||
keybindingsFns []types.KeybindingsFn
|
||||
mouseKeybindingsFns []types.MouseKeybindingsFn
|
||||
onClickFn func() error
|
||||
onRenderToMainFn func() error
|
||||
onRenderToMainFn func()
|
||||
onFocusFn onFocusFn
|
||||
onFocusLostFn onFocusLostFn
|
||||
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
needsRerenderOnWidthChange bool
|
||||
highlightOnFocus bool
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
needsRerenderOnWidthChange types.NeedsRerenderOnWidthChangeLevel
|
||||
needsRerenderOnHeightChange bool
|
||||
highlightOnFocus bool
|
||||
|
||||
*ParentContextMgr
|
||||
}
|
||||
|
||||
type (
|
||||
onFocusFn = func(types.OnFocusOpts) error
|
||||
onFocusLostFn = func(types.OnFocusLostOpts) error
|
||||
onFocusFn = func(types.OnFocusOpts)
|
||||
onFocusLostFn = func(types.OnFocusLostOpts)
|
||||
)
|
||||
|
||||
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
|
||||
NeedsRerenderOnWidthChange 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 types.NeedsRerenderOnWidthChangeLevel
|
||||
NeedsRerenderOnHeightChange bool
|
||||
|
||||
OnGetOptionsMap func() map[string]string
|
||||
}
|
||||
@@ -56,18 +58,19 @@ 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,
|
||||
needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange,
|
||||
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,
|
||||
needsRerenderOnHeightChange: opts.NeedsRerenderOnHeightChange,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
viewTrait: viewTrait,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +133,11 @@ func (self *BaseContext) AddMouseKeybindingsFn(fn types.MouseKeybindingsFn) {
|
||||
self.mouseKeybindingsFns = append(self.mouseKeybindingsFns, fn)
|
||||
}
|
||||
|
||||
func (self *BaseContext) ClearAllBindingsFn() {
|
||||
self.keybindingsFns = []types.KeybindingsFn{}
|
||||
self.mouseKeybindingsFns = []types.MouseKeybindingsFn{}
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnClickFn(fn func() error) {
|
||||
if fn != nil {
|
||||
self.onClickFn = fn
|
||||
@@ -140,13 +148,13 @@ func (self *BaseContext) GetOnClick() func() error {
|
||||
return self.onClickFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnRenderToMainFn(fn func() error) {
|
||||
func (self *BaseContext) AddOnRenderToMainFn(fn func()) {
|
||||
if fn != nil {
|
||||
self.onRenderToMainFn = fn
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetOnRenderToMain() func() error {
|
||||
func (self *BaseContext) GetOnRenderToMain() func() {
|
||||
return self.onRenderToMainFn
|
||||
}
|
||||
|
||||
@@ -193,10 +201,18 @@ func (self *BaseContext) HasControlledBounds() bool {
|
||||
return self.hasControlledBounds
|
||||
}
|
||||
|
||||
func (self *BaseContext) NeedsRerenderOnWidthChange() bool {
|
||||
func (self *BaseContext) NeedsRerenderOnWidthChange() types.NeedsRerenderOnWidthChangeLevel {
|
||||
return self.needsRerenderOnWidthChange
|
||||
}
|
||||
|
||||
func (self *BaseContext) NeedsRerenderOnHeightChange() bool {
|
||||
return self.needsRerenderOnHeightChange
|
||||
}
|
||||
|
||||
func (self *BaseContext) Title() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *BaseContext) TotalContentHeight() int {
|
||||
return self.view.ViewLinesHeight()
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
c.Modes().Diffing.Ref,
|
||||
c.Views().Branches.Width(),
|
||||
c.Tr,
|
||||
c.UserConfig,
|
||||
c.UserConfig(),
|
||||
c.Model().Worktrees,
|
||||
)
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_WIDTH_CHANGES,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
@@ -18,15 +21,16 @@ type CommitFilesContext struct {
|
||||
}
|
||||
|
||||
var (
|
||||
_ types.IListContext = (*CommitFilesContext)(nil)
|
||||
_ types.DiffableContext = (*CommitFilesContext)(nil)
|
||||
_ types.IListContext = (*CommitFilesContext)(nil)
|
||||
_ types.DiffableContext = (*CommitFilesContext)(nil)
|
||||
_ types.ISearchableContext = (*CommitFilesContext)(nil)
|
||||
)
|
||||
|
||||
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
viewModel := filetree.NewCommitFileTreeViewModel(
|
||||
func() []*models.CommitFile { return c.Model().CommitFiles },
|
||||
c.Log,
|
||||
c.UserConfig.Gui.ShowFileTree,
|
||||
c.UserConfig().Gui.ShowFileTree,
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
@@ -34,7 +38,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
return [][]string{{style.FgRed.Sprint("(none)")}}
|
||||
}
|
||||
|
||||
showFileIcons := icons.IsIconEnabled() && c.UserConfig.Gui.ShowFileIcons
|
||||
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
||||
lines := presentation.RenderCommitFileTree(viewModel, c.Git().Patch.PatchBuilder, showFileIcons)
|
||||
return lo.Map(lines, func(line string, _ int) []string {
|
||||
return []string{line}
|
||||
@@ -64,10 +68,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
},
|
||||
}
|
||||
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
|
||||
ctx.GetList().SetSelection(selectedLineIdx)
|
||||
return ctx.HandleFocus(types.OnFocusOpts{})
|
||||
}))
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(ctx.OnSearchSelect))
|
||||
|
||||
return ctx
|
||||
}
|
||||
@@ -75,3 +76,26 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
func (self *CommitFilesContext) GetDiffTerminals() []string {
|
||||
return []string{self.GetRef().RefName()}
|
||||
}
|
||||
|
||||
func (self *CommitFilesContext) GetFromAndToForDiff() (string, string) {
|
||||
if refs := self.GetRefRange(); refs != nil {
|
||||
return refs.From.ParentRefName(), refs.To.RefName()
|
||||
}
|
||||
ref := self.GetRef()
|
||||
return ref.ParentRefName(), ref.RefName()
|
||||
}
|
||||
|
||||
func (self *CommitFilesContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CommitFilesContext) ReInit(ref types.Ref, refRange *types.RefRange) {
|
||||
self.SetRef(ref)
|
||||
self.SetRefRange(refRange)
|
||||
if refRange != nil {
|
||||
self.SetTitleRef(fmt.Sprintf("%s-%s", refRange.From.ShortRefName(), refRange.To.ShortRefName()))
|
||||
} else {
|
||||
self.SetTitleRef(ref.Description())
|
||||
}
|
||||
self.GetView().Title = self.Title()
|
||||
}
|
||||
|
||||
@@ -113,17 +113,19 @@ func (self *CommitMessageContext) SetPanelState(
|
||||
|
||||
self.c.Views().CommitDescription.Subtitle = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionSubTitle,
|
||||
map[string]string{
|
||||
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.Universal.TogglePanel),
|
||||
"commitMenuKeybinding": keybindings.Label(self.c.UserConfig.Keybinding.CommitMessage.CommitMenu),
|
||||
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig().Keybinding.Universal.TogglePanel),
|
||||
"commitMenuKeybinding": keybindings.Label(self.c.UserConfig().Keybinding.CommitMessage.CommitMenu),
|
||||
})
|
||||
|
||||
self.c.Views().CommitDescription.Visible = true
|
||||
}
|
||||
|
||||
func (self *CommitMessageContext) RenderCommitLength() {
|
||||
if !self.c.UserConfig.Gui.CommitLength.Show {
|
||||
return
|
||||
if self.c.UserConfig().Gui.CommitLength.Show {
|
||||
self.c.Views().CommitMessage.Subtitle = getBufferLength(self.c.Views().CommitMessage)
|
||||
} else {
|
||||
self.c.Views().CommitMessage.Subtitle = ""
|
||||
}
|
||||
|
||||
self.c.Views().CommitMessage.Subtitle = getBufferLength(self.c.Views().CommitMessage)
|
||||
}
|
||||
|
||||
func getBufferLength(view *gocui.View) string {
|
||||
|
||||
@@ -18,6 +18,9 @@ type ListContextTrait struct {
|
||||
// we should find out exactly which lines are now part of the path and refresh those.
|
||||
// We should also keep track of the previous path and refresh those lines too.
|
||||
refreshViewportOnChange bool
|
||||
// If this is true, we only render the visible lines of the list. Useful for lists that can
|
||||
// get very long, because it can save a lot of memory
|
||||
renderOnlyVisibleLines bool
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) IsListContext() {}
|
||||
@@ -25,7 +28,8 @@ func (self *ListContextTrait) IsListContext() {}
|
||||
func (self *ListContextTrait) FocusLine() {
|
||||
// Doing this at the end of the layout function because we need the view to be
|
||||
// resized before we focus the line, otherwise if we're in accordion mode
|
||||
// the view could be squashed and won't how to adjust the cursor/origin
|
||||
// the view could be squashed and won't how to adjust the cursor/origin.
|
||||
// Also, refreshing the viewport needs to happen after the view has been resized.
|
||||
self.c.AfterLayout(func() error {
|
||||
oldOrigin, _ := self.GetViewTrait().ViewPortYBounds()
|
||||
|
||||
@@ -40,22 +44,18 @@ func (self *ListContextTrait) FocusLine() {
|
||||
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
|
||||
// jump to the top or bottom.
|
||||
newOrigin, _ := self.GetViewTrait().ViewPortYBounds()
|
||||
if self.refreshViewportOnChange && oldOrigin != newOrigin {
|
||||
if self.refreshViewportOnChange {
|
||||
self.refreshViewport()
|
||||
} else if self.renderOnlyVisibleLines {
|
||||
newOrigin, _ := self.GetViewTrait().ViewPortYBounds()
|
||||
if oldOrigin != newOrigin {
|
||||
self.HandleRender()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
self.setFooter()
|
||||
|
||||
if self.refreshViewportOnChange {
|
||||
self.refreshViewport()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) refreshViewport() {
|
||||
@@ -72,38 +72,50 @@ func formatListFooter(selectedLineIdx int, length int) string {
|
||||
return fmt.Sprintf("%d of %d", selectedLineIdx+1, length)
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) HandleFocus(opts types.OnFocusOpts) error {
|
||||
func (self *ListContextTrait) HandleFocus(opts types.OnFocusOpts) {
|
||||
self.FocusLine()
|
||||
|
||||
self.GetViewTrait().SetHighlight(self.list.Len() > 0)
|
||||
|
||||
return self.Context.HandleFocus(opts)
|
||||
self.Context.HandleFocus(opts)
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) HandleFocusLost(opts types.OnFocusLostOpts) error {
|
||||
func (self *ListContextTrait) HandleFocusLost(opts types.OnFocusLostOpts) {
|
||||
self.GetViewTrait().SetOriginX(0)
|
||||
|
||||
if self.refreshViewportOnChange {
|
||||
self.refreshViewport()
|
||||
}
|
||||
|
||||
return self.Context.HandleFocusLost(opts)
|
||||
self.Context.HandleFocusLost(opts)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func (self *ListContextTrait) HandleRender() {
|
||||
self.list.ClampSelection()
|
||||
content := self.renderLines(-1, -1)
|
||||
self.GetViewTrait().SetContent(content)
|
||||
if self.renderOnlyVisibleLines {
|
||||
// Rendering only the visible area can save a lot of cell memory for
|
||||
// those views that support it.
|
||||
totalLength := self.list.Len()
|
||||
if self.getNonModelItems != nil {
|
||||
totalLength += len(self.getNonModelItems())
|
||||
}
|
||||
self.GetViewTrait().SetContentLineCount(totalLength)
|
||||
startIdx, length := self.GetViewTrait().ViewPortYBounds()
|
||||
content := self.renderLines(startIdx, startIdx+length)
|
||||
self.GetViewTrait().SetViewPortContentAndClearEverythingElse(content)
|
||||
} else {
|
||||
content := self.renderLines(-1, -1)
|
||||
self.GetViewTrait().SetContent(content)
|
||||
}
|
||||
self.c.Render()
|
||||
self.setFooter()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
|
||||
self.GetList().SetSelection(selectedLineIdx)
|
||||
return self.HandleFocus(types.OnFocusOpts{})
|
||||
self.GetList().SetSelection(self.ViewIndexToModelIndex(selectedLineIdx))
|
||||
self.HandleFocus(types.OnFocusOpts{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool {
|
||||
@@ -123,3 +135,15 @@ func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool {
|
||||
func (self *ListContextTrait) RangeSelectEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) RenderOnlyVisibleLines() bool {
|
||||
return self.renderOnlyVisibleLines
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) TotalContentHeight() int {
|
||||
result := self.list.Len()
|
||||
if self.getNonModelItems != nil {
|
||||
result += len(self.getNonModelItems())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type ListRenderer struct {
|
||||
numNonModelItems int
|
||||
viewIndicesByModelIndex []int
|
||||
modelIndicesByViewIndex []int
|
||||
columnPositions []int
|
||||
}
|
||||
|
||||
func (self *ListRenderer) GetList() types.IList {
|
||||
@@ -59,6 +60,10 @@ func (self *ListRenderer) ViewIndexToModelIndex(viewIndex int) int {
|
||||
return viewIndex
|
||||
}
|
||||
|
||||
func (self *ListRenderer) ColumnPositions() []int {
|
||||
return self.columnPositions
|
||||
}
|
||||
|
||||
// startIdx and endIdx are view indices, not model indices. If you want to
|
||||
// render the whole list, pass -1 for both.
|
||||
func (self *ListRenderer) renderLines(startIdx int, endIdx int) string {
|
||||
@@ -87,6 +92,7 @@ func (self *ListRenderer) renderLines(startIdx int, endIdx int) string {
|
||||
lines, columnPositions := utils.RenderDisplayStrings(
|
||||
self.getDisplayStrings(startModelIdx, endModelIdx),
|
||||
columnAlignments)
|
||||
self.columnPositions = columnPositions
|
||||
lines = self.insertNonModelItems(nonModelItems, endIdx, startIdx, lines, columnPositions)
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
@@ -115,7 +121,10 @@ func (self *ListRenderer) insertNonModelItems(
|
||||
break
|
||||
}
|
||||
if item.Index+offset >= startIdx {
|
||||
padding := strings.Repeat(" ", columnPositions[item.Column])
|
||||
padding := ""
|
||||
if columnPositions != nil {
|
||||
padding = strings.Repeat(" ", columnPositions[item.Column])
|
||||
}
|
||||
lines = slices.Insert(lines, item.Index+offset-startIdx, padding+item.Content)
|
||||
}
|
||||
offset++
|
||||
|
||||
@@ -2,8 +2,10 @@ package context
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
@@ -18,8 +20,9 @@ type LocalCommitsContext struct {
|
||||
}
|
||||
|
||||
var (
|
||||
_ types.IListContext = (*LocalCommitsContext)(nil)
|
||||
_ types.DiffableContext = (*LocalCommitsContext)(nil)
|
||||
_ types.IListContext = (*LocalCommitsContext)(nil)
|
||||
_ types.DiffableContext = (*LocalCommitsContext)(nil)
|
||||
_ types.ISearchableContext = (*LocalCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
@@ -31,7 +34,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
getDisplayStrings := func(startIdx int, endIdx int) [][]string {
|
||||
selectedCommitHash := ""
|
||||
|
||||
if c.CurrentContext().GetKey() == LOCAL_COMMITS_CONTEXT_KEY {
|
||||
if c.Context().Current().GetKey() == LOCAL_COMMITS_CONTEXT_KEY {
|
||||
selectedCommit := viewModel.GetSelected()
|
||||
if selectedCommit != nil {
|
||||
selectedCommitHash = selectedCommit.Hash
|
||||
@@ -51,10 +54,10 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
c.Modes().CherryPicking.SelectedHashSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
c.Modes().MarkedBaseCommit.GetHash(),
|
||||
c.UserConfig.Gui.TimeFormat,
|
||||
c.UserConfig.Gui.ShortTimeFormat,
|
||||
c.UserConfig().Gui.TimeFormat,
|
||||
c.UserConfig().Gui.ShortTimeFormat,
|
||||
time.Now(),
|
||||
c.UserConfig.Git.ParseEmoji,
|
||||
c.UserConfig().Git.ParseEmoji,
|
||||
selectedCommitHash,
|
||||
startIdx,
|
||||
endIdx,
|
||||
@@ -69,12 +72,13 @@ 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,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
View: c.Views().Commits,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
|
||||
NeedsRerenderOnHeightChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
@@ -82,13 +86,11 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
},
|
||||
c: c,
|
||||
refreshViewportOnChange: true,
|
||||
renderOnlyVisibleLines: true,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
|
||||
ctx.GetList().SetSelection(selectedLineIdx)
|
||||
return ctx.HandleFocus(types.OnFocusOpts{})
|
||||
}))
|
||||
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(ctx.OnSearchSelect))
|
||||
|
||||
return ctx
|
||||
}
|
||||
@@ -108,7 +110,7 @@ func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *ContextCommon
|
||||
self := &LocalCommitsViewModel{
|
||||
ListViewModel: NewListViewModel(getModel),
|
||||
limitCommits: true,
|
||||
showWholeGitGraph: c.UserConfig.Git.Log.ShowWholeGraph,
|
||||
showWholeGitGraph: c.UserConfig().Git.Log.ShowWholeGraph,
|
||||
}
|
||||
|
||||
return self
|
||||
@@ -126,6 +128,19 @@ func (self *LocalCommitsContext) GetSelectedRef() types.Ref {
|
||||
return commit
|
||||
}
|
||||
|
||||
func (self *LocalCommitsContext) GetSelectedRefRangeForDiffFiles() *types.RefRange {
|
||||
commits, startIdx, endIdx := self.GetSelectedItems()
|
||||
if commits == nil || startIdx == endIdx {
|
||||
return nil
|
||||
}
|
||||
from := commits[len(commits)-1]
|
||||
to := commits[0]
|
||||
if from.IsTODO() || to.IsTODO() {
|
||||
return nil
|
||||
}
|
||||
return &types.RefRange{From: from, To: to}
|
||||
}
|
||||
|
||||
// Returns the commit hash of the selected commit, or an empty string if no
|
||||
// commit is selected
|
||||
func (self *LocalCommitsContext) GetSelectedCommitHash() string {
|
||||
@@ -155,6 +170,10 @@ func (self *LocalCommitsContext) GetDiffTerminals() []string {
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *LocalCommitsContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||
return searchModelCommits(caseSensitive, self.GetCommits(), self.ColumnPositions(), searchStr)
|
||||
}
|
||||
|
||||
func (self *LocalCommitsViewModel) SetLimitCommits(value bool) {
|
||||
self.limitCommits = value
|
||||
}
|
||||
@@ -194,3 +213,27 @@ func shouldShowGraph(c *ContextCommon) bool {
|
||||
log.Fatalf("Unknown value for git.log.showGraph: %s. Expected one of: 'always', 'never', 'when-maximised'", value)
|
||||
return false
|
||||
}
|
||||
|
||||
func searchModelCommits(caseSensitive bool, commits []*models.Commit, columnPositions []int, searchStr string) []gocui.SearchPosition {
|
||||
if columnPositions == nil {
|
||||
// This should never happen. We are being called at a time where our
|
||||
// entire view content is scrolled out of view, so that we didn't draw
|
||||
// anything the last time we rendered. If we run into a scenario where
|
||||
// this happens, we should fix it, but until we found them all, at least
|
||||
// make sure we don't crash.
|
||||
return []gocui.SearchPosition{}
|
||||
}
|
||||
|
||||
normalize := lo.Ternary(caseSensitive, func(s string) string { return s }, strings.ToLower)
|
||||
return lo.FilterMap(commits, func(commit *models.Commit, idx int) (gocui.SearchPosition, bool) {
|
||||
// The XStart and XEnd values are only used if the search string can't
|
||||
// be found in the view. This can really only happen if the user is
|
||||
// searching for a commit hash that is longer than the truncated hash
|
||||
// that we render. So we just set the XStart and XEnd values to the
|
||||
// start and end of the commit hash column, which is the second one.
|
||||
result := gocui.SearchPosition{XStart: columnPositions[1], XEnd: columnPositions[2] - 1, Y: idx}
|
||||
return result, strings.Contains(normalize(commit.Hash), searchStr) ||
|
||||
strings.Contains(normalize(commit.Name), searchStr) ||
|
||||
strings.Contains(normalize(commit.ExtraInfo), searchStr) // allow searching for tags
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ func NewMenuContext(
|
||||
type MenuViewModel struct {
|
||||
c *ContextCommon
|
||||
menuItems []*types.MenuItem
|
||||
prompt string
|
||||
promptLines []string
|
||||
columnAlignment []utils.Alignment
|
||||
*FilteredListViewModel[*types.MenuItem]
|
||||
}
|
||||
@@ -73,6 +75,23 @@ func (self *MenuViewModel) SetMenuItems(items []*types.MenuItem, columnAlignment
|
||||
self.columnAlignment = columnAlignment
|
||||
}
|
||||
|
||||
func (self *MenuViewModel) GetPrompt() string {
|
||||
return self.prompt
|
||||
}
|
||||
|
||||
func (self *MenuViewModel) SetPrompt(prompt string) {
|
||||
self.prompt = prompt
|
||||
self.promptLines = nil
|
||||
}
|
||||
|
||||
func (self *MenuViewModel) GetPromptLines() []string {
|
||||
return self.promptLines
|
||||
}
|
||||
|
||||
func (self *MenuViewModel) SetPromptLines(promptLines []string) {
|
||||
self.promptLines = promptLines
|
||||
}
|
||||
|
||||
// TODO: move into presentation package
|
||||
func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
|
||||
menuItems := self.FilteredListViewModel.GetItems()
|
||||
@@ -88,20 +107,42 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
|
||||
keyLabel = style.FgCyan.Sprint(keybindings.LabelFromKey(item.Key))
|
||||
}
|
||||
|
||||
displayStrings = utils.Prepend(displayStrings, keyLabel)
|
||||
checkMark := ""
|
||||
switch item.Widget {
|
||||
case types.MenuWidgetNone:
|
||||
// do nothing
|
||||
case types.MenuWidgetRadioButtonSelected:
|
||||
checkMark = "(•)"
|
||||
case types.MenuWidgetRadioButtonUnselected:
|
||||
checkMark = "( )"
|
||||
case types.MenuWidgetCheckboxSelected:
|
||||
checkMark = "[✓]"
|
||||
case types.MenuWidgetCheckboxUnselected:
|
||||
checkMark = "[ ]"
|
||||
}
|
||||
|
||||
displayStrings = utils.Prepend(displayStrings, keyLabel, checkMark)
|
||||
return displayStrings
|
||||
})
|
||||
}
|
||||
|
||||
func (self *MenuViewModel) GetNonModelItems() []*NonModelItem {
|
||||
result := []*NonModelItem{}
|
||||
result = append(result, lo.Map(self.promptLines, func(line string, _ int) *NonModelItem {
|
||||
return &NonModelItem{
|
||||
Index: 0,
|
||||
Column: 0,
|
||||
Content: line,
|
||||
}
|
||||
})...)
|
||||
|
||||
// Don't display section headers when we are filtering, and the filter mode
|
||||
// is fuzzy. The reason is that filtering changes the order of the items
|
||||
// (they are sorted by best match), so all the sections would be messed up.
|
||||
if self.FilteredListViewModel.IsFiltering() && self.c.UserConfig.Gui.UseFuzzySearch() {
|
||||
return []*NonModelItem{}
|
||||
if self.FilteredListViewModel.IsFiltering() && self.c.UserConfig().Gui.UseFuzzySearch() {
|
||||
return result
|
||||
}
|
||||
|
||||
result := []*NonModelItem{}
|
||||
menuItems := self.FilteredListViewModel.GetItems()
|
||||
var prevSection *types.MenuSection = nil
|
||||
for i, menuItem := range menuItems {
|
||||
@@ -156,9 +197,7 @@ func (self *MenuContext) OnMenuPress(selectedItem *types.MenuItem) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := self.c.PopContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
self.c.Context().Pop()
|
||||
|
||||
if selectedItem == nil {
|
||||
return nil
|
||||
|
||||
@@ -68,13 +68,11 @@ func (self *MergeConflictsContext) IsUserScrolling() bool {
|
||||
return self.viewModel.userVerticalScrolling
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) RenderAndFocus() error {
|
||||
func (self *MergeConflictsContext) RenderAndFocus() {
|
||||
self.setContent()
|
||||
self.FocusSelection()
|
||||
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) Render() error {
|
||||
@@ -99,7 +97,7 @@ func (self *MergeConflictsContext) setContent() {
|
||||
|
||||
func (self *MergeConflictsContext) FocusSelection() {
|
||||
if !self.IsUserScrolling() {
|
||||
_ = self.GetView().SetOriginY(self.GetOriginY())
|
||||
self.GetView().SetOriginY(self.GetOriginY())
|
||||
}
|
||||
|
||||
self.SetSelectedLineRange()
|
||||
|
||||
@@ -4,17 +4,14 @@ import "github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
|
||||
type ParentContextMgr struct {
|
||||
ParentContext types.Context
|
||||
// we can't know on the calling end whether a Context is actually a nil value without reflection, so we're storing this flag here to tell us. There has got to be a better way around this
|
||||
hasParent bool
|
||||
}
|
||||
|
||||
var _ types.ParentContexter = (*ParentContextMgr)(nil)
|
||||
|
||||
func (self *ParentContextMgr) SetParentContext(context types.Context) {
|
||||
self.ParentContext = context
|
||||
self.hasParent = true
|
||||
}
|
||||
|
||||
func (self *ParentContextMgr) GetParentContext() (types.Context, bool) {
|
||||
return self.ParentContext, self.hasParent
|
||||
func (self *ParentContextMgr) GetParentContext() types.Context {
|
||||
return self.ParentContext
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ type PatchExplorerContext struct {
|
||||
mutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
var _ types.IPatchExplorerContext = (*PatchExplorerContext)(nil)
|
||||
var (
|
||||
_ types.IPatchExplorerContext = (*PatchExplorerContext)(nil)
|
||||
_ types.ISearchableContext = (*PatchExplorerContext)(nil)
|
||||
)
|
||||
|
||||
func NewPatchExplorerContext(
|
||||
view *gocui.View,
|
||||
@@ -50,7 +53,8 @@ func NewPatchExplorerContext(
|
||||
func(selectedLineIdx int) error {
|
||||
ctx.GetMutex().Lock()
|
||||
defer ctx.GetMutex().Unlock()
|
||||
return ctx.NavigateTo(ctx.c.IsCurrentContext(ctx), selectedLineIdx)
|
||||
ctx.NavigateTo(ctx.c.Context().IsCurrent(ctx), selectedLineIdx)
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -75,28 +79,22 @@ func (self *PatchExplorerContext) GetIncludedLineIndices() []int {
|
||||
return self.getIncludedLineIndices()
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) RenderAndFocus(isFocused bool) error {
|
||||
func (self *PatchExplorerContext) RenderAndFocus(isFocused bool) {
|
||||
self.setContent(isFocused)
|
||||
|
||||
self.FocusSelection()
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) Render(isFocused bool) error {
|
||||
func (self *PatchExplorerContext) Render(isFocused bool) {
|
||||
self.setContent(isFocused)
|
||||
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) Focus() error {
|
||||
func (self *PatchExplorerContext) Focus() {
|
||||
self.FocusSelection()
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) setContent(isFocused bool) {
|
||||
@@ -113,7 +111,7 @@ func (self *PatchExplorerContext) FocusSelection() {
|
||||
|
||||
newOriginY := state.CalculateOrigin(origin, bufferHeight, numLines)
|
||||
|
||||
_ = view.SetOriginY(newOriginY)
|
||||
view.SetOriginY(newOriginY)
|
||||
|
||||
startIdx, endIdx := state.SelectedRange()
|
||||
// As far as the view is concerned, we are always selecting a range
|
||||
@@ -129,13 +127,17 @@ func (self *PatchExplorerContext) GetContentToRender(isFocused bool) string {
|
||||
return self.GetState().RenderForLineIndices(isFocused, self.GetIncludedLineIndices())
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) NavigateTo(isFocused bool, selectedLineIdx int) error {
|
||||
func (self *PatchExplorerContext) NavigateTo(isFocused bool, selectedLineIdx int) {
|
||||
self.GetState().SetLineSelectMode()
|
||||
self.GetState().SelectLine(selectedLineIdx)
|
||||
|
||||
return self.RenderAndFocus(isFocused)
|
||||
self.RenderAndFocus(isFocused)
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetMutex() *deadlock.Mutex {
|
||||
return self.mutex
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||
c.Modes().CherryPicking.SelectedHashSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
time.Now(),
|
||||
c.UserConfig.Gui.TimeFormat,
|
||||
c.UserConfig.Gui.ShortTimeFormat,
|
||||
c.UserConfig.Git.ParseEmoji,
|
||||
c.UserConfig().Gui.TimeFormat,
|
||||
c.UserConfig().Gui.ShortTimeFormat,
|
||||
c.UserConfig().Git.ParseEmoji,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
@@ -71,6 +71,11 @@ func (self *ReflogCommitsContext) GetSelectedRef() types.Ref {
|
||||
return commit
|
||||
}
|
||||
|
||||
func (self *ReflogCommitsContext) GetSelectedRefRangeForDiffFiles() *types.RefRange {
|
||||
// It doesn't make much sense to show a range diff between two reflog entries.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ReflogCommitsContext) GetCommits() []*models.Commit {
|
||||
return self.getModel()
|
||||
}
|
||||
|
||||
@@ -37,13 +37,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,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
View: c.Views().RemoteBranches,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
NeedsRerenderOnHeightChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -26,7 +26,7 @@ func NewRemotesContext(c *ContextCommon) *RemotesContext {
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
return presentation.GetRemoteListDisplayStrings(
|
||||
viewModel.GetItems(), c.Modes().Diffing.Ref, c.State().GetItemOperation, c.Tr, c.UserConfig)
|
||||
viewModel.GetItems(), c.Modes().Diffing.Ref, c.State().GetItemOperation, c.Tr, c.UserConfig())
|
||||
}
|
||||
|
||||
return &RemotesContext{
|
||||
|
||||
@@ -51,7 +51,7 @@ func (self *SearchTrait) onSelectItemWrapper(innerFunc func(int) error) func(int
|
||||
}
|
||||
|
||||
func (self *SearchTrait) RenderSearchStatus(index int, total int) {
|
||||
keybindingConfig := self.c.UserConfig.Keybinding
|
||||
keybindingConfig := self.c.UserConfig().Keybinding
|
||||
|
||||
if total == 0 {
|
||||
self.c.SetViewContent(
|
||||
|
||||
@@ -31,41 +31,33 @@ func NewDisplayContext(key types.ContextKey, view *gocui.View, windowName string
|
||||
)
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) error {
|
||||
func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) {
|
||||
if self.highlightOnFocus {
|
||||
self.GetViewTrait().SetHighlight(true)
|
||||
}
|
||||
|
||||
if self.onFocusFn != nil {
|
||||
if err := self.onFocusFn(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
self.onFocusFn(opts)
|
||||
}
|
||||
|
||||
if self.onRenderToMainFn != nil {
|
||||
if err := self.onRenderToMainFn(); err != nil {
|
||||
return err
|
||||
}
|
||||
self.onRenderToMainFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleFocusLost(opts types.OnFocusLostOpts) error {
|
||||
func (self *SimpleContext) HandleFocusLost(opts types.OnFocusLostOpts) {
|
||||
self.GetViewTrait().SetHighlight(false)
|
||||
self.view.SetOriginX(0)
|
||||
if self.onFocusLostFn != nil {
|
||||
return self.onFocusLostFn(opts)
|
||||
self.onFocusLostFn(opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleRender() error {
|
||||
return nil
|
||||
func (self *SimpleContext) HandleRender() {
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleRenderToMain() error {
|
||||
func (self *SimpleContext) HandleRenderToMain() {
|
||||
if self.onRenderToMainFn != nil {
|
||||
return self.onRenderToMainFn()
|
||||
self.onRenderToMainFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ func (self *StashContext) GetSelectedRef() types.Ref {
|
||||
return stash
|
||||
}
|
||||
|
||||
func (self *StashContext) GetSelectedRefRangeForDiffFiles() *types.RefRange {
|
||||
// It doesn't make much sense to show a range diff between two stash entries.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StashContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user