mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-28 10:16:22 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a71b5f837 | ||
|
|
b62546c391 | ||
|
|
de8dc935a3 | ||
|
|
dd765801db | ||
|
|
111407d9a6 | ||
|
|
9f03f9e95d | ||
|
|
181b00b758 | ||
|
|
e1e4e1be1f | ||
|
|
fdeaf9cea0 | ||
|
|
b0a766cc95 | ||
|
|
f858460ab9 | ||
|
|
b2cbd93619 | ||
|
|
8da43af924 | ||
|
|
7edf629eeb | ||
|
|
1e8b8471ca | ||
|
|
095eb130e9 | ||
|
|
f08b3e9e1d | ||
|
|
8f3e59b78e | ||
|
|
eaf3bf0971 | ||
|
|
4883c867bb | ||
|
|
a58770ee1b | ||
|
|
7655f6864e | ||
|
|
286e5f4849 | ||
|
|
052974be65 | ||
|
|
85523402d6 | ||
|
|
f473d23d65 | ||
|
|
59a937ee7a | ||
|
|
53f8249ee1 | ||
|
|
f71274b601 | ||
|
|
f2fd435c05 | ||
|
|
4e361e1a87 | ||
|
|
696e78fcc8 | ||
|
|
b71aa5e23b | ||
|
|
72cf3efb1d | ||
|
|
ae610dcbb7 | ||
|
|
d11e11d179 | ||
|
|
825f5c0a91 | ||
|
|
26e3a93fc3 | ||
|
|
1ceb5a6b37 | ||
|
|
65b731f484 | ||
|
|
c6a7722066 | ||
|
|
1ab70ec645 | ||
|
|
e181de1180 | ||
|
|
c712b1d0fe | ||
|
|
c4e5995cb9 | ||
|
|
be3683ccc8 | ||
|
|
7e7309f97e | ||
|
|
d2b6f93858 | ||
|
|
a91fe517d3 | ||
|
|
8a328a553a | ||
|
|
3d56357294 | ||
|
|
9b2a0c4538 | ||
|
|
a04ad24a60 | ||
|
|
0d633896ae | ||
|
|
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 | ||
|
|
c08a5fe4e7 | ||
|
|
92dd80c3e3 | ||
|
|
899e25b208 | ||
|
|
906d21f3ce | ||
|
|
7780f1264a | ||
|
|
f3718ddfb0 | ||
|
|
f9ba2dac9d | ||
|
|
6cb2ac6fcc | ||
|
|
6b9cf72e79 | ||
|
|
92f13fc56e | ||
|
|
06496ccd17 | ||
|
|
03a075c223 | ||
|
|
eef70dbdf6 | ||
|
|
f5329440fc | ||
|
|
4f6d3fb592 | ||
|
|
187a2f0cc2 | ||
|
|
f6457c4def | ||
|
|
66a04e4273 | ||
|
|
ffedd84e92 | ||
|
|
ab0b0da850 | ||
|
|
b85687797d | ||
|
|
a8921a13cb | ||
|
|
837f7456ab | ||
|
|
ddf5e24499 | ||
|
|
ca6d88080c | ||
|
|
1b7ded6df3 | ||
|
|
36a4696573 | ||
|
|
343db7b3f1 | ||
|
|
4ac77f4575 | ||
|
|
373b1970ca | ||
|
|
5b613f5bc7 | ||
|
|
8c385731f7 | ||
|
|
e79b4259e4 | ||
|
|
f4d922bc80 | ||
|
|
b2011dca35 | ||
|
|
19d0048cc4 | ||
|
|
7c51ec21bf | ||
|
|
39ea5d9ab1 | ||
|
|
205357a44f | ||
|
|
c9c556beba | ||
|
|
b82c72b63d | ||
|
|
dbdabb34f3 | ||
|
|
c1a65546ad | ||
|
|
2c9bca8b57 | ||
|
|
880528b2e4 | ||
|
|
e1b4d625c7 | ||
|
|
a957c5542f | ||
|
|
f085d10c46 | ||
|
|
1269938ee6 | ||
|
|
557dfc5a6d | ||
|
|
c5baa5da3a | ||
|
|
3284d69886 | ||
|
|
116c18e957 | ||
|
|
e93617b1de | ||
|
|
aac2535104 | ||
|
|
993d66a8ff | ||
|
|
ab797fe986 | ||
|
|
4bfda1a78c | ||
|
|
91cb33db85 | ||
|
|
7492521829 | ||
|
|
35af886f55 | ||
|
|
f45ecbc19a | ||
|
|
51eb6d7c7d | ||
|
|
38ec4e646f | ||
|
|
ef1daf314a | ||
|
|
ab643749e8 | ||
|
|
2d0c96466a | ||
|
|
f6ace8380f | ||
|
|
b4aa68d0b3 | ||
|
|
4a97fb80f3 | ||
|
|
8f1a04d095 | ||
|
|
e5f4de8d34 | ||
|
|
ce3fe37787 | ||
|
|
6cf6941a77 | ||
|
|
0fbed71f15 | ||
|
|
22a38c9f50 | ||
|
|
6343fb57d6 | ||
|
|
5af0ea85fc | ||
|
|
a4e9181a6b | ||
|
|
f5d57f7cc7 | ||
|
|
fc7b10f557 | ||
|
|
b75c177c31 | ||
|
|
9b152d7619 | ||
|
|
b98ae1c773 | ||
|
|
7d787afb2c | ||
|
|
af842e40d4 |
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
*.go text
|
||||
*.md text eol=lf
|
||||
*.json text eol=lf
|
||||
@@ -6,7 +6,8 @@
|
||||
* [ ] 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))
|
||||
* [ ] Docs (specifically `docs/Config.md`) have been updated if necessary
|
||||
* [ ] 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
|
||||
|
||||
<!--
|
||||
|
||||
@@ -21,6 +21,9 @@ changelog:
|
||||
- title: I18n 🌎
|
||||
labels:
|
||||
- i18n
|
||||
- title: Performance Improvements 📊
|
||||
labels:
|
||||
- performance
|
||||
- title: Other Changes
|
||||
labels:
|
||||
- "*"
|
||||
|
||||
@@ -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
|
||||
|
||||
+39
-20
@@ -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
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
mkdir -p /tmp/code_coverage
|
||||
go test ./... -short -cover -args "-test.gocoverdir=/tmp/code_coverage"
|
||||
- name: Upload code coverage artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-unit-${{ matrix.os }}-${{ github.run_id }}
|
||||
path: /tmp/code_coverage
|
||||
@@ -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
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
mkdir -p /tmp/code_coverage
|
||||
./scripts/run_integration_tests.sh
|
||||
- name: Upload code coverage artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-integration-${{ matrix.git-version }}-${{ github.run_id }}
|
||||
path: /tmp/code_coverage
|
||||
@@ -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: latest
|
||||
version: v1.60
|
||||
- name: errors
|
||||
run: golangci-lint run
|
||||
if: ${{ failure() }}
|
||||
@@ -184,7 +184,7 @@ jobs:
|
||||
with:
|
||||
mode: exactly
|
||||
count: 1
|
||||
labels: "ignore-for-release, feature, enhancement, bug, maintenance, docs, i18n"
|
||||
labels: "ignore-for-release, feature, enhancement, bug, maintenance, docs, i18n, performance"
|
||||
upload-coverage:
|
||||
# List all jobs that produce coverage files
|
||||
needs: [unit-tests, integration-tests]
|
||||
@@ -192,15 +192,15 @@ 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
|
||||
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/code_coverage
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
# Hidden
|
||||
.*
|
||||
!.codespellrc
|
||||
|
||||
# Notes
|
||||
*.notes
|
||||
@@ -19,6 +20,7 @@ lazygit.exe
|
||||
|
||||
# Exceptions
|
||||
!.gitignore
|
||||
!.gitattributes
|
||||
!.goreleaser.yml
|
||||
!.golangci.yml
|
||||
!.circleci/
|
||||
|
||||
+6
-4
@@ -1,23 +1,25 @@
|
||||
linters:
|
||||
disable:
|
||||
- structcheck # gives false positives
|
||||
enable:
|
||||
- gofumpt
|
||||
- thelper
|
||||
- goimports
|
||||
- tparallel
|
||||
- wastedassign
|
||||
- exportloopref
|
||||
- unparam
|
||||
- prealloc
|
||||
- unconvert
|
||||
- exhaustive
|
||||
- makezero
|
||||
- nakedret
|
||||
# - goconst # TODO: enable and fix issues
|
||||
- copyloopvar
|
||||
fast: false
|
||||
|
||||
linters-settings:
|
||||
copyloopvar:
|
||||
# Check all assigning the loop variable to another variable.
|
||||
# Default: false
|
||||
# If true, an assignment like `a := x` will be detected as an error.
|
||||
check-alias: true
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
staticcheck:
|
||||
|
||||
+1
-25
@@ -154,31 +154,7 @@ If you want to trigger a debug session from VSCode, you can use the following sn
|
||||
|
||||
## Profiling
|
||||
|
||||
If you want to investigate what's contributing to CPU usage you can add the following to the top of the `main()` function in `main.go`
|
||||
|
||||
```go
|
||||
import "runtime/pprof"
|
||||
|
||||
func main() {
|
||||
f, err := os.Create("cpu.prof")
|
||||
if err != nil {
|
||||
log.Fatal("could not create CPU profile: ", err)
|
||||
}
|
||||
defer f.Close()
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
log.Fatal("could not start CPU profile: ", err)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
...
|
||||
```
|
||||
|
||||
Then run lazygit, and afterwards, from your terminal, run:
|
||||
|
||||
```sh
|
||||
go tool pprof --web cpu.prof
|
||||
```
|
||||
|
||||
That should open an application which allows you to view the breakdown of CPU usage.
|
||||
If you want to investigate what's contributing to CPU or memory usage, see [this separate document](docs/dev/Profiling.md).
|
||||
|
||||
## Testing
|
||||
|
||||
|
||||
+1
-1
@@ -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 ./
|
||||
|
||||
@@ -38,6 +38,10 @@ generate:
|
||||
format:
|
||||
gofumpt -l -w .
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
# For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md.
|
||||
.PHONY: integration-test-tui
|
||||
integration-test-tui:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+564
-207
@@ -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
|
||||
@@ -29,268 +31,602 @@ to the top of your config file or via [Visual Studio Code settings.json config][
|
||||
|
||||
## Default
|
||||
|
||||
<!-- START CONFIG YAML: AUTOMATICALLY GENERATED with `go generate ./..., DO NOT UPDATE MANUALLY -->
|
||||
```yaml
|
||||
# Config relating to the Lazygit UI
|
||||
gui:
|
||||
# stuff relating to the UI
|
||||
windowSize: 'normal' # one of 'normal' | 'half' | 'full' default is 'normal'
|
||||
scrollHeight: 2 # how many lines you scroll by
|
||||
scrollPastBottom: true # enable scrolling past the bottom
|
||||
scrollOffMargin: 2 # how many lines to keep before/after the cursor when it reaches the top/bottom of the view; see 'Scroll-off Margin' section below
|
||||
scrollOffBehavior: 'margin' # one of 'margin' | 'jump'; see 'Scroll-off Margin' section below
|
||||
sidePanelWidth: 0.3333 # number from 0 to 1
|
||||
# The number of lines you scroll by when scrolling the main window
|
||||
scrollHeight: 2
|
||||
|
||||
# If true, allow scrolling past the bottom of the content in the main window
|
||||
scrollPastBottom: true
|
||||
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#scroll-off-margin
|
||||
scrollOffMargin: 2
|
||||
|
||||
# One of: 'margin' (default) | 'jump'
|
||||
scrollOffBehavior: margin
|
||||
|
||||
# If true, capture mouse events.
|
||||
# When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.
|
||||
mouseEvents: true
|
||||
|
||||
# If true, do not show a warning when discarding changes in the staging view.
|
||||
skipDiscardChangeWarning: false
|
||||
|
||||
# If true, do not show warning when applying/popping the stash
|
||||
skipStashWarning: false
|
||||
|
||||
# If true, do not show a warning when attempting to commit without any staged files; instead stage all unstaged files.
|
||||
skipNoStagedFilesWarning: false
|
||||
|
||||
# If true, do not show a warning when rewording a commit via an external editor
|
||||
skipRewordInEditorWarning: false
|
||||
|
||||
# Fraction of the total screen width to use for the left side section. You may want to pick a small number (e.g. 0.2) if you're using a narrow screen, so that you can see more of the main section.
|
||||
# Number from 0 to 1.0.
|
||||
sidePanelWidth: 0.3333
|
||||
|
||||
# If true, increase the height of the focused side window; creating an accordion effect.
|
||||
expandFocusedSidePanel: false
|
||||
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
|
||||
enlargedSideViewLocation: 'left' # one of 'left' | 'top'
|
||||
language: 'auto' # one of 'auto' | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
timeFormat: '02 Jan 06' # https://pkg.go.dev/time#Time.Format
|
||||
shortTimeFormat: '3:04PM'
|
||||
|
||||
# The weight of the expanded side panel, relative to the other panels. 2 means
|
||||
# twice as tall as the other panels. Only relevant if `expandFocusedSidePanel` is true.
|
||||
expandedSidePanelWeight: 2
|
||||
|
||||
# Sometimes the main window is split in two (e.g. when the selected file has both staged and unstaged changes). This setting controls how the two sections are split.
|
||||
# Options are:
|
||||
# - 'horizontal': split the window horizontally
|
||||
# - 'vertical': split the window vertically
|
||||
# - 'flexible': (default) split the window horizontally if the window is wide enough, otherwise split vertically
|
||||
mainPanelSplitMode: flexible
|
||||
|
||||
# How the window is split when in half screen mode (i.e. after hitting '+' once).
|
||||
# Possible values:
|
||||
# - 'left': split the window horizontally (side panel on the left, main view on the right)
|
||||
# - 'top': split the window vertically (side panel on top, main view below)
|
||||
enlargedSideViewLocation: left
|
||||
|
||||
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
language: auto
|
||||
|
||||
# Format used when displaying time e.g. commit time.
|
||||
# Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
|
||||
timeFormat: 02 Jan 06
|
||||
|
||||
# Format used when displaying time if the time is less than 24 hours ago.
|
||||
# Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
|
||||
shortTimeFormat: 3:04PM
|
||||
|
||||
# Config relating to colors and styles.
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes
|
||||
theme:
|
||||
# Border color of focused window
|
||||
activeBorderColor:
|
||||
- green
|
||||
- bold
|
||||
|
||||
# Border color of non-focused windows
|
||||
inactiveBorderColor:
|
||||
- white
|
||||
- default
|
||||
|
||||
# Border color of focused window when searching in that window
|
||||
searchingActiveBorderColor:
|
||||
- cyan
|
||||
- bold
|
||||
|
||||
# Color of keybindings help text in the bottom line
|
||||
optionsTextColor:
|
||||
- blue
|
||||
|
||||
# Background color of selected line.
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
|
||||
selectedLineBgColor:
|
||||
- blue # set to `default` to have no background colour
|
||||
cherryPickedCommitBgColor:
|
||||
- cyan
|
||||
- blue
|
||||
|
||||
# Background color of selected line when view doesn't have focus.
|
||||
inactiveViewSelectedLineBgColor:
|
||||
- bold
|
||||
|
||||
# Foreground color of copied commit
|
||||
cherryPickedCommitFgColor:
|
||||
- blue
|
||||
|
||||
# Background color of copied commit
|
||||
cherryPickedCommitBgColor:
|
||||
- cyan
|
||||
|
||||
# Foreground color of marked base commit (for rebase)
|
||||
markedBaseCommitFgColor:
|
||||
- blue
|
||||
|
||||
# Background color of marked base commit (for rebase)
|
||||
markedBaseCommitBgColor:
|
||||
- yellow
|
||||
|
||||
# Color for file with unstaged changes
|
||||
unstagedChangesColor:
|
||||
- red
|
||||
|
||||
# Default text color
|
||||
defaultFgColor:
|
||||
- default
|
||||
|
||||
# Config relating to the commit length indicator
|
||||
commitLength:
|
||||
# If true, show an indicator of commit message length
|
||||
show: true
|
||||
mouseEvents: true
|
||||
skipDiscardChangeWarning: false
|
||||
skipStashWarning: false
|
||||
showFileTree: true # for rendering changes files in a tree format
|
||||
showListFooter: true # for seeing the '5 of 20' message in list panels
|
||||
|
||||
# If true, show the '5 of 20' footer at the bottom of list views
|
||||
showListFooter: true
|
||||
|
||||
# If true, display the files in the file views as a tree. If false, display the files as a flat list.
|
||||
# This can be toggled from within Lazygit with the '~' key, but that will not change the default.
|
||||
showFileTree: true
|
||||
|
||||
# If true, show a random tip in the command log when Lazygit starts
|
||||
showRandomTip: true
|
||||
showBranchCommitHash: false # show commit hashes alongside branch names
|
||||
showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
|
||||
showPanelJumps: true # for showing the jump-to-panel keybindings as panel subtitles
|
||||
|
||||
# If true, show the command log
|
||||
showCommandLog: true
|
||||
showIcons: false # deprecated: use nerdFontsVersion instead
|
||||
nerdFontsVersion: "" # nerd fonts version to use ("2" or "3"); empty means don't show nerd font icons
|
||||
showFileIcons: true # for hiding file icons in the file views
|
||||
commitHashLength: 8 # length of commit hash in commits view. 0 shows '*' if NF icons aren't enabled
|
||||
|
||||
# If true, show the bottom line that contains keybinding info and useful buttons. If false, this line will be hidden except to display a loader for an in-progress action.
|
||||
showBottomLine: true
|
||||
|
||||
# If true, show jump-to-window keybindings in window titles.
|
||||
showPanelJumps: true
|
||||
|
||||
# Deprecated: use nerdFontsVersion instead
|
||||
showIcons: false
|
||||
|
||||
# Nerd fonts version to use.
|
||||
# One of: '2' | '3' | empty string (default)
|
||||
# If empty, do not show icons.
|
||||
nerdFontsVersion: ""
|
||||
|
||||
# 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
|
||||
|
||||
# If true, show commit hashes alongside branch names in the branches view.
|
||||
showBranchCommitHash: false
|
||||
|
||||
# Whether to show the divergence from the base branch in the branches view.
|
||||
# One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
|
||||
showDivergenceFromBaseBranch: none
|
||||
|
||||
# Height of the command log view
|
||||
commandLogSize: 8
|
||||
splitDiff: 'auto' # one of 'auto' | 'always'
|
||||
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
||||
border: 'rounded' # one of 'single' | 'double' | 'rounded' | 'hidden'
|
||||
animateExplosion: true # shows an explosion animation when nuking the working tree
|
||||
portraitMode: 'auto' # one of 'auto' | 'never' | 'always'
|
||||
filterMode: 'substring' # one of 'substring' | 'fuzzy'; see 'Filtering' section below
|
||||
|
||||
# Whether to split the main window when viewing file changes.
|
||||
# One of: 'auto' | 'always'
|
||||
# If 'auto', only split the main window when a file has both staged and unstaged changes
|
||||
splitDiff: auto
|
||||
|
||||
# Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).
|
||||
# One of: 'normal' (default) | 'half' | 'full'
|
||||
windowSize: normal
|
||||
|
||||
# Window border style.
|
||||
# One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
||||
border: rounded
|
||||
|
||||
# If true, show a seriously epic explosion animation when nuking the working tree.
|
||||
animateExplosion: true
|
||||
|
||||
# Whether to stack UI components on top of each other.
|
||||
# One of 'auto' (default) | 'always' | 'never'
|
||||
portraitMode: auto
|
||||
|
||||
# How things are filtered when typing '/'.
|
||||
# One of 'substring' (default) | 'fuzzy'
|
||||
filterMode: substring
|
||||
|
||||
# Config relating to the spinner.
|
||||
spinner:
|
||||
frames: ['|', '/', '-', '\\']
|
||||
rate: 50 # spinner rate in milliseconds
|
||||
statusPanelView: 'dashboard' # one of 'dashboard' | 'allBranchesLog'
|
||||
# The frames of the spinner animation.
|
||||
frames:
|
||||
- '|'
|
||||
- /
|
||||
- '-'
|
||||
- \
|
||||
|
||||
# The "speed" of the spinner in milliseconds.
|
||||
rate: 50
|
||||
|
||||
# Status panel view.
|
||||
# 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
|
||||
|
||||
# If true, when using the panel jump keys (default 1 through 5) and target panel is already active, go to next tab instead
|
||||
switchTabsWithPanelJumpKeys: false
|
||||
|
||||
# Config relating to git
|
||||
git:
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
|
||||
paging:
|
||||
# Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never'
|
||||
colorArg: always
|
||||
|
||||
# e.g.
|
||||
# diff-so-fancy
|
||||
# delta --dark --paging=never
|
||||
# ydiff -p cat -s --wrap --width={{columnWidth}}
|
||||
pager: ""
|
||||
|
||||
# If true, Lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
|
||||
useConfig: false
|
||||
|
||||
# e.g. 'difft --color=always'
|
||||
externalDiffCommand: ""
|
||||
|
||||
# Config relating to committing
|
||||
commit:
|
||||
# If true, pass '--signoff' flag when committing
|
||||
signOff: false
|
||||
autoWrapCommitMessage: true # automatic WYSIWYG wrapping of the commit message as you type
|
||||
autoWrapWidth: 72 # if autoWrapCommitMessage is true, the width to wrap to
|
||||
|
||||
# Automatic WYSIWYG wrapping of the commit message as you type
|
||||
autoWrapCommitMessage: true
|
||||
|
||||
# If autoWrapCommitMessage is true, the width to wrap to
|
||||
autoWrapWidth: 72
|
||||
|
||||
# Config relating to merging
|
||||
merging:
|
||||
# only applicable to unix users
|
||||
# If true, run merges in a subprocess so that if a commit message is required, Lazygit will not hang
|
||||
# Only applicable to unix users.
|
||||
manualCommit: false
|
||||
# extra args passed to `git merge`, e.g. --no-ff
|
||||
args: ''
|
||||
|
||||
# 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
|
||||
- main
|
||||
|
||||
# Prefix to use when skipping hooks. E.g. if set to 'WIP', then pre-commit hooks will be skipped when the commit message starts with 'WIP'
|
||||
skipHookPrefix: WIP
|
||||
|
||||
# If true, periodically fetch from remote
|
||||
autoFetch: true
|
||||
|
||||
# If true, periodically refresh files and submodules
|
||||
autoRefresh: true
|
||||
|
||||
# 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.
|
||||
# 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
|
||||
overrideGpg: false
|
||||
|
||||
# If true, do not allow force pushes
|
||||
disableForcePushing: false
|
||||
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix
|
||||
commitPrefix:
|
||||
# pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*"
|
||||
pattern: ""
|
||||
|
||||
# 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
|
||||
|
||||
# Config for showing the log in the commits view
|
||||
log:
|
||||
# one of date-order, author-date-order, topo-order or default.
|
||||
# topo-order makes it easier to read the git log graph, but commits may not
|
||||
# appear chronologically. See https://git-scm.com/docs/git-log#_commit_ordering
|
||||
# One of: 'date-order' | 'author-date-order' | 'topo-order' | 'default'
|
||||
# 'topo-order' makes it easier to read the git log graph, but commits may not
|
||||
# appear chronologically. See https://git-scm.com/docs/
|
||||
#
|
||||
# Deprecated: Configure this with `Log menu -> Commit sort order` (<c-l> in the commits window by default).
|
||||
order: 'topo-order'
|
||||
# one of always, never, when-maximised
|
||||
# this determines whether the git graph is rendered in the commits panel
|
||||
order: topo-order
|
||||
|
||||
# This determines whether the git graph is rendered in the commits panel
|
||||
# One of 'always' | 'never' | 'when-maximised'
|
||||
#
|
||||
# Deprecated: Configure this with `Log menu -> Show git graph` (<c-l> in the commits window by default).
|
||||
showGraph: 'always'
|
||||
# displays the whole git graph by default in the commits panel (equivalent to passing the `--all` argument to `git log`)
|
||||
showGraph: always
|
||||
|
||||
# displays the whole git graph by default in the commits view (equivalent to passing the `--all` argument to `git log`)
|
||||
showWholeGraph: false
|
||||
skipHookPrefix: WIP
|
||||
# The main branches. We colour commits green if they belong to one of these branches,
|
||||
# so that you can easily see which commits are unique to your branch (coloured in yellow)
|
||||
mainBranches: [master, main]
|
||||
autoFetch: true
|
||||
autoRefresh: true
|
||||
fetchAll: true # Pass --all flag when running git fetch. Set to false to fetch only origin (or the current branch's upstream remote if there is one)
|
||||
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'
|
||||
overrideGpg: false # prevents lazygit from spawning a separate process when using GPG
|
||||
disableForcePushing: false
|
||||
parseEmoji: false
|
||||
truncateCopiedCommitHashesTo: 12 # When copying commit hashes to the clipboard, truncate them to this length. Set to 40 to disable truncation.
|
||||
os:
|
||||
copyToClipboardCmd: '' # See 'Custom Command for Copying to Clipboard' section
|
||||
editPreset: '' # see 'Configuring File Editing' section
|
||||
edit: ''
|
||||
editAtLine: ''
|
||||
editAtLineAndWait: ''
|
||||
open: ''
|
||||
openLink: ''
|
||||
refresher:
|
||||
refreshInterval: 10 # File/submodule refresh interval in seconds. Auto-refresh can be disabled via option 'git.autoRefresh'.
|
||||
fetchInterval: 60 # Re-fetch interval in seconds. Auto-fetch can be disabled via option 'git.autoFetch'.
|
||||
|
||||
# When copying commit hashes to the clipboard, truncate them to this
|
||||
# length. Set to 40 to disable truncation.
|
||||
truncateCopiedCommitHashesTo: 12
|
||||
|
||||
# Periodic update checks
|
||||
update:
|
||||
method: prompt # can be: prompt | background | never
|
||||
days: 14 # how often an update is checked for
|
||||
# One of: 'prompt' (default) | 'background' | 'never'
|
||||
method: prompt
|
||||
|
||||
# Period in days between update checks
|
||||
days: 14
|
||||
|
||||
# Background refreshes
|
||||
refresher:
|
||||
# File/submodule refresh interval in seconds.
|
||||
# Auto-refresh can be disabled via option 'git.autoRefresh'.
|
||||
refreshInterval: 10
|
||||
|
||||
# Re-fetch interval in seconds.
|
||||
# Auto-fetch can be disabled via option 'git.autoFetch'.
|
||||
fetchInterval: 60
|
||||
|
||||
# If true, show a confirmation popup before quitting Lazygit
|
||||
confirmOnQuit: false
|
||||
# determines whether hitting 'esc' will quit the application when there is nothing to cancel/close
|
||||
|
||||
# If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close
|
||||
quitOnTopLevelReturn: false
|
||||
|
||||
# Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc
|
||||
os:
|
||||
# Command for editing a file. Should contain "{{filename}}".
|
||||
edit: ""
|
||||
|
||||
# Command for editing a file at a given line number. Should contain
|
||||
# "{{filename}}", and may optionally contain "{{line}}".
|
||||
editAtLine: ""
|
||||
|
||||
# Same as EditAtLine, except that the command needs to wait until the
|
||||
# window is closed.
|
||||
editAtLineAndWait: ""
|
||||
|
||||
# For opening a directory in an editor
|
||||
openDirInEditor: ""
|
||||
|
||||
# A built-in preset that sets all of the above settings. Supported presets
|
||||
# are defined in the getPreset function in editor_presets.go.
|
||||
editPreset: ""
|
||||
|
||||
# Command for opening a file, as if the file is double-clicked. Should
|
||||
# contain "{{filename}}", but doesn't support "{{line}}".
|
||||
open: ""
|
||||
|
||||
# Command for opening a link. Should contain "{{link}}".
|
||||
openLink: ""
|
||||
|
||||
# EditCommand is the command for editing a file.
|
||||
# Deprecated: use Edit instead. Note that semantics are different:
|
||||
# EditCommand is just the command itself, whereas Edit contains a
|
||||
# "{{filename}}" variable.
|
||||
editCommand: ""
|
||||
|
||||
# EditCommandTemplate is the command template for editing a file
|
||||
# Deprecated: use EditAtLine instead.
|
||||
editCommandTemplate: ""
|
||||
|
||||
# OpenCommand is the command for opening a file
|
||||
# Deprecated: use Open instead.
|
||||
openCommand: ""
|
||||
|
||||
# OpenLinkCommand is the command for opening a link
|
||||
# Deprecated: use OpenLink instead.
|
||||
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-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
|
||||
notARepository: 'prompt' # one of: 'prompt' | 'create' | 'skip' | 'quit'
|
||||
promptToReturnFromSubprocess: true # display confirmation when subprocess terminates
|
||||
|
||||
# What to do when opening Lazygit outside of a git repo.
|
||||
# - 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo
|
||||
# - 'create': initialize a new repo
|
||||
# - 'skip': open most recent repo
|
||||
# - 'quit': exit Lazygit
|
||||
notARepository: prompt
|
||||
|
||||
# If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.
|
||||
promptToReturnFromSubprocess: true
|
||||
|
||||
# Keybindings
|
||||
keybinding:
|
||||
universal:
|
||||
quit: 'q'
|
||||
quit-alt1: '<c-c>' # alternative/alias of quit
|
||||
return: '<esc>' # return to previous menu, will quit if there's nowhere to return
|
||||
quitWithoutChangingDirectory: 'Q'
|
||||
togglePanel: '<tab>' # goto the next panel
|
||||
prevItem: '<up>' # go one line up
|
||||
nextItem: '<down>' # go one line down
|
||||
prevItem-alt: 'k' # go one line up
|
||||
nextItem-alt: 'j' # go one line down
|
||||
prevPage: ',' # go to next page in list
|
||||
nextPage: '.' # go to previous page in list
|
||||
gotoTop: '<' # go to top of list
|
||||
gotoBottom: '>' # go to bottom of list
|
||||
scrollLeft: 'H' # scroll left within list view
|
||||
scrollRight: 'L' # scroll right within list view
|
||||
prevBlock: '<left>' # goto the previous block / panel
|
||||
nextBlock: '<right>' # goto the next block / panel
|
||||
prevBlock-alt: 'h' # goto the previous block / panel
|
||||
nextBlock-alt: 'l' # goto the next block / panel
|
||||
jumpToBlock: ['1', '2', '3', '4', '5'] # goto the Nth block / panel
|
||||
nextMatch: 'n'
|
||||
prevMatch: 'N'
|
||||
optionMenu: <disabled> # show help menu
|
||||
optionMenu-alt1: '?' # show help menu
|
||||
select: '<space>'
|
||||
goInto: '<enter>'
|
||||
openRecentRepos: '<c-r>'
|
||||
confirm: '<enter>'
|
||||
remove: 'd'
|
||||
new: 'n'
|
||||
edit: 'e'
|
||||
openFile: 'o'
|
||||
scrollUpMain: '<pgup>' # main panel scroll up
|
||||
scrollDownMain: '<pgdown>' # main panel scroll down
|
||||
scrollUpMain-alt1: 'K' # main panel scroll up
|
||||
scrollDownMain-alt1: 'J' # main panel scroll down
|
||||
scrollUpMain-alt2: '<c-u>' # main panel scroll up
|
||||
scrollDownMain-alt2: '<c-d>' # main panel scroll down
|
||||
executeCustomCommand: ':'
|
||||
createRebaseOptionsMenu: 'm'
|
||||
pushFiles: 'P'
|
||||
pullFiles: 'p'
|
||||
refresh: 'R'
|
||||
createPatchOptionsMenu: '<c-p>'
|
||||
quit: q
|
||||
quit-alt1: <c-c>
|
||||
return: <esc>
|
||||
quitWithoutChangingDirectory: Q
|
||||
togglePanel: <tab>
|
||||
prevItem: <up>
|
||||
nextItem: <down>
|
||||
prevItem-alt: k
|
||||
nextItem-alt: j
|
||||
prevPage: ','
|
||||
nextPage: .
|
||||
scrollLeft: H
|
||||
scrollRight: L
|
||||
gotoTop: <
|
||||
gotoBottom: '>'
|
||||
toggleRangeSelect: v
|
||||
rangeSelectDown: <s-down>
|
||||
rangeSelectUp: <s-up>
|
||||
prevBlock: <left>
|
||||
nextBlock: <right>
|
||||
prevBlock-alt: h
|
||||
nextBlock-alt: l
|
||||
nextBlock-alt2: <tab>
|
||||
prevBlock-alt2: <backtab>
|
||||
jumpToBlock:
|
||||
- "1"
|
||||
- "2"
|
||||
- "3"
|
||||
- "4"
|
||||
- "5"
|
||||
nextMatch: "n"
|
||||
prevMatch: "N"
|
||||
startSearch: /
|
||||
optionMenu: <disabled>
|
||||
optionMenu-alt1: '?'
|
||||
select: <space>
|
||||
goInto: <enter>
|
||||
confirm: <enter>
|
||||
confirmInEditor: <a-enter>
|
||||
remove: d
|
||||
new: "n"
|
||||
edit: e
|
||||
openFile: o
|
||||
scrollUpMain: <pgup>
|
||||
scrollDownMain: <pgdown>
|
||||
scrollUpMain-alt1: K
|
||||
scrollDownMain-alt1: J
|
||||
scrollUpMain-alt2: <c-u>
|
||||
scrollDownMain-alt2: <c-d>
|
||||
executeShellCommand: ':'
|
||||
createRebaseOptionsMenu: m
|
||||
|
||||
# 'Files' appended for legacy reasons
|
||||
pushFiles: P
|
||||
|
||||
# 'Files' appended for legacy reasons
|
||||
pullFiles: p
|
||||
refresh: R
|
||||
createPatchOptionsMenu: <c-p>
|
||||
nextTab: ']'
|
||||
prevTab: '['
|
||||
nextScreenMode: '+'
|
||||
prevScreenMode: '_'
|
||||
undo: 'z'
|
||||
redo: '<c-z>'
|
||||
filteringMenu: '<c-s>'
|
||||
diffingMenu: 'W'
|
||||
diffingMenu-alt: '<c-e>' # deprecated
|
||||
copyToClipboard: '<c-o>'
|
||||
submitEditorText: '<enter>'
|
||||
nextScreenMode: +
|
||||
prevScreenMode: _
|
||||
undo: z
|
||||
redo: <c-z>
|
||||
filteringMenu: <c-s>
|
||||
diffingMenu: W
|
||||
diffingMenu-alt: <c-e>
|
||||
copyToClipboard: <c-o>
|
||||
openRecentRepos: <c-r>
|
||||
submitEditorText: <enter>
|
||||
extrasMenu: '@'
|
||||
toggleWhitespaceInDiffView: '<c-w>'
|
||||
toggleWhitespaceInDiffView: <c-w>
|
||||
increaseContextInDiffView: '}'
|
||||
decreaseContextInDiffView: '{'
|
||||
toggleRangeSelect: 'v'
|
||||
rangeSelectUp: '<s-up>'
|
||||
rangeSelectDown: '<s-down>'
|
||||
increaseRenameSimilarityThreshold: )
|
||||
decreaseRenameSimilarityThreshold: (
|
||||
openDiffTool: <c-t>
|
||||
status:
|
||||
checkForUpdate: 'u'
|
||||
recentRepos: '<enter>'
|
||||
checkForUpdate: u
|
||||
recentRepos: <enter>
|
||||
allBranchesLogGraph: a
|
||||
files:
|
||||
commitChanges: 'c'
|
||||
commitChangesWithoutHook: 'w' # commit changes without pre-commit hook
|
||||
amendLastCommit: 'A'
|
||||
commitChangesWithEditor: 'C'
|
||||
findBaseCommitForFixup: '<c-f>'
|
||||
confirmDiscard: 'x'
|
||||
ignoreFile: 'i'
|
||||
refreshFiles: 'r'
|
||||
stashAllChanges: 's'
|
||||
viewStashOptions: 'S'
|
||||
toggleStagedAll: 'a' # stage/unstage all
|
||||
viewResetOptions: 'D'
|
||||
fetch: 'f'
|
||||
commitChanges: c
|
||||
commitChangesWithoutHook: w
|
||||
amendLastCommit: A
|
||||
commitChangesWithEditor: C
|
||||
findBaseCommitForFixup: <c-f>
|
||||
confirmDiscard: x
|
||||
ignoreFile: i
|
||||
refreshFiles: r
|
||||
stashAllChanges: s
|
||||
viewStashOptions: S
|
||||
toggleStagedAll: a
|
||||
viewResetOptions: D
|
||||
fetch: f
|
||||
toggleTreeView: '`'
|
||||
openMergeTool: 'M'
|
||||
openStatusFilter: '<c-b>'
|
||||
openMergeTool: M
|
||||
openStatusFilter: <c-b>
|
||||
copyFileInfoToClipboard: "y"
|
||||
branches:
|
||||
createPullRequest: 'o'
|
||||
viewPullRequestOptions: 'O'
|
||||
checkoutBranchByName: 'c'
|
||||
forceCheckoutBranch: 'F'
|
||||
rebaseBranch: 'r'
|
||||
renameBranch: 'R'
|
||||
mergeIntoCurrentBranch: 'M'
|
||||
viewGitFlowOptions: 'i'
|
||||
fastForward: 'f' # fast-forward this branch from its upstream
|
||||
createTag: 'T'
|
||||
pushTag: 'P'
|
||||
setUpstream: 'u' # set as upstream of checked-out branch
|
||||
fetchRemote: 'f'
|
||||
createPullRequest: o
|
||||
viewPullRequestOptions: O
|
||||
copyPullRequestURL: <c-y>
|
||||
checkoutBranchByName: c
|
||||
forceCheckoutBranch: F
|
||||
rebaseBranch: r
|
||||
renameBranch: R
|
||||
mergeIntoCurrentBranch: M
|
||||
viewGitFlowOptions: i
|
||||
fastForward: f
|
||||
createTag: T
|
||||
pushTag: P
|
||||
setUpstream: u
|
||||
fetchRemote: f
|
||||
sortOrder: s
|
||||
worktrees:
|
||||
viewWorktreeOptions: w
|
||||
commits:
|
||||
squashDown: 's'
|
||||
renameCommit: 'r'
|
||||
renameCommitWithEditor: 'R'
|
||||
viewResetOptions: 'g'
|
||||
markCommitAsFixup: 'f'
|
||||
createFixupCommit: 'F' # create fixup commit for this commit
|
||||
squashAboveCommits: 'S'
|
||||
moveDownCommit: '<c-j>' # move commit down one
|
||||
moveUpCommit: '<c-k>' # move commit up one
|
||||
amendToCommit: 'A'
|
||||
amendAttributeMenu: 'a'
|
||||
pickCommit: 'p' # pick commit (when mid-rebase)
|
||||
revertCommit: 't'
|
||||
cherryPickCopy: 'C'
|
||||
pasteCommits: 'V'
|
||||
tagCommit: 'T'
|
||||
checkoutCommit: '<space>'
|
||||
resetCherryPick: '<c-R>'
|
||||
copyCommitMessageToClipboard: '<c-y>'
|
||||
openLogMenu: '<c-l>'
|
||||
viewBisectOptions: 'b'
|
||||
stash:
|
||||
popStash: 'g'
|
||||
renameStash: 'r'
|
||||
commitFiles:
|
||||
checkoutCommitFile: 'c'
|
||||
main:
|
||||
toggleSelectHunk: 'a'
|
||||
pickBothHunks: 'b'
|
||||
submodules:
|
||||
init: 'i'
|
||||
update: 'u'
|
||||
bulkMenu: 'b'
|
||||
commitMessage:
|
||||
commitMenu: '<c-o>'
|
||||
squashDown: s
|
||||
renameCommit: r
|
||||
renameCommitWithEditor: R
|
||||
viewResetOptions: g
|
||||
markCommitAsFixup: f
|
||||
createFixupCommit: F
|
||||
squashAboveCommits: S
|
||||
moveDownCommit: <c-j>
|
||||
moveUpCommit: <c-k>
|
||||
amendToCommit: A
|
||||
resetCommitAuthor: a
|
||||
pickCommit: p
|
||||
revertCommit: t
|
||||
cherryPickCopy: C
|
||||
pasteCommits: V
|
||||
markCommitAsBaseForRebase: B
|
||||
tagCommit: T
|
||||
checkoutCommit: <space>
|
||||
resetCherryPick: <c-R>
|
||||
copyCommitAttributeToClipboard: "y"
|
||||
openLogMenu: <c-l>
|
||||
openInBrowser: o
|
||||
viewBisectOptions: b
|
||||
startInteractiveRebase: i
|
||||
amendAttribute:
|
||||
addCoAuthor: 'c'
|
||||
resetAuthor: 'a'
|
||||
setAuthor: 'A'
|
||||
resetAuthor: a
|
||||
setAuthor: A
|
||||
addCoAuthor: c
|
||||
stash:
|
||||
popStash: g
|
||||
renameStash: r
|
||||
commitFiles:
|
||||
checkoutCommitFile: c
|
||||
main:
|
||||
toggleSelectHunk: a
|
||||
pickBothHunks: b
|
||||
editSelectHunk: E
|
||||
submodules:
|
||||
init: i
|
||||
update: u
|
||||
bulkMenu: b
|
||||
commitMessage:
|
||||
commitMenu: <c-o>
|
||||
```
|
||||
<!-- END CONFIG YAML -->
|
||||
|
||||
## Platform Defaults
|
||||
|
||||
@@ -315,7 +651,7 @@ os:
|
||||
open: 'open {{filename}}'
|
||||
```
|
||||
|
||||
## Custom Command for Copying to Clipboard
|
||||
## Custom Command for Copying to and Pasting from Clipboard
|
||||
```yaml
|
||||
os:
|
||||
copyToClipboardCmd: ''
|
||||
@@ -328,6 +664,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
|
||||
|
||||
@@ -340,7 +682,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.
|
||||
|
||||
@@ -566,6 +908,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:
|
||||
|
||||
@@ -59,6 +59,7 @@ For a given custom command, here are the allowed fields:
|
||||
| description | Label for the custom command when displayed in the keybindings menu | no |
|
||||
| stream | Whether you want to stream the command's output to the Command Log panel | no |
|
||||
| showOutput | Whether you want to show the command's output in a popup within Lazygit | no |
|
||||
| outputTitle | The title to display in the popup panel if showOutput is true. If left unset, the command will be used as the title. | no |
|
||||
| after | Actions to take after the command has completed | no |
|
||||
|
||||
Here are the options for the `after` key:
|
||||
@@ -86,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
|
||||
@@ -290,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
|
||||
@@ -305,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
|
||||
|
||||
+7
-19
@@ -56,22 +56,10 @@ base commit in the Commits view automatically. From there, you can either press
|
||||
shift-F to create a fixup commit for it, or shift-A to amend your changes into
|
||||
the commit if you haven't published your branch yet.
|
||||
|
||||
This command works in many cases, and when it does it almost feels like magic,
|
||||
but it's important to understand its limitations because it doesn't always work.
|
||||
The way it works is that it looks at the deleted lines of your current
|
||||
modifications, blames them to find out which commit those lines come from, and
|
||||
if they all come from the same commit, it selects it. So here are cases where it
|
||||
doesn't work:
|
||||
|
||||
- Your current diff has only added lines, but no deleted lines. In this case
|
||||
there's no way for lazygit to know which commit you want to add them to.
|
||||
- The deleted lines belong to multiple different commits. In this case you can
|
||||
help lazygit by staging a set of files or hunks that all belong to the same
|
||||
commit; if some changes are staged, the ctrl-f command works only on those.
|
||||
- The found commit is already on master; in this case, lazygit refuses to select
|
||||
it, because it doesn't make sense to create fixups for it, let alone amend to
|
||||
it.
|
||||
|
||||
To sum it up: the command works great if you are changing code again that you
|
||||
changed or added earlier in the same branch. This is a common enough case to
|
||||
make the command useful.
|
||||
If you have many modifications in your working copy, it is a good idea to stage
|
||||
related changes that are meant to go into the same fixup commit; if no changes
|
||||
are staged, ctrl-f works on all unstaged modifications, and then it might show
|
||||
an error if it finds multiple different base commits. If you are interested in
|
||||
what the command does to do its magic, and how you can help it work better, you
|
||||
may want to read the [design document](dev/Find_Base_Commit_For_Fixup_Design.md)
|
||||
that describes this.
|
||||
|
||||
+11
-11
@@ -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).
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
# About the mechanics of lazygit's "Find base commit for fixup" command
|
||||
|
||||
## Background
|
||||
|
||||
Lazygit has a command called "Find base commit for fixup" that helps with
|
||||
creating fixup commits. (It is bound to "ctrl-f" by default, and I'll call it
|
||||
simply "the ctrl-f command" throughout the rest of this text for brevity.)
|
||||
|
||||
It's a heuristic that needs to make a few assumptions; it tends to work well in
|
||||
practice if users are aware of its limitations. The user-facing side of the
|
||||
topic is explained [here](../Fixup_Commits.md). In this document we describe how
|
||||
it works internally, and the design decisions behind it.
|
||||
|
||||
It is also interesting to compare it to the standalone tool
|
||||
[git-absorb](https://github.com/tummychow/git-absorb) which does a very similar
|
||||
thing, but made different decisions in some cases. We'll explore these
|
||||
differences in this document.
|
||||
|
||||
## Design goals
|
||||
|
||||
I'll start with git-absorb's design goals (my interpretation, since I can't
|
||||
speak for git-absorb's maintainer of course): its main goal seems to be minimum
|
||||
user interaction required. The idea is that you have a PR in review, the
|
||||
reviewer requested a bunch of changes, you make all these changes, so you have a
|
||||
working copy with lots of modified files, and then you fire up git-absorb and it
|
||||
creates all the necessary fixup commits automatically with no further user
|
||||
intervention.
|
||||
|
||||
While this sounds attractive, it conflicts with ctrl-f's main design goal, which
|
||||
is to support creating high-quality fixups. My philosophy is that fixup commits
|
||||
should have the same high quality standards as normal commits; in particular:
|
||||
|
||||
- they should be atomic. This means that multiple diff hunks that belong
|
||||
together to form one logical change should be in the same fixup commit. (Not
|
||||
always possible if the logical change needs to be fixed up into several
|
||||
different base commits.)
|
||||
- they should be minimal. Every fixup commit should ideally contain only one
|
||||
logical change, not several unrelated ones.
|
||||
|
||||
Why is this important? Because fixup commits are mainly a tool for reviewing (if
|
||||
they weren't, you might as well squash the changes into their base commits right
|
||||
away). And reviewing fixup commits is easier if they are well-structured, just
|
||||
like normal commits.
|
||||
|
||||
The only way to achieve this with git-absorb is to set the `oneFixupPerCommit`
|
||||
config option (for the first goal), and then manually stage the changes that
|
||||
belong together (for the second). This is close to what you have to do with
|
||||
ctrl-f, with one exception that we'll get to below.
|
||||
|
||||
But ctrl-f enforces this by refusing to do the job if the staged hunks belong to
|
||||
more than one base commit. Git-absorb will happily create multiple fixup commits
|
||||
in this case; ctrl-f doesn't, to enforce that you pay attention to how you group
|
||||
the changes. There's another reason for this behavior: ctrl-f doesn't create
|
||||
fixup commits itself (unlike git-absorb), instead it just selects the found base
|
||||
commit so that the user can decide whether to amend the changes right in, or
|
||||
create a fixup commit from there (both are single-key commands in lazygit). And
|
||||
lazygit doesn't support non-contiguous multiselections of commits, but even if
|
||||
it did, it wouldn't help much in this case.
|
||||
|
||||
## The mechanics
|
||||
|
||||
### General approach
|
||||
|
||||
Git-absorb uses a relatively simple approach, and the benefit is of course that
|
||||
it is easy to understand: it looks at every diff hunk separately, and for every
|
||||
hunk it looks at all commits (starting from the newest one backwards) to find
|
||||
the earliest commit that the change can be amended to without conflicts.
|
||||
|
||||
It is important to realize that "diff hunk" doesn't necessarily mean what you
|
||||
see in the diff view. Git-absorb and ctrl-f both use a context of 0 when diffing
|
||||
your code, so they often see more and smaller hunks than users do. For example,
|
||||
moving a line of code down by one line is a single hunk for users, but it's two
|
||||
separate hunks for git-absorb and ctrl-f; one for deleting the line at the old
|
||||
place, and another one for adding the line at the new place, even if it's only
|
||||
one line further down.
|
||||
|
||||
From this, it follows that there's one big problem with git-absorb's approach:
|
||||
when moving code, it doesn't realize that the two related hunks of deleting the
|
||||
code from the old place and inserting it at the new place belong together, and
|
||||
often it will manage to create a fixup commit for the first hunk, but leave the
|
||||
other hunk in your working copy as "don't know what to do with this". As an
|
||||
example, suppose your PR is adding a line of code to an existing function, maybe
|
||||
one that declares a new variable, and a reviewer suggests to move this line down
|
||||
a bit, closer to where some other related variables are declared. Moving the
|
||||
line down results in two diff hunks (from the perspective of git-absorb and
|
||||
ctrl-f, as they both use a context of 0 when diffing), and when looking at the
|
||||
second diff hunk in isolation there's no way to find a base commit in your PR
|
||||
for it, because the surrounding code is already on main.
|
||||
|
||||
To solve this, the ctrl-f command makes a distinction between hunks that have
|
||||
deleted lines and hunks that have only added lines. If the whole diff contains
|
||||
any hunks that have deleted lines, it uses only those hunks to determine the
|
||||
base commit, and then assumes that all the hunks that have only added lines
|
||||
belong into the same commit. This nicely solves the above example of moving
|
||||
code, but also other examples such as the following:
|
||||
|
||||
<details>
|
||||
<summary>Click to show example</summary>
|
||||
|
||||
Suppose you have a PR in which you added the following function:
|
||||
|
||||
```go
|
||||
func findCommit(hash string) (*models.Commit, int, bool) {
|
||||
for i, commit := range self.c.Model().Commits {
|
||||
if commit.Hash == hash {
|
||||
return commit, i, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, -1, false
|
||||
}
|
||||
```
|
||||
|
||||
A reviewer suggests to replace the manual `for` loop with a call to
|
||||
`lo.FindIndexOf` since that's less code and more idiomatic. So your modification
|
||||
is this:
|
||||
|
||||
```diff
|
||||
--- a/my_file.go
|
||||
+++ b/my_file.go
|
||||
@@ -12,2 +12,3 @@ import (
|
||||
"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, error
|
||||
func findCommit(hash string) (*models.Commit, int, bool) {
|
||||
- for i, commit := range self.c.Model().Commits {
|
||||
- if commit.Hash == hash {
|
||||
- return commit, i, true
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return nil, -1, false
|
||||
+ return lo.FindIndexOf(self.c.Model().Commits, func(commit *models.Commit) bool {
|
||||
+ return commit.Hash == hash
|
||||
+ })
|
||||
}
|
||||
```
|
||||
|
||||
If we were to look at these two hunks separately, we'd easily find the base
|
||||
commit for the second one, but we wouldn't find the one for the first hunk
|
||||
because the imports around the added import have been on main for a long time.
|
||||
In fact, git-absorb leaves this hunk in the working copy because it doesn't know
|
||||
what to do with it.
|
||||
|
||||
</details>
|
||||
|
||||
Only if there are no hunks with deleted lines does ctrl-f look at the hunks with
|
||||
only added lines and determines the base commit for them. This solves cases like
|
||||
adding a comment above a function that you added in your PR.
|
||||
|
||||
The downside of this more complicated approach is that it relies on the user
|
||||
staging related hunks correctly. However, in my experience this is easy to do
|
||||
and not very error-prone, as long as users are aware of this behavior. Lazygit
|
||||
tries to help making them aware of it by showing a warning whenever there are
|
||||
hunks with only added lines in addition to hunks with deleted lines.
|
||||
|
||||
### Finding the base commit for a given hunk
|
||||
|
||||
As explained above, git-absorb finds the base commit by walking the commits
|
||||
backwards until it finds one that conflicts with the hunk, and then the found
|
||||
base commit is the one just before that one. This works reliably, but it is
|
||||
slow.
|
||||
|
||||
Ctrl-f uses a different approach that is usually much faster, but should always
|
||||
yield the same result. Again, it makes a distinction between hunks with deleted
|
||||
lines and hunks with only added lines. For hunks with deleted lines it performs
|
||||
a line range blame for all the deleted lines (e.g. `git blame -L42,+3 --
|
||||
filename`), and if the result is the same for all deleted lines, then that's the
|
||||
base commit; otherwise it returns an error.
|
||||
|
||||
For hunks with only added lines, it gets a little more complicated. We blame the
|
||||
single lines just before and just after the hunk (I'll ignore the edge cases of
|
||||
either of those not existing because the hunk is at the beginning or end of the
|
||||
file; read the code to see how we handle these cases). If the blame result is
|
||||
the same for both, then that's the base commit. This is the case of adding a
|
||||
line in the middle of a block of code that was added in the PR. Otherwise, the
|
||||
base commit is the more recent of the two (and in this case it doesn't matter if
|
||||
the other one is an earlier commit in the current branch, or a possibly very old
|
||||
commit that's already on main). This covers the common case of adding a comment
|
||||
to a function that was added in the PR, but also adding another line at the end
|
||||
of a block of code that was added in the base commit.
|
||||
|
||||
It's interesting to discuss what "more recent" means here. You could say if
|
||||
commit A is an ancestor of commit B (or in other words, A is reachable from B)
|
||||
then B is the more recent one. And if none of the two commits is reachable from
|
||||
the other, you have an error case because it's unclear which of the two should
|
||||
be considered the base commit. The scenario in which this happens is a commit
|
||||
history like this:
|
||||
|
||||
```
|
||||
C---D
|
||||
/ \
|
||||
A---B---E---F---G
|
||||
```
|
||||
|
||||
where, for instance, D and E are the two blame results.
|
||||
|
||||
Unfortunately, determining the ancestry relationship between two commits using
|
||||
git commands is a bit expensive and not totally straightforward. Fortunately,
|
||||
it's not necessary in lazygit because lazygit has the most recent 300 commits
|
||||
cached in memory, and can simply search its linear list of commits to see which
|
||||
one is closer to the beginning of the list. If only one of the two commits is
|
||||
found within those 300 commits, then that's the more recent one; if neither is
|
||||
found, we assume that both commits are on main and error out. In the merge
|
||||
scenario pictured above, we arbitrarily return one of the two commits (this will
|
||||
depend on the log order), but that's probably fine as this scenario should be
|
||||
extremely rare in practice; in most cases, feature branches are simply linear.
|
||||
|
||||
### Knowing where to stop searching
|
||||
|
||||
Git-absorb needs to know when to stop walking backwards searching for commits,
|
||||
since it doesn't make sense to create fixups for commits that are already on
|
||||
main. However, it doesn't know where the current branch ends and main starts, so
|
||||
it needs to rely on user input for this. By default it searches the most recent
|
||||
10 commits, but this can be overridden with a config setting. In longer branches
|
||||
this is often not enough for finding the base commit; but setting it to a higher
|
||||
value causes the command to take longer to complete when the base commit can't
|
||||
be found.
|
||||
|
||||
Lazygit doesn't have this problem. For a given blame result it needs to
|
||||
determine whether that commit is already on main, and if it can find the commit
|
||||
in its cached list of the first 300 commits it can get that information from
|
||||
there, because lazygit knows what the user's configured main branches are
|
||||
(`master` and `main` by default, but it could also include branches like `devel`
|
||||
or `1.0-hotfixes`), and so it can tell for each commit whether it's contained in
|
||||
one of those main branches. And if it can't find it among the first 300 commits,
|
||||
it assumes the commit already on main, on the assumption that no feature branch
|
||||
has more than 300 commits.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Profiling Lazygit
|
||||
|
||||
If you want to investigate what's contributing to CPU or memory usage, start
|
||||
lazygit with the `-profile` command line flag. This tells it to start an
|
||||
integrated web server that listens for profiling requests.
|
||||
|
||||
## Save profile data
|
||||
|
||||
### CPU
|
||||
|
||||
While lazygit is running with the `-profile` flag, perform a CPU profile and
|
||||
save it to a file by running this command in another terminal window:
|
||||
|
||||
```sh
|
||||
curl -o cpu.out http://127.0.0.1:6060/debug/pprof/profile
|
||||
```
|
||||
|
||||
By default, it profiles for 30 seconds. To change the duration, use
|
||||
|
||||
```sh
|
||||
curl -o cpu.out 'http://127.0.0.1:6060/debug/pprof/profile?seconds=60'
|
||||
```
|
||||
|
||||
### Memory
|
||||
|
||||
To save a heap profile (containing information about all memory allocated so
|
||||
far since startup), use
|
||||
|
||||
```sh
|
||||
curl -o mem.out http://127.0.0.1:6060/debug/pprof/heap
|
||||
```
|
||||
|
||||
Sometimes it can be useful to get a delta log, i.e. to see how memory usage
|
||||
developed from one point in time to another. For that, use
|
||||
|
||||
```sh
|
||||
curl -o mem.out 'http://127.0.0.1:6060/debug/pprof/heap?seconds=20'
|
||||
```
|
||||
|
||||
This will log the memory usage difference between now and 20 seconds later, so
|
||||
it gives you 20 seconds to perform the action in lazygit that you are interested
|
||||
in measuring.
|
||||
|
||||
## View profile data
|
||||
|
||||
To display the profile data, you can either use speedscope.app, or the pprof
|
||||
tool that comes with go. I prefer the former because it has a nicer UI and is a
|
||||
little more powerful; however, I have seen cases where it wasn't able to load a
|
||||
profile for some reason, in which case it's good to have the pprof tool as a
|
||||
fallback.
|
||||
|
||||
### Speedscope.app
|
||||
|
||||
Go to https://www.speedscope.app/ in your browser, and drag the saved profile
|
||||
onto the browser window. Refer to [the
|
||||
documentation](https://github.com/jlfwong/speedscope?tab=readme-ov-file#usage)
|
||||
for how to navigate the data.
|
||||
|
||||
### Pprof tool
|
||||
|
||||
To view a profile that you saved as `cpu.out`, use
|
||||
|
||||
```sh
|
||||
go tool pprof -http=:8080 cpu.out
|
||||
```
|
||||
|
||||
By default this shows the graph view, which I don't find very useful myself.
|
||||
Choose "Flame Graph" from the View menu to show a much more useful
|
||||
representation of the data.
|
||||
@@ -4,3 +4,5 @@
|
||||
* [Busy/Idle Tracking](./Busy.md)
|
||||
* [Integration Tests](../../pkg/integration/README.md)
|
||||
* [Demo Recordings](./Demo_Recordings.md)
|
||||
* [Find base commit for fixup design](Find_Base_Commit_For_Fixup_Design.md)
|
||||
* [Profiling](Profiling.md)
|
||||
|
||||
@@ -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`. |
|
||||
@@ -80,7 +82,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` 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 fixup, but the selected commit's message will be discarded. |
|
||||
| `` 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 | Reword the selected commit's message. |
|
||||
| `` R `` | Reword with editor | |
|
||||
| `` d `` | Drop | 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. |
|
||||
@@ -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`. |
|
||||
@@ -97,7 +99,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` 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 fixup, but the selected commit's message will be discarded. |
|
||||
| `` 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 `` | エディタでコミットメッセージを編集 | |
|
||||
| `` 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. |
|
||||
@@ -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 | |
|
||||
@@ -260,7 +264,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
|
||||
| `` b `` | Bisect 옵션 보기 | |
|
||||
| `` 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 fixup, but the selected commit's message will be discarded. |
|
||||
| `` 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 `` | 에디터에서 커밋메시지 수정 | |
|
||||
| `` 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. |
|
||||
@@ -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 | |
|
||||
@@ -143,7 +146,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` 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 fixup, but the selected commit's message will be discarded. |
|
||||
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
|
||||
| `` r `` | Hernoem commit | Reword the selected commit's message. |
|
||||
| `` R `` | Hernoem commit met editor | |
|
||||
| `` d `` | Verwijder commit | 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. |
|
||||
@@ -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`. |
|
||||
@@ -144,7 +146,7 @@ _Связки клавиш_
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов | |
|
||||
| `` 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 fixup, but the selected commit's message will be discarded. |
|
||||
| `` 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 `` | Переписать коммит с помощью редактора | |
|
||||
| `` 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. |
|
||||
@@ -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 fixup, 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. |
|
||||
| `` / `` | 搜尋 | |
|
||||
|
||||
@@ -166,23 +168,23 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
|
||||
| `` 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 fixup, but the selected commit's message will be discarded. |
|
||||
| `` r `` | 改寫提交 | Reword the selected commit's message. |
|
||||
| `` f `` | 修復 (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
|
||||
| `` 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 `` | 檢視工作目錄選項 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
|
||||
@@ -11,11 +11,12 @@ require (
|
||||
github.com/gdamore/tcell/v2 v2.7.4
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/iancoleman/orderedmap v0.3.0
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240928100326-393cf89a5d3f
|
||||
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
|
||||
@@ -23,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
|
||||
@@ -37,6 +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.8.0
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -73,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
|
||||
)
|
||||
|
||||
@@ -171,6 +171,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
|
||||
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
@@ -186,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.20240928100326-393cf89a5d3f h1:ZzsAUDwPFLPITKLcJpMSqt/3rERdI8YRZKr2l0plrls=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240928100326-393cf89a5d3f/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=
|
||||
@@ -233,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=
|
||||
@@ -421,6 +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.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=
|
||||
@@ -470,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=
|
||||
@@ -487,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=
|
||||
|
||||
+28
-24
@@ -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())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -30,6 +32,7 @@ type cliArgs struct {
|
||||
PrintVersionInfo bool
|
||||
Debug bool
|
||||
TailLogs bool
|
||||
Profile bool
|
||||
PrintDefaultConfig bool
|
||||
PrintConfigDir bool
|
||||
UseConfigDir string
|
||||
@@ -133,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)
|
||||
@@ -145,6 +154,14 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
||||
return
|
||||
}
|
||||
|
||||
if cliArgs.Profile {
|
||||
go func() {
|
||||
if err := http.ListenAndServe("localhost:6060", nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
parsedGitArg := parseGitArg(cliArgs.GitArg)
|
||||
|
||||
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest))
|
||||
@@ -171,6 +188,9 @@ func parseCliArgsAndEnvVars() *cliArgs {
|
||||
tailLogs := false
|
||||
flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)")
|
||||
|
||||
profile := false
|
||||
flaggy.Bool(&profile, "", "profile", "Start the profiler and serve it on http port 6060. See CONTRIBUTING.md for more info.")
|
||||
|
||||
printDefaultConfig := false
|
||||
flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config")
|
||||
|
||||
@@ -202,6 +222,7 @@ func parseCliArgsAndEnvVars() *cliArgs {
|
||||
PrintVersionInfo: printVersionInfo,
|
||||
Debug: debug,
|
||||
TailLogs: tailLogs,
|
||||
Profile: profile,
|
||||
PrintDefaultConfig: printDefaultConfig,
|
||||
PrintConfigDir: printConfigDir,
|
||||
UseConfigDir: useConfigDir,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -4,13 +4,16 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"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 +31,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 +146,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 +228,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 +247,40 @@ 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()
|
||||
}
|
||||
|
||||
func (self *BranchCommands) IsBranchMerged(branch *models.Branch, mainBranches *MainBranches) (bool, error) {
|
||||
branchesToCheckAgainst := []string{"HEAD"}
|
||||
if branch.RemoteBranchStoredLocally() {
|
||||
branchesToCheckAgainst = append(branchesToCheckAgainst, fmt.Sprintf("%s@{upstream}", branch.Name))
|
||||
}
|
||||
branchesToCheckAgainst = append(branchesToCheckAgainst, mainBranches.Get()...)
|
||||
|
||||
cmdArgs := NewGitCmd("rev-list").
|
||||
Arg("--max-count=1").
|
||||
Arg(branch.Name).
|
||||
Arg(lo.Map(branchesToCheckAgainst, func(branch string, _ int) string {
|
||||
return fmt.Sprintf("^%s", branch)
|
||||
})...).
|
||||
ToArgv()
|
||||
|
||||
stdout, _, err := self.cmd.New(cmdArgs).RunWithOutputs()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return stdout == "", nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/go-git/v5/config"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// context:
|
||||
@@ -63,7 +65,13 @@ func NewBranchLoader(
|
||||
}
|
||||
|
||||
// Load the list of branches for the current repo
|
||||
func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch, error) {
|
||||
func (self *BranchLoader) Load(reflogCommits []*models.Commit,
|
||||
mainBranches *MainBranches,
|
||||
oldBranches []*models.Branch,
|
||||
loadBehindCounts bool,
|
||||
onWorker func(func() error),
|
||||
renderFunc func(),
|
||||
) ([]*models.Branch, error) {
|
||||
branches := self.obtainBranches(self.version.IsAtLeast(2, 22, 0))
|
||||
|
||||
if self.AppState.LocalBranchSortOrder == "recency" {
|
||||
@@ -122,11 +130,108 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch
|
||||
branch.UpstreamRemote = match.Remote
|
||||
branch.UpstreamBranch = match.Merge.Short()
|
||||
}
|
||||
|
||||
// If the branch already existed, take over its BehindBaseBranch value
|
||||
// to reduce flicker
|
||||
if oldBranch, found := lo.Find(oldBranches, func(b *models.Branch) bool {
|
||||
return b.Name == branch.Name
|
||||
}); found {
|
||||
branch.BehindBaseBranch.Store(oldBranch.BehindBaseBranch.Load())
|
||||
}
|
||||
}
|
||||
|
||||
if loadBehindCounts && self.UserConfig().Gui.ShowDivergenceFromBaseBranch != "none" {
|
||||
onWorker(func() error {
|
||||
return self.GetBehindBaseBranchValuesForAllBranches(branches, mainBranches, renderFunc)
|
||||
})
|
||||
}
|
||||
|
||||
return branches, nil
|
||||
}
|
||||
|
||||
func (self *BranchLoader) GetBehindBaseBranchValuesForAllBranches(
|
||||
branches []*models.Branch,
|
||||
mainBranches *MainBranches,
|
||||
renderFunc func(),
|
||||
) error {
|
||||
mainBranchRefs := mainBranches.Get()
|
||||
if len(mainBranchRefs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
errg := errgroup.Group{}
|
||||
|
||||
for _, branch := range branches {
|
||||
errg.Go(func() error {
|
||||
baseBranch, err := self.GetBaseBranch(branch, mainBranches)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
behind := 0 // prime it in case something below fails
|
||||
if baseBranch != "" {
|
||||
output, err := self.cmd.New(
|
||||
NewGitCmd("rev-list").
|
||||
Arg("--left-right").
|
||||
Arg("--count").
|
||||
Arg(fmt.Sprintf("%s...%s", branch.FullRefName(), baseBranch)).
|
||||
ToArgv(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The format of the output is "<ahead>\t<behind>"
|
||||
aheadBehindStr := strings.Split(strings.TrimSpace(output), "\t")
|
||||
if len(aheadBehindStr) == 2 {
|
||||
if value, err := strconv.Atoi(aheadBehindStr[1]); err == nil {
|
||||
behind = value
|
||||
}
|
||||
}
|
||||
}
|
||||
branch.BehindBaseBranch.Store(int32(behind))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
err := errg.Wait()
|
||||
self.Log.Debugf("time to get behind base branch values for all branches: %s", time.Since(t))
|
||||
renderFunc()
|
||||
return err
|
||||
}
|
||||
|
||||
// Find the base branch for the given branch (i.e. the main branch that the
|
||||
// given branch was forked off of)
|
||||
//
|
||||
// Note that this function may return an empty string even if the returned error
|
||||
// is nil, e.g. when none of the configured main branches exist. This is not
|
||||
// considered an error condition, so callers need to check both the returned
|
||||
// error and whether the returned base branch is empty (and possibly react
|
||||
// differently in both cases).
|
||||
func (self *BranchLoader) GetBaseBranch(branch *models.Branch, mainBranches *MainBranches) (string, error) {
|
||||
mergeBase := mainBranches.GetMergeBase(branch.FullRefName())
|
||||
if mergeBase == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
output, err := self.cmd.New(
|
||||
NewGitCmd("for-each-ref").
|
||||
Arg("--contains").
|
||||
Arg(mergeBase).
|
||||
Arg("--format=%(refname)").
|
||||
Arg(mainBranches.Get()...).
|
||||
ToArgv(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
trimmedOutput := strings.TrimSpace(output)
|
||||
split := strings.Split(trimmedOutput, "\n")
|
||||
if len(split) == 0 || split[0] == "" {
|
||||
return "", nil
|
||||
}
|
||||
return split[0], nil
|
||||
}
|
||||
|
||||
func (self *BranchLoader) obtainBranches(canUsePushTrack bool) []*models.Branch {
|
||||
output, err := self.getRawBranches()
|
||||
if err != nil {
|
||||
|
||||
@@ -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 ""
|
||||
@@ -189,7 +189,7 @@ type Author struct {
|
||||
|
||||
func (self *CommitCommands) GetCommitAuthor(commitHash string) (Author, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitHash).
|
||||
Arg("--no-patch", "--pretty=format:%an%x00%ae", commitHash).
|
||||
ToArgv()
|
||||
|
||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
@@ -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()
|
||||
|
||||
@@ -35,11 +35,6 @@ type CommitLoader struct {
|
||||
readFile func(filename string) ([]byte, error)
|
||||
walkFiles func(root string, fn filepath.WalkFunc) error
|
||||
dotGitDir string
|
||||
// List of main branches that exist in the repo.
|
||||
// We use these to obtain the merge base of the branch.
|
||||
// When nil, we're yet to obtain the list of existing main branches.
|
||||
// When an empty slice, we've obtained the list and it's empty.
|
||||
mainBranches []string
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
@@ -56,7 +51,6 @@ func NewCommitLoader(
|
||||
getRebaseMode: getRebaseMode,
|
||||
readFile: os.ReadFile,
|
||||
walkFiles: filepath.Walk,
|
||||
mainBranches: nil,
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
}
|
||||
@@ -72,6 +66,7 @@ type GetCommitsOptions struct {
|
||||
All bool
|
||||
// If non-empty, show divergence from this ref (left-right log)
|
||||
RefToShowDivergenceFrom string
|
||||
MainBranches *MainBranches
|
||||
}
|
||||
|
||||
// GetCommits obtains the commits of the current branch
|
||||
@@ -108,9 +103,9 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||
go utils.Safe(func() {
|
||||
defer wg.Done()
|
||||
|
||||
ancestor = self.getMergeBase(opts.RefName)
|
||||
ancestor = opts.MainBranches.GetMergeBase(opts.RefName)
|
||||
if opts.RefToShowDivergenceFrom != "" {
|
||||
remoteAncestor = self.getMergeBase(opts.RefToShowDivergenceFrom)
|
||||
remoteAncestor = opts.MainBranches.GetMergeBase(opts.RefToShowDivergenceFrom)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -349,6 +344,8 @@ func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) []*mod
|
||||
for _, t := range todos {
|
||||
if t.Command == todo.UpdateRef {
|
||||
t.Msg = t.Ref
|
||||
} else if t.Command == todo.Exec {
|
||||
t.Msg = t.ExecCommand
|
||||
} else if t.Commit == "" {
|
||||
// Command does not have a commit associated, skip
|
||||
continue
|
||||
@@ -471,82 +468,6 @@ func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getMergeBase(refName string) string {
|
||||
if self.mainBranches == nil {
|
||||
self.mainBranches = self.getExistingMainBranches()
|
||||
}
|
||||
|
||||
if len(self.mainBranches) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// We pass all configured main branches to the merge-base call; git will
|
||||
// return the base commit for the closest one.
|
||||
|
||||
output, err := self.cmd.New(
|
||||
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
|
||||
ToArgv(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
// If there's an error, it must be because one of the main branches that
|
||||
// used to exist when we called getExistingMainBranches() was deleted
|
||||
// meanwhile. To fix this for next time, throw away our cache.
|
||||
self.mainBranches = nil
|
||||
}
|
||||
return ignoringWarnings(output)
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getExistingMainBranches() []string {
|
||||
var existingBranches []string
|
||||
var wg sync.WaitGroup
|
||||
|
||||
mainBranches := self.UserConfig.Git.MainBranches
|
||||
existingBranches = make([]string, len(mainBranches))
|
||||
|
||||
for i, branchName := range mainBranches {
|
||||
wg.Add(1)
|
||||
go utils.Safe(func() {
|
||||
defer wg.Done()
|
||||
|
||||
// Try to determine upstream of local main branch
|
||||
if ref, err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
|
||||
).DontLog().RunWithOutput(); err == nil {
|
||||
existingBranches[i] = strings.TrimSpace(ref)
|
||||
return
|
||||
}
|
||||
|
||||
// If this failed, a local branch for this main branch doesn't exist or it
|
||||
// has no upstream configured. Try looking for one in the "origin" remote.
|
||||
ref := "refs/remotes/origin/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
return
|
||||
}
|
||||
|
||||
// If this failed as well, try if we have the main branch as a local
|
||||
// branch. This covers the case where somebody is using git locally
|
||||
// for something, but never pushing anywhere.
|
||||
ref = "refs/heads/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
|
||||
return branch != ""
|
||||
})
|
||||
|
||||
return existingBranches
|
||||
}
|
||||
|
||||
func ignoringWarnings(commandOutput string) string {
|
||||
trimmedOutput := strings.TrimSpace(commandOutput)
|
||||
split := strings.Split(trimmedOutput, "\n")
|
||||
|
||||
@@ -307,10 +307,11 @@ func TestGetCommits(t *testing.T) {
|
||||
common := utils.NewDummyCommon()
|
||||
common.AppState = &config.AppState{}
|
||||
common.AppState.GitLogOrder = scenario.logOrder
|
||||
cmd := oscommands.NewDummyCmdObjBuilder(scenario.runner)
|
||||
|
||||
builder := &CommitLoader{
|
||||
Common: common,
|
||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||
cmd: cmd,
|
||||
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
|
||||
dotGitDir: ".git",
|
||||
readFile: func(filename string) ([]byte, error) {
|
||||
@@ -321,8 +322,10 @@ func TestGetCommits(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
common.UserConfig.Git.MainBranches = scenario.mainBranches
|
||||
commits, err := builder.GetCommits(scenario.opts)
|
||||
common.UserConfig().Git.MainBranches = scenario.mainBranches
|
||||
opts := scenario.opts
|
||||
opts.MainBranches = NewMainBranches(common, cmd)
|
||||
commits, err := builder.GetCommits(opts)
|
||||
|
||||
assert.Equal(t, scenario.expectedCommits, commits)
|
||||
assert.Equal(t, scenario.expectedError, err)
|
||||
|
||||
@@ -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 uint64
|
||||
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" },
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"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 {
|
||||
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(
|
||||
cmn *common.Common,
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
) *MainBranches {
|
||||
return &MainBranches{
|
||||
c: cmn,
|
||||
existingMainBranches: nil,
|
||||
cmd: cmd,
|
||||
mutex: &deadlock.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
// Get the list of main branches that exist in the repository. This is a list of
|
||||
// full ref names.
|
||||
func (self *MainBranches) Get() []string {
|
||||
self.mutex.Lock()
|
||||
defer self.mutex.Unlock()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Return the merge base of the given refName with the closest main branch.
|
||||
func (self *MainBranches) GetMergeBase(refName string) string {
|
||||
mainBranches := self.Get()
|
||||
if len(mainBranches) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// We pass all existing main branches to the merge-base call; git will
|
||||
// return the base commit for the closest one.
|
||||
|
||||
// We ignore errors from this call, since we can't distinguish whether the
|
||||
// error is because one of the main branches has been deleted since the last
|
||||
// call to determineMainBranches, or because the refName has no common
|
||||
// history with any of the main branches. Since the former should happen
|
||||
// very rarely, users must quit and restart lazygit to fix it; the latter is
|
||||
// also not very common, but can totally happen and is not an error.
|
||||
|
||||
output, _ := self.cmd.New(
|
||||
NewGitCmd("merge-base").Arg(refName).Arg(mainBranches...).
|
||||
ToArgv(),
|
||||
).DontLog().RunWithOutput()
|
||||
return ignoringWarnings(output)
|
||||
}
|
||||
|
||||
func (self *MainBranches) determineMainBranches(configuredMainBranches []string) []string {
|
||||
var existingBranches []string
|
||||
var wg sync.WaitGroup
|
||||
|
||||
existingBranches = make([]string, len(configuredMainBranches))
|
||||
|
||||
for i, branchName := range configuredMainBranches {
|
||||
wg.Add(1)
|
||||
go utils.Safe(func() {
|
||||
defer wg.Done()
|
||||
|
||||
// Try to determine upstream of local main branch
|
||||
if ref, err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
|
||||
).DontLog().RunWithOutput(); err == nil {
|
||||
existingBranches[i] = strings.TrimSpace(ref)
|
||||
return
|
||||
}
|
||||
|
||||
// If this failed, a local branch for this main branch doesn't exist or it
|
||||
// has no upstream configured. Try looking for one in the "origin" remote.
|
||||
ref := "refs/remotes/origin/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
return
|
||||
}
|
||||
|
||||
// If this failed as well, try if we have the main branch as a local
|
||||
// branch. This covers the case where somebody is using git locally
|
||||
// for something, but never pushing anywhere.
|
||||
ref = "refs/heads/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
|
||||
return branch != ""
|
||||
})
|
||||
|
||||
return existingBranches
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -67,42 +66,47 @@ func (self *RebaseCommands) RewordCommitInEditor(commits []*models.Commit, index
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, index int) error {
|
||||
return self.GenericAmend(commits, index, func() error {
|
||||
func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, start, end int) error {
|
||||
return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
|
||||
return self.commit.ResetAuthor()
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, index int, value string) error {
|
||||
return self.GenericAmend(commits, index, func() error {
|
||||
func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, start, end int, value string) error {
|
||||
return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
|
||||
return self.commit.SetAuthor(value)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, index int, value string) error {
|
||||
return self.GenericAmend(commits, index, func() error {
|
||||
return self.commit.AddCoAuthor(commits[index].Hash, value)
|
||||
func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, start, end int, value string) error {
|
||||
return self.GenericAmend(commits, start, end, func(commit *models.Commit) error {
|
||||
return self.commit.AddCoAuthor(commit.Hash, value)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f func() error) error {
|
||||
if models.IsHeadCommit(commits, index) {
|
||||
func (self *RebaseCommands) GenericAmend(commits []*models.Commit, start, end int, f func(commit *models.Commit) error) error {
|
||||
if start == end && models.IsHeadCommit(commits, start) {
|
||||
// we've selected the top commit so no rebase is required
|
||||
return f()
|
||||
return f(commits[start])
|
||||
}
|
||||
|
||||
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
|
||||
err := self.BeginInteractiveRebaseForCommitRange(commits, start, end, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// now the selected commit should be our head so we'll amend it
|
||||
err = f()
|
||||
if err != nil {
|
||||
return err
|
||||
for commitIndex := end; commitIndex >= start; commitIndex-- {
|
||||
err = f(commits[commitIndex])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.ContinueRebase(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return self.ContinueRebase()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveCommitsDown(commits []*models.Commit, startIdx int, endIdx int) error {
|
||||
@@ -280,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]
|
||||
@@ -288,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
|
||||
}
|
||||
@@ -298,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()
|
||||
}
|
||||
|
||||
@@ -381,7 +401,13 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
|
||||
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
|
||||
commits []*models.Commit, commitIndex int, keepCommitsThatBecomeEmpty bool,
|
||||
) error {
|
||||
if len(commits)-1 < commitIndex {
|
||||
return self.BeginInteractiveRebaseForCommitRange(commits, commitIndex, commitIndex, keepCommitsThatBecomeEmpty)
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange(
|
||||
commits []*models.Commit, start, end int, keepCommitsThatBecomeEmpty bool,
|
||||
) error {
|
||||
if len(commits)-1 < end {
|
||||
return errors.New("index outside of range of commits")
|
||||
}
|
||||
|
||||
@@ -392,14 +418,17 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
|
||||
return errors.New(self.Tr.DisabledForGPG)
|
||||
}
|
||||
|
||||
changes := []daemon.ChangeTodoAction{{
|
||||
Hash: commits[commitIndex].Hash,
|
||||
NewAction: todo.Edit,
|
||||
}}
|
||||
changes := make([]daemon.ChangeTodoAction, 0, end-start)
|
||||
for commitIndex := end; commitIndex >= start; commitIndex-- {
|
||||
changes = append(changes, daemon.ChangeTodoAction{
|
||||
Hash: commits[commitIndex].Hash,
|
||||
NewAction: todo.Edit,
|
||||
})
|
||||
}
|
||||
self.os.LogCommand(logTodoChanges(changes), false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
|
||||
baseHashOrRoot: getBaseHashOrRoot(commits, end+1),
|
||||
overrideEditor: true,
|
||||
keepCommitsThatBecomeEmpty: keepCommitsThatBecomeEmpty,
|
||||
instruction: daemon.NewChangeTodoActionsInstruction(changes),
|
||||
|
||||
@@ -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 uint64
|
||||
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) {
|
||||
|
||||
@@ -19,6 +19,7 @@ func NewSyncCommands(gitCommon *GitCommon) *SyncCommands {
|
||||
// Push pushes to a branch
|
||||
type PushOpts struct {
|
||||
Force bool
|
||||
ForceWithLease bool
|
||||
UpstreamRemote string
|
||||
UpstreamBranch string
|
||||
SetUpstream bool
|
||||
@@ -30,10 +31,11 @@ func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (oscommands
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("push").
|
||||
ArgIf(opts.Force, "--force-with-lease").
|
||||
ArgIf(opts.Force, "--force").
|
||||
ArgIf(opts.ForceWithLease, "--force-with-lease").
|
||||
ArgIf(opts.SetUpstream, "--set-upstream").
|
||||
ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote).
|
||||
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
|
||||
ArgIf(opts.UpstreamBranch != "", "HEAD:"+opts.UpstreamBranch).
|
||||
ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task)
|
||||
@@ -58,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)
|
||||
@@ -70,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()
|
||||
@@ -93,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()
|
||||
|
||||
@@ -110,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()
|
||||
|
||||
@@ -18,62 +18,70 @@ func TestSyncPush(t *testing.T) {
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "Push with force disabled",
|
||||
opts: PushOpts{Force: false},
|
||||
opts: PushOpts{ForceWithLease: false},
|
||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push"})
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Push with force-with-lease enabled",
|
||||
opts: PushOpts{ForceWithLease: true},
|
||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Push with force enabled",
|
||||
opts: PushOpts{Force: true},
|
||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force"})
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Push with force disabled, upstream supplied",
|
||||
opts: PushOpts{
|
||||
Force: false,
|
||||
ForceWithLease: false,
|
||||
UpstreamRemote: "origin",
|
||||
UpstreamBranch: "master",
|
||||
},
|
||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "master"})
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "HEAD:master"})
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Push with force disabled, setting upstream",
|
||||
opts: PushOpts{
|
||||
Force: false,
|
||||
ForceWithLease: false,
|
||||
UpstreamRemote: "origin",
|
||||
UpstreamBranch: "master",
|
||||
SetUpstream: true,
|
||||
},
|
||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "master"})
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "HEAD:master"})
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Push with force enabled, setting upstream",
|
||||
testName: "Push with force-with-lease enabled, setting upstream",
|
||||
opts: PushOpts{
|
||||
Force: true,
|
||||
ForceWithLease: true,
|
||||
UpstreamRemote: "origin",
|
||||
UpstreamBranch: "master",
|
||||
SetUpstream: true,
|
||||
},
|
||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"})
|
||||
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "HEAD:master"})
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Push with remote branch but no origin",
|
||||
opts: PushOpts{
|
||||
Force: true,
|
||||
ForceWithLease: true,
|
||||
UpstreamRemote: "",
|
||||
UpstreamBranch: "master",
|
||||
SetUpstream: true,
|
||||
@@ -125,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))
|
||||
})
|
||||
@@ -163,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 uint64
|
||||
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",
|
||||
}
|
||||
@@ -329,7 +352,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
reverse bool
|
||||
plain bool
|
||||
ignoreWhitespace bool
|
||||
contextSize int
|
||||
contextSize uint64
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package models
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Branch : A git branch
|
||||
// duplicating this for now
|
||||
@@ -32,6 +35,11 @@ type Branch struct {
|
||||
Subject string
|
||||
// commit hash
|
||||
CommitHash string
|
||||
|
||||
// How far we have fallen behind our base branch. 0 means either not
|
||||
// determined yet, or up to date with base branch. (We don't need to
|
||||
// distinguish the two, as we don't draw anything in both cases.)
|
||||
BehindBaseBranch atomic.Int32
|
||||
}
|
||||
|
||||
func (b *Branch) FullRefName() string {
|
||||
@@ -45,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() + "^"
|
||||
}
|
||||
@@ -104,7 +116,7 @@ func (b *Branch) IsBehindForPull() bool {
|
||||
}
|
||||
|
||||
func (b *Branch) IsBehindForPush() bool {
|
||||
return b.BehindForPush != "" && b.BehindForPush != "0"
|
||||
return b.RemoteBranchStoredLocally() && b.BehindForPush != "0"
|
||||
}
|
||||
|
||||
// for when we're in a detached head state
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+11
-1
@@ -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)
|
||||
}
|
||||
|
||||
+167
-77
@@ -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
|
||||
DiffContextSize uint64
|
||||
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.
|
||||
|
||||
+236
-185
@@ -19,12 +19,9 @@ type UserConfig struct {
|
||||
ConfirmOnQuit bool `yaml:"confirmOnQuit"`
|
||||
// If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close
|
||||
QuitOnTopLevelReturn bool `yaml:"quitOnTopLevelReturn"`
|
||||
// Keybindings
|
||||
Keybinding KeybindingConfig `yaml:"keybinding"`
|
||||
// Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc
|
||||
OS OSConfig `yaml:"os,omitempty"`
|
||||
// If true, don't display introductory popups upon opening Lazygit.
|
||||
// Lazygit sets this to true upon first runninng the program so that you don't see introductory popups every time you open the program.
|
||||
DisableStartupPopups bool `yaml:"disableStartupPopups"`
|
||||
// User-configured commands that can be invoked from within Lazygit
|
||||
CustomCommands []CustomCommand `yaml:"customCommands" jsonschema:"uniqueItems=true"`
|
||||
@@ -38,6 +35,8 @@ type UserConfig struct {
|
||||
NotARepository string `yaml:"notARepository" jsonschema:"enum=prompt,enum=create,enum=skip,enum=quit"`
|
||||
// If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.
|
||||
PromptToReturnFromSubprocess bool `yaml:"promptToReturnFromSubprocess"`
|
||||
// Keybindings
|
||||
Keybinding KeybindingConfig `yaml:"keybinding"`
|
||||
}
|
||||
|
||||
type RefresherConfig struct {
|
||||
@@ -78,6 +77,9 @@ type GuiConfig struct {
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth" jsonschema:"maximum=1,minimum=0"`
|
||||
// If true, increase the height of the focused side window; creating an accordion effect.
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
// The weight of the expanded side panel, relative to the other panels. 2 means
|
||||
// twice as tall as the other panels. Only relevant if `expandFocusedSidePanel` is true.
|
||||
ExpandedSidePanelWeight int `yaml:"expandedSidePanelWeight"`
|
||||
// Sometimes the main window is split in two (e.g. when the selected file has both staged and unstaged changes). This setting controls how the two sections are split.
|
||||
// Options are:
|
||||
// - 'horizontal': split the window horizontally
|
||||
@@ -123,10 +125,17 @@ 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.
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
// Whether to show the divergence from the base branch in the branches view.
|
||||
// One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
|
||||
ShowDivergenceFromBaseBranch string `yaml:"showDivergenceFromBaseBranch" jsonschema:"enum=none,enum=onlyArrow,enum=arrowAndNumber"`
|
||||
// Height of the command log view
|
||||
CommandLogSize int `yaml:"commandLogSize" jsonschema:"minimum=0"`
|
||||
// Whether to split the main window when viewing file changes.
|
||||
@@ -152,6 +161,12 @@ type GuiConfig struct {
|
||||
// Status panel view.
|
||||
// One of 'dashboard' (default) | 'allBranchesLog'
|
||||
StatusPanelView string `yaml:"statusPanelView" jsonschema:"enum=dashboard,enum=allBranchesLog"`
|
||||
// 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"`
|
||||
// If true, when using the panel jump keys (default 1 through 5) and target panel is already active, go to next tab instead
|
||||
SwitchTabsWithPanelJumpKeys bool `yaml:"switchTabsWithPanelJumpKeys"`
|
||||
}
|
||||
|
||||
func (c *GuiConfig) UseFuzzySearch() bool {
|
||||
@@ -170,6 +185,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
|
||||
@@ -213,10 +230,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
|
||||
@@ -225,6 +250,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"`
|
||||
@@ -252,7 +279,7 @@ type PagingConfig struct {
|
||||
// diff-so-fancy
|
||||
// delta --dark --paging=never
|
||||
// ydiff -p cat -s --wrap --width={{columnWidth}}
|
||||
Pager PagerType `yaml:"pager" jsonschema:"minLength=1"`
|
||||
Pager PagerType `yaml:"pager"`
|
||||
// If true, Lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
|
||||
UseConfig bool `yaml:"useConfig"`
|
||||
// e.g. 'difft --color=always'
|
||||
@@ -274,6 +301,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 {
|
||||
@@ -294,9 +323,9 @@ type LogConfig struct {
|
||||
|
||||
type CommitPrefixConfig struct {
|
||||
// pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*"
|
||||
Pattern string `yaml:"pattern" jsonschema:"example=^\\w+\\/(\\w+-\\w+).*,minLength=1"`
|
||||
Pattern string `yaml:"pattern" jsonschema:"example=^\\w+\\/(\\w+-\\w+).*"`
|
||||
// Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] "
|
||||
Replace string `yaml:"replace" jsonschema:"example=[$1] ,minLength=1"`
|
||||
Replace string `yaml:"replace" jsonschema:"example=[$1]"`
|
||||
}
|
||||
|
||||
type UpdateConfig struct {
|
||||
@@ -323,73 +352,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 {
|
||||
@@ -521,7 +552,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}}".
|
||||
@@ -554,8 +585,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 {
|
||||
@@ -581,6 +616,8 @@ type CustomCommand struct {
|
||||
Stream bool `yaml:"stream"`
|
||||
// If true, show the command's output in a popup within Lazygit
|
||||
ShowOutput bool `yaml:"showOutput"`
|
||||
// The title to display in the popup panel if showOutput is true. If left unset, the command will be used as the title.
|
||||
OutputTitle string `yaml:"outputTitle"`
|
||||
// Actions to take after the command has completed
|
||||
After CustomCommandAfterHook `yaml:"after"`
|
||||
}
|
||||
@@ -650,49 +687,58 @@ func GetDefaultConfig() *UserConfig {
|
||||
SkipStashWarning: false,
|
||||
SidePanelWidth: 0.3333,
|
||||
ExpandFocusedSidePanel: false,
|
||||
ExpandedSidePanelWeight: 2,
|
||||
MainPanelSplitMode: "flexible",
|
||||
EnlargedSideViewLocation: "left",
|
||||
Language: "auto",
|
||||
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,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowPanelJumps: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ShowFileIcons: true,
|
||||
CommitHashLength: 8,
|
||||
ShowBranchCommitHash: false,
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "rounded",
|
||||
AnimateExplosion: true,
|
||||
PortraitMode: "auto",
|
||||
FilterMode: "substring",
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowPanelJumps: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ShowFileIcons: true,
|
||||
CommitAuthorShortLength: 2,
|
||||
CommitAuthorLongLength: 17,
|
||||
CommitHashLength: 8,
|
||||
ShowBranchCommitHash: false,
|
||||
ShowDivergenceFromBaseBranch: "none",
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
WindowSize: "normal",
|
||||
Border: "rounded",
|
||||
AnimateExplosion: true,
|
||||
PortraitMode: "auto",
|
||||
FilterMode: "substring",
|
||||
Spinner: SpinnerConfig{
|
||||
Frames: []string{"|", "/", "-", "\\"},
|
||||
Rate: 50,
|
||||
},
|
||||
StatusPanelView: "dashboard",
|
||||
StatusPanelView: "dashboard",
|
||||
SwitchToFilesAfterStashPop: true,
|
||||
SwitchToFilesAfterStashApply: true,
|
||||
SwitchTabsWithPanelJumpKeys: false,
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
@@ -707,8 +753,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",
|
||||
@@ -720,10 +767,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,
|
||||
},
|
||||
@@ -735,77 +784,85 @@ func GetDefaultConfig() *UserConfig {
|
||||
Method: "prompt",
|
||||
Days: 14,
|
||||
},
|
||||
ConfirmOnQuit: false,
|
||||
QuitOnTopLevelReturn: false,
|
||||
ConfirmOnQuit: false,
|
||||
QuitOnTopLevelReturn: false,
|
||||
OS: OSConfig{},
|
||||
DisableStartupPopups: false,
|
||||
CustomCommands: []CustomCommand(nil),
|
||||
Services: map[string]string(nil),
|
||||
NotARepository: "prompt",
|
||||
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",
|
||||
@@ -903,11 +960,5 @@ func GetDefaultConfig() *UserConfig {
|
||||
CommitMenu: "<c-o>",
|
||||
},
|
||||
},
|
||||
OS: OSConfig{},
|
||||
DisableStartupPopups: false,
|
||||
CustomCommands: []CustomCommand(nil),
|
||||
Services: map[string]string(nil),
|
||||
NotARepository: "prompt",
|
||||
PromptToReturnFromSubprocess: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,12 @@ import (
|
||||
)
|
||||
|
||||
func (config *UserConfig) Validate() error {
|
||||
if err := validateEnum("gui.statusPanelView", config.Gui.StatusPanelView, []string{"dashboard", "allBranchesLog"}); err != nil {
|
||||
if err := validateEnum("gui.statusPanelView", config.Gui.StatusPanelView,
|
||||
[]string{"dashboard", "allBranchesLog"}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateEnum("gui.showDivergenceFromBaseBranch", config.Gui.ShowDivergenceFromBaseBranch,
|
||||
[]string{"none", "onlyArrow", "arrowAndNumber"}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
+28
-3
@@ -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)
|
||||
|
||||
+47
-65
@@ -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,20 @@ func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
}
|
||||
|
||||
if !c.IsFocusable() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
gui.resetHelpersAndControllers()
|
||||
|
||||
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 +141,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 +157,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 +178,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 +210,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 +287,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 +360,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 {
|
||||
|
||||
@@ -14,6 +14,7 @@ type ConfirmationContext struct {
|
||||
type ConfirmationContextState struct {
|
||||
OnConfirm func() error
|
||||
OnClose func() error
|
||||
Multiline bool
|
||||
}
|
||||
|
||||
var _ types.Context = (*ConfirmationContext)(nil)
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user