mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 21:46:49 -04:00
Compare commits
397
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6acac9efc2 | ||
|
|
06e0acb338 | ||
|
|
50bd95be88 | ||
|
|
c96b70c160 | ||
|
|
8773a2b880 | ||
|
|
7f8c08cd5d | ||
|
|
91cb1ff29a | ||
|
|
28d10c26a4 | ||
|
|
2c321011db | ||
|
|
dbd407c01d | ||
|
|
b8d5e481bb | ||
|
|
2b3525bfd6 | ||
|
|
b2c46c33b6 | ||
|
|
c44231a7d7 | ||
|
|
3518ec9f72 | ||
|
|
ec19fcf134 | ||
|
|
2d193cb296 | ||
|
|
64cd7cd9f6 | ||
|
|
eaaf123238 | ||
|
|
1c5fe8ff17 | ||
|
|
49ca7f6a84 | ||
|
|
80c212faba | ||
|
|
ae53059ed2 | ||
|
|
13829d8ff7 | ||
|
|
53b1e12110 | ||
|
|
009062534e | ||
|
|
33e81f717d | ||
|
|
ef718f3386 | ||
|
|
bf9339557e | ||
|
|
ebfc7ff7c6 | ||
|
|
928e76a82f | ||
|
|
9de8d17d84 | ||
|
|
9776be3131 | ||
|
|
7f7d9b166f | ||
|
|
3dd2e25dfa | ||
|
|
9790a7e00c | ||
|
|
a5c6a6120d | ||
|
|
bf9f9b6c04 | ||
|
|
d520d5e404 | ||
|
|
3241a9c251 | ||
|
|
4c4a2c3ff4 | ||
|
|
508cdb40a8 | ||
|
|
ae459b2377 | ||
|
|
c834e132c7 | ||
|
|
5e9ba82d7b | ||
|
|
fc78082e81 | ||
|
|
d7d5733a71 | ||
|
|
5240b2862f | ||
|
|
e1c18226bf | ||
|
|
c4e59aea66 | ||
|
|
75311750c8 | ||
|
|
16a158a025 | ||
|
|
621229bb09 | ||
|
|
536ef82866 | ||
|
|
54680e0836 | ||
|
|
fc69945a80 | ||
|
|
69a048c0ff | ||
|
|
6c6d835d50 | ||
|
|
2a72e96011 | ||
|
|
a507c836b2 | ||
|
|
8b90cca521 | ||
|
|
3573289452 | ||
|
|
87b54a4107 | ||
|
|
1b0d3829f8 | ||
|
|
aad3dc42a7 | ||
|
|
f884cc2af9 | ||
|
|
f4c8287143 | ||
|
|
03d7bc854e | ||
|
|
fdf1643f63 | ||
|
|
d93be027b6 | ||
|
|
fd5be11370 | ||
|
|
1e1fba4ce6 | ||
|
|
b174791490 | ||
|
|
a4ecd293f9 | ||
|
|
5fac40c129 | ||
|
|
ec410b2ae6 | ||
|
|
3a30211099 | ||
|
|
ec92d92bf4 | ||
|
|
87d5da511e | ||
|
|
e29ddf46d2 | ||
|
|
15288b7bf4 | ||
|
|
2828fb94fb | ||
|
|
5213a9de32 | ||
|
|
b7444b9a49 | ||
|
|
5d3b3c6656 | ||
|
|
65a28c4c3b | ||
|
|
1f2cb35cc9 | ||
|
|
2b49865d0d | ||
|
|
da47498066 | ||
|
|
2f1564d288 | ||
|
|
3610f13418 | ||
|
|
2417b70acd | ||
|
|
b61c395278 | ||
|
|
a9ef69b9c7 | ||
|
|
cf27974ea3 | ||
|
|
83356d441f | ||
|
|
49c50fc95c | ||
|
|
b719dc4d8e | ||
|
|
75121384a3 | ||
|
|
078445db63 | ||
|
|
64eb3d560b | ||
|
|
2823a7cff0 | ||
|
|
d5f2fb6003 | ||
|
|
bd0d9ef259 | ||
|
|
75d2fb1df2 | ||
|
|
13e9e1de83 | ||
|
|
93a37cf83e | ||
|
|
0f269183de | ||
|
|
4268701606 | ||
|
|
799827ee0e | ||
|
|
4a7cf6040e | ||
|
|
f455f99705 | ||
|
|
f3a5c184e1 | ||
|
|
1543b83d10 | ||
|
|
4cfeb18632 | ||
|
|
3a4422fc68 | ||
|
|
6da42b07cd | ||
|
|
954347c5bd | ||
|
|
7fb9e8fa9a | ||
|
|
5cca4c7063 | ||
|
|
53dc23a037 | ||
|
|
debfe1a21f | ||
|
|
d84986880e | ||
|
|
17bb3970c1 | ||
|
|
016d46526c | ||
|
|
0766b14afd | ||
|
|
4624d496a2 | ||
|
|
ea03ae5ee3 | ||
|
|
51e5816dd7 | ||
|
|
c1b4201726 | ||
|
|
0b0910573b | ||
|
|
92bce7de43 | ||
|
|
e98cc4d016 | ||
|
|
2ffd52acd1 | ||
|
|
24e98d1792 | ||
|
|
a50712b6c2 | ||
|
|
64cebfc0a8 | ||
|
|
b07109de4d | ||
|
|
59303981f9 | ||
|
|
62e31efd0f | ||
|
|
f6f2a52dee | ||
|
|
579053d5a3 | ||
|
|
10db72d223 | ||
|
|
c3cf48cc49 | ||
|
|
949e131ebe | ||
|
|
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 |
@@ -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
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
* [ ] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
|
* [ ] 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)
|
* [ ] 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))
|
* [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
|
||||||
|
* [ ] If a new UserConfig entry was added, make sure it can be hot-reloaded (see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
|
||||||
* [ ] Docs have been updated if necessary
|
* [ ] Docs have been updated if necessary
|
||||||
* [ ] You've read through your own file changes for silly mistakes etc
|
* [ ] You've read through your own file changes for silly mistakes etc
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ changelog:
|
|||||||
- title: I18n 🌎
|
- title: I18n 🌎
|
||||||
labels:
|
labels:
|
||||||
- i18n
|
- i18n
|
||||||
|
- title: Performance Improvements 📊
|
||||||
|
labels:
|
||||||
|
- performance
|
||||||
- title: Other Changes
|
- title: Other Changes
|
||||||
labels:
|
labels:
|
||||||
- "*"
|
- "*"
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
mkdir -p /tmp/code_coverage
|
mkdir -p /tmp/code_coverage
|
||||||
go test ./... -short -cover -args "-test.gocoverdir=/tmp/code_coverage"
|
go test ./... -short -cover -args "-test.gocoverdir=/tmp/code_coverage"
|
||||||
- name: Upload code coverage artifacts
|
- name: Upload code coverage artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: coverage-unit-${{ matrix.os }}-${{ github.run_id }}
|
name: coverage-unit-${{ matrix.os }}-${{ github.run_id }}
|
||||||
path: /tmp/code_coverage
|
path: /tmp/code_coverage
|
||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
mkdir -p /tmp/code_coverage
|
mkdir -p /tmp/code_coverage
|
||||||
./scripts/run_integration_tests.sh
|
./scripts/run_integration_tests.sh
|
||||||
- name: Upload code coverage artifacts
|
- name: Upload code coverage artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: coverage-integration-${{ matrix.git-version }}-${{ github.run_id }}
|
name: coverage-integration-${{ matrix.git-version }}-${{ github.run_id }}
|
||||||
path: /tmp/code_coverage
|
path: /tmp/code_coverage
|
||||||
@@ -170,9 +170,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: 1.22.x
|
go-version: 1.22.x
|
||||||
- name: Lint
|
- name: Lint
|
||||||
uses: golangci/golangci-lint-action@v3.7.0
|
uses: golangci/golangci-lint-action@v6.1.0
|
||||||
with:
|
with:
|
||||||
version: v1.58
|
version: v1.60
|
||||||
- name: errors
|
- name: errors
|
||||||
run: golangci-lint run
|
run: golangci-lint run
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
@@ -184,7 +184,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
mode: exactly
|
mode: exactly
|
||||||
count: 1
|
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:
|
upload-coverage:
|
||||||
# List all jobs that produce coverage files
|
# List all jobs that produce coverage files
|
||||||
needs: [unit-tests, integration-tests]
|
needs: [unit-tests, integration-tests]
|
||||||
@@ -200,7 +200,7 @@ jobs:
|
|||||||
go-version: 1.22.x
|
go-version: 1.22.x
|
||||||
|
|
||||||
- name: Download all coverage artifacts
|
- name: Download all coverage artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/code_coverage
|
path: /tmp/code_coverage
|
||||||
|
|
||||||
@@ -219,3 +219,22 @@ jobs:
|
|||||||
CODACY_PROJECT_TOKEN=${{ secrets.CODACY_PROJECT_TOKEN }} \
|
CODACY_PROJECT_TOKEN=${{ secrets.CODACY_PROJECT_TOKEN }} \
|
||||||
bash <(curl -Ls https://coverage.codacy.com/get.sh) report \
|
bash <(curl -Ls https://coverage.codacy.com/get.sh) report \
|
||||||
--force-coverage-parser go -r coverage.out
|
--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
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
name: Automated Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
# schedule:
|
||||||
|
# # Runs at 2:00 AM UTC on the first Saturday of every month
|
||||||
|
# - cron: '0 2 * * 6'
|
||||||
|
# workflow_dispatch: # Allow manual triggering of the workflow
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-and-release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# - name: Check for changes since last release
|
||||||
|
# run: |
|
||||||
|
# if [ -z "$(git diff --name-only ${{ env.latest_tag }})" ]; then
|
||||||
|
# echo "No changes detected since last release"
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
- name: Check for Blocking Issues/PRs
|
||||||
|
id: check_blocks
|
||||||
|
run: |
|
||||||
|
gh auth setup-git
|
||||||
|
gh auth status
|
||||||
|
|
||||||
|
echo "Checking for blocking issues and PRs..."
|
||||||
|
|
||||||
|
# Check for blocking issues
|
||||||
|
blocking_issues=$(gh issue list -l blocks-release --json number,title --jq '.[] | "- \(.title) (#\(.number))"')
|
||||||
|
|
||||||
|
# Check for blocking PRs
|
||||||
|
blocking_prs=$(gh pr list -l blocks-release --json number,title --jq '.[] | "- \(.title) (#\(.number)) (PR)"')
|
||||||
|
|
||||||
|
# Combine the results
|
||||||
|
blocking_items="$blocking_issues"$'\n'"$blocking_prs"
|
||||||
|
|
||||||
|
# Remove empty lines
|
||||||
|
blocking_items=$(echo "$blocking_items" | grep . || true)
|
||||||
|
|
||||||
|
if [ -n "$blocking_items" ]; then
|
||||||
|
echo "Blocking issues/PRs detected:"
|
||||||
|
echo "$blocking_items"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Calculate next version
|
||||||
|
run: |
|
||||||
|
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1) || echo "v0.0.0")
|
||||||
|
echo "Latest tag: $latest_tag"
|
||||||
|
IFS='.' read -r major minor patch <<< "$latest_tag"
|
||||||
|
new_minor=$((minor + 1))
|
||||||
|
new_tag="$major.$new_minor.0"
|
||||||
|
echo "New tag: $new_tag"
|
||||||
|
echo "new_tag=$new_tag" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Push New Tag
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git tag example-${{ env.new_tag }}
|
||||||
|
git push origin example-${{ env.new_tag }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
# Hidden
|
# Hidden
|
||||||
.*
|
.*
|
||||||
|
!.codespellrc
|
||||||
|
|
||||||
# Notes
|
# Notes
|
||||||
*.notes
|
*.notes
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
linters:
|
linters:
|
||||||
disable:
|
|
||||||
- structcheck # gives false positives
|
|
||||||
enable:
|
enable:
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- thelper
|
- thelper
|
||||||
- goimports
|
- goimports
|
||||||
- tparallel
|
- tparallel
|
||||||
- wastedassign
|
- wastedassign
|
||||||
- exportloopref
|
|
||||||
- unparam
|
- unparam
|
||||||
- prealloc
|
- prealloc
|
||||||
- unconvert
|
- unconvert
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ before making a change.
|
|||||||
|
|
||||||
[This video](https://www.youtube.com/watch?v=kNavnhzZHtk) walks through the process of adding a small feature to lazygit. If you have no idea where to start, watching that video is a good first step.
|
[This video](https://www.youtube.com/watch?v=kNavnhzZHtk) walks through the process of adding a small feature to lazygit. If you have no idea where to start, watching that video is a good first step.
|
||||||
|
|
||||||
|
## Design principles
|
||||||
|
|
||||||
|
See [here](./VISION.md) for a set of design principles that we want to consider when building a feature or making a change.
|
||||||
|
|
||||||
## Codebase guide
|
## Codebase guide
|
||||||
|
|
||||||
[This doc](./docs/dev/Codebase_Guide.md) explains:
|
[This doc](./docs/dev/Codebase_Guide.md) explains:
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Vision and Design Principles
|
||||||
|
|
||||||
|
## Vision
|
||||||
|
|
||||||
|
Lazygit's vision is to be the most enjoyable UI for git.
|
||||||
|
|
||||||
|
## Design Principles
|
||||||
|
|
||||||
|
There are seven (sometimes contradictory) design principles we follow:
|
||||||
|
|
||||||
|
- Dicoverability
|
||||||
|
- Simplicity
|
||||||
|
- Safety
|
||||||
|
- Power
|
||||||
|
- Speed
|
||||||
|
- Conformity with git
|
||||||
|
- Think of the codebase
|
||||||
|
|
||||||
|
### Discoverability
|
||||||
|
|
||||||
|
TUI's are notoriously hard to learn, thanks to limited screen real-estate to provide contextual help and a general lack of effort on the part of developers to make things obvious. We want Lazygit to buck the trend and be easy for a new user to grok.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- Clearly document all the features/configuration options
|
||||||
|
- e.g. gifs in the README
|
||||||
|
- Document how to solve various git problems with Lazygit
|
||||||
|
- This is something we don't have yet but should: a section in the docs explaining how Lazygit can help you in various scenarios
|
||||||
|
- Use tooltips to explain what actions will do
|
||||||
|
- Make it easy for users to ask questions and get answers from the community
|
||||||
|
- Make it easy to find entities and actions from within Lazygit
|
||||||
|
- Use visual elements to make things obvious
|
||||||
|
- e.g. '<-- YOU ARE HERE' label when rebasing
|
||||||
|
- Don't require the user to memorise keybindings
|
||||||
|
- e.g. when the user is mid-rebase, we prominently show that the keybinding for viewing rebase options is 'm'
|
||||||
|
- When the user performs an action in Lazygit, make the impact obvious
|
||||||
|
- If the affected entity isn't visible, show a toast notification
|
||||||
|
- If a keybinding is disabled, give a reason why
|
||||||
|
|
||||||
|
### Simplicity
|
||||||
|
|
||||||
|
The git CLI is very complex but most git use cases are simple. Lazygit needs to ensure that simple use cases are easy to satisfy.
|
||||||
|
|
||||||
|
- Make the most common use cases dead-simple (staging files, committing, pulling/pushing)
|
||||||
|
- Don't overwhelm the user with options
|
||||||
|
- Use sensible defaults
|
||||||
|
- We already have too many configuration options: think hard before adding any new ones
|
||||||
|
|
||||||
|
### Safety
|
||||||
|
|
||||||
|
It's easy to screw things up in git so Lazygit should try to protect the user from screwing things up.
|
||||||
|
|
||||||
|
- Prompt for a confirmation before doing anything that's hard to reverse
|
||||||
|
- Make it easy to correct mistakes
|
||||||
|
- e.g. undo action
|
||||||
|
- the escape key should get you out of most transient situations (rebasing, diffing, etc)
|
||||||
|
|
||||||
|
## Power
|
||||||
|
|
||||||
|
Users shouldn't have to drop down the CLI _too_ often. Lazygit should be able to handle some complex use cases.
|
||||||
|
|
||||||
|
- Make complex (but common) CLI flows simple
|
||||||
|
- e.g. interactive rebasing
|
||||||
|
- Use the custom commands system to handle the really rare complex edge-cases
|
||||||
|
|
||||||
|
### Speed
|
||||||
|
|
||||||
|
Pro users should be able to move at lightning speed with Lazygit.
|
||||||
|
|
||||||
|
- Always think about the number of keypresses involved in a given UX flow
|
||||||
|
- Make lazygit performant and responsive
|
||||||
|
- Think about the individual commands being run and how fast they are
|
||||||
|
- Startup should be FAST. If you want to run something at startup that is slow, make it non-blocking.
|
||||||
|
- Support muscle-memory
|
||||||
|
- Prefer disabling menu items instead of hiding them so that muscle memory can be used to select the desired menu item
|
||||||
|
- Try to make keybinding intuitions to transfer across contexts (e.g. 'd' for destroy)
|
||||||
|
- When changing keybindings in a new release, always consider what will happen if a user does not read the release notes and relies on muscle memory.
|
||||||
|
|
||||||
|
### Conformity with git
|
||||||
|
|
||||||
|
Satisfying the use-cases of git users is more important than perfectly conforming to git's API, but even obscure parts of git's API were motivated by real use-cases.
|
||||||
|
|
||||||
|
- Users should only have to drop down to the git CLI in rare circumstances
|
||||||
|
- Honour the git config
|
||||||
|
- Don't override anything set in the git config without the user's permission
|
||||||
|
- Work with git, not against it.
|
||||||
|
- Too much magic will get us into trouble
|
||||||
|
- Avoid storing Lazygit-specific session state that could instead be stored in git
|
||||||
|
- Ensure that Lazygit can represent the state of any repo
|
||||||
|
- Sometimes git's default behaviour is just silly and we'll make the call to override but it should be a well-considered decision.
|
||||||
|
|
||||||
|
### Think of the codebase
|
||||||
|
|
||||||
|
Will somebody PLEASE think of the codebase!
|
||||||
|
|
||||||
|
Some features are not worth the added complexity in the codebase. The more this codebase grows, the harder it will be to make the changes that everybody wants.
|
||||||
|
|
||||||
|
## Resolving conflicts
|
||||||
|
|
||||||
|
Many of the above objectives are directly antithetical to one another. If you add an extra confirmation prompt for the sake of _safety_, you're sacrificing _speed_. If you support toggling various git flags in the name of _power_, you're sacrificing _simplicity_. There are a few things to say here.
|
||||||
|
|
||||||
|
When there are conflicts, we need to make a judgement call. In general we should err on the side of safety and simplicity as the default, with the ability for users to make things faster / more powerful either through configuration or separate keybindings.
|
||||||
|
|
||||||
|
This does not mean for example that force pushes should be impossible without being manually enabled: force pushes are table stakes for anybody who rebases. But it does mean that a confirmation popup should appear when force pushing.
|
||||||
+65
-8
@@ -1,6 +1,6 @@
|
|||||||
# User Config
|
# User Config
|
||||||
|
|
||||||
Default path for the config file:
|
Default path for the global config file:
|
||||||
|
|
||||||
- Linux: `~/.config/lazygit/config.yml`
|
- Linux: `~/.config/lazygit/config.yml`
|
||||||
- MacOS: `~/Library/Application\ Support/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"`
|
- 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
|
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
|
```yaml
|
||||||
@@ -85,6 +87,11 @@ gui:
|
|||||||
# - 'top': split the window vertically (side panel on top, main view below)
|
# - 'top': split the window vertically (side panel on top, main view below)
|
||||||
enlargedSideViewLocation: left
|
enlargedSideViewLocation: left
|
||||||
|
|
||||||
|
# If true, wrap lines in the staging view to the width of the view. This
|
||||||
|
# makes it much easier to work with diffs that have long lines, e.g.
|
||||||
|
# paragraphs of markdown text.
|
||||||
|
wrapLinesInStagingView: true
|
||||||
|
|
||||||
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||||
language: auto
|
language: auto
|
||||||
|
|
||||||
@@ -162,6 +169,9 @@ gui:
|
|||||||
# This can be toggled from within Lazygit with the '~' key, but that will not change the default.
|
# This can be toggled from within Lazygit with the '~' key, but that will not change the default.
|
||||||
showFileTree: true
|
showFileTree: true
|
||||||
|
|
||||||
|
# If true, show the number of lines changed per file in the Files view
|
||||||
|
showNumstatInFilesView: false
|
||||||
|
|
||||||
# If true, show a random tip in the command log when Lazygit starts
|
# If true, show a random tip in the command log when Lazygit starts
|
||||||
showRandomTip: true
|
showRandomTip: true
|
||||||
|
|
||||||
@@ -209,9 +219,9 @@ gui:
|
|||||||
# If 'auto', only split the main window when a file has both staged and unstaged changes
|
# If 'auto', only split the main window when a file has both staged and unstaged changes
|
||||||
splitDiff: auto
|
splitDiff: auto
|
||||||
|
|
||||||
# Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).
|
# Default size for focused window. Can be changed from within Lazygit with '+' and '_' (but this won't change the default).
|
||||||
# One of: 'normal' (default) | 'half' | 'full'
|
# One of: 'normal' (default) | 'half' | 'full'
|
||||||
windowSize: normal
|
screenMode: normal
|
||||||
|
|
||||||
# Window border style.
|
# Window border style.
|
||||||
# One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
# One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
||||||
@@ -244,6 +254,15 @@ gui:
|
|||||||
# One of 'dashboard' (default) | 'allBranchesLog'
|
# One of 'dashboard' (default) | 'allBranchesLog'
|
||||||
statusPanelView: dashboard
|
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
|
# Config relating to git
|
||||||
git:
|
git:
|
||||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
|
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
|
||||||
@@ -303,11 +322,17 @@ git:
|
|||||||
# If true, pass the --all arg to git fetch
|
# If true, pass the --all arg to git fetch
|
||||||
fetchAll: true
|
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
|
# 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}} --
|
branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --
|
||||||
|
|
||||||
# Command used to display git log of all branches in the main window.
|
# Command used to display git log of all branches in the main window.
|
||||||
# Deprecated: User `allBranchesLogCmds` instead.
|
# Deprecated: Use `allBranchesLogCmds` instead.
|
||||||
allBranchesLogCmd: git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium
|
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
|
# If true, do not spawn a separate process when using GPG
|
||||||
@@ -495,7 +520,7 @@ keybinding:
|
|||||||
scrollDownMain-alt1: J
|
scrollDownMain-alt1: J
|
||||||
scrollUpMain-alt2: <c-u>
|
scrollUpMain-alt2: <c-u>
|
||||||
scrollDownMain-alt2: <c-d>
|
scrollDownMain-alt2: <c-d>
|
||||||
executeCustomCommand: ':'
|
executeShellCommand: ':'
|
||||||
createRebaseOptionsMenu: m
|
createRebaseOptionsMenu: m
|
||||||
|
|
||||||
# 'Files' appended for legacy reasons
|
# 'Files' appended for legacy reasons
|
||||||
@@ -634,6 +659,13 @@ os:
|
|||||||
open: 'open {{filename}}'
|
open: 'open {{filename}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Custom Command for Opening a Link
|
||||||
|
```yaml
|
||||||
|
os:
|
||||||
|
openLink: 'bash -C /path/to/your/shell-script.sh {{link}}'
|
||||||
|
```
|
||||||
|
Specify the external command to invoke when opening URL links (i.e. creating MR/PR in GitLab, BitBucket or GitHub). `{{link}}` will be replaced by the URL to be opened. A simple shell script can be used to further mangle the passed URL.
|
||||||
|
|
||||||
## Custom Command for Copying to and Pasting from Clipboard
|
## Custom Command for Copying to and Pasting from Clipboard
|
||||||
```yaml
|
```yaml
|
||||||
os:
|
os:
|
||||||
@@ -642,9 +674,26 @@ os:
|
|||||||
Specify an external command to invoke when copying to clipboard is requested. `{{text}` will be replaced by text to be copied. Default is to copy to system clipboard.
|
Specify an external command to invoke when copying to clipboard is requested. `{{text}` will be replaced by text to be copied. Default is to copy to system clipboard.
|
||||||
|
|
||||||
If you are working on a terminal that supports OSC52, the following command will let you take advantage of it:
|
If you are working on a terminal that supports OSC52, the following command will let you take advantage of it:
|
||||||
```
|
```yaml
|
||||||
os:
|
os:
|
||||||
copyToClipboardCmd: printf "\033]52;c;$(printf {{text}} | base64)\a" > /dev/tty
|
copyToClipboardCmd: printf "\033]52;c;$(printf {{text}} | base64 -w 0)\a" > /dev/tty
|
||||||
|
```
|
||||||
|
|
||||||
|
For tmux you need to wrap it with the [tmux escape sequence](https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it), and enable passthrough in tmux config with `set -g allow-passthrough on`:
|
||||||
|
```yaml
|
||||||
|
os:
|
||||||
|
copyToClipboardCmd: printf "\033Ptmux;\033\033]52;c;$(printf {{text}} | base64 -w 0)\a\033\\" > /dev/tty
|
||||||
|
```
|
||||||
|
|
||||||
|
For the best of both worlds, we can let the command determine if we are running in a tmux session and send the correct sequence:
|
||||||
|
```yaml
|
||||||
|
os:
|
||||||
|
copyToClipboardCmd: >
|
||||||
|
if [[ "$TERM" =~ ^(screen|tmux) ]]; then
|
||||||
|
printf "\033Ptmux;\033\033]52;c;$(printf {{text}} | base64 -w 0)\a\033\\" > /dev/tty
|
||||||
|
else
|
||||||
|
printf "\033]52;c;$(printf {{text}} | base64 -w 0)\a" > /dev/tty
|
||||||
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
A custom command for reading from the clipboard can be set using
|
A custom command for reading from the clipboard can be set using
|
||||||
@@ -665,7 +714,7 @@ os:
|
|||||||
editPreset: 'vscode'
|
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.
|
`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.
|
||||||
|
|
||||||
@@ -891,6 +940,14 @@ git:
|
|||||||
replace: '[$1] '
|
replace: '[$1] '
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The way golang regex works is when you use `$n` in the replacement string, where `n` is a number, it puts the nth captured subgroup at that place. If `n` is out of range because there aren't that many capture groups in the regex, it puts an empty string there.
|
||||||
|
>
|
||||||
|
> So make sure you are capturing group or groups in your regex.
|
||||||
|
>
|
||||||
|
> For example `^[A-Z]+-\d+$` won't work on branch name like BRANCH-1111
|
||||||
|
> But `^([A-Z]+-\d+)$` will
|
||||||
|
|
||||||
## Predefined branch name prefix
|
## 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.
|
In situations where certain naming pattern is used for branches, this can be used to populate new branch creation with a static prefix.
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ Custom command keybindings will appear alongside inbuilt keybindings when you vi
|
|||||||
For a given custom command, here are the allowed fields:
|
For a given custom command, here are the allowed fields:
|
||||||
| _field_ | _description_ | required |
|
| _field_ | _description_ | required |
|
||||||
|-----------------|----------------------|-|
|
|-----------------|----------------------|-|
|
||||||
| key | The key to trigger the command. Use a single letter or one of the values from [here](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md) | yes |
|
| key | The key to trigger the command. Use a single letter or one of the values from [here](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md). Custom commands without a key specified can be triggered by selecting them from the keybindings (`?`) menu | no |
|
||||||
| command | The command to run (using Go template syntax for placeholder values) | yes |
|
| command | The command to run (using Go template syntax for placeholder values) | yes |
|
||||||
| context | The context in which to listen for the key (see [below](#contexts)) | yes |
|
| context | The context in which to listen for the key (see [below](#contexts)) | yes |
|
||||||
| subprocess | Whether you want the command to run in a subprocess (e.g. if the command requires user input) | no |
|
| subprocess | Whether you want the command to run in a subprocess (e.g. if the command requires user input) | no |
|
||||||
@@ -87,6 +87,11 @@ The permitted contexts are:
|
|||||||
| stash | The 'Stash' tab |
|
| stash | The 'Stash' tab |
|
||||||
| global | This keybinding will take affect everywhere |
|
| 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
|
## Prompts
|
||||||
|
|
||||||
### Common fields
|
### Common fields
|
||||||
@@ -291,9 +296,7 @@ Here's an example using a command but not specifying anything else: so each line
|
|||||||
Your commands can contain placeholder strings using Go's [template syntax](https://jan.newmarch.name/golang/template/chapter-template.html). The template syntax is pretty powerful, letting you do things like conditionals if you want, but for the most part you'll simply want to be accessing the fields on the following objects:
|
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
|
SelectedCommit
|
||||||
SelectedReflogCommit
|
|
||||||
SelectedSubCommit
|
|
||||||
SelectedFile
|
SelectedFile
|
||||||
SelectedPath
|
SelectedPath
|
||||||
SelectedLocalBranch
|
SelectedLocalBranch
|
||||||
@@ -306,6 +309,9 @@ SelectedWorktree
|
|||||||
CheckedOutBranch
|
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).
|
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
|
## 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
|
## Diff-so-fancy
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## The use-case
|
## The use-case
|
||||||
|
|
||||||
This topic deserves its own doc because there there are a few touch points for it. We have a use-case for knowing when Lazygit is idle or busy because integration tests follow the following process:
|
This topic deserves its own doc because there are a few touch points for it. We have a use-case for knowing when Lazygit is idle or busy because integration tests follow the following process:
|
||||||
1) press a key
|
1) press a key
|
||||||
2) wait until Lazygit is idle
|
2) wait until Lazygit is idle
|
||||||
3) run assertion / press another key
|
3) run assertion / press another key
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Packages
|
## 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/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/cheatsheet`: Generates the keybinding cheatsheets in `docs/keybindings`.
|
||||||
* `pkg/commands/git_commands`: All communication to the git binary happens here. So for example there's a `Checkout` method which calls `git checkout`.
|
* `pkg/commands/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/models`: Contains model structs that represent commits, branches, files, etc.
|
||||||
* `pkg/commands/patch`: Contains code for parsing and working with git patches
|
* `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/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/constants`: Contains some constant strings (e.g. links to docs)
|
||||||
* `pkg/env`: Contains code relating to setting/getting environment variables
|
* `pkg/env`: Contains code relating to setting/getting environment variables
|
||||||
* `pkg/i18n`: Contains internationalised strings
|
* `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)`.
|
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
|
## 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).
|
Before we had controllers and contexts, all the code lived directly in the gui package under a gui God Struct. This was fairly bloated and so we split things out to have a better separation of concerns. Nonetheless, it's a big effort to migrate all the code so we still have some logic in the gui struct that ought to live somewhere else. Likewise, we have some keybindings defined in `pkg/gui/keybindings.go` that ought to live on a controller (all keybindings used to be defined in that one file).
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ is this:
|
|||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
+ "github.com/samber/lo"
|
+ "github.com/samber/lo"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -308,9 +309,5 @@ func (self *FixupHelper) blameAddedLines(addedLineHunks []*hunk) ([]string, erro
|
@@ -308,9 +309,5 @@ func (self *FixupHelper) blameAddedLines(addedLineHunks []*hunk) ([]string, error
|
||||||
func findCommit(hash string) (*models.Commit, int, bool) {
|
func findCommit(hash string) (*models.Commit, int, bool) {
|
||||||
- for i, commit := range self.c.Model().Commits {
|
- for i, commit := range self.c.Model().Commits {
|
||||||
- if commit.Hash == hash {
|
- if commit.Hash == hash {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` ( `` | Decrease rename similarity threshold | Decrease 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. |
|
| `` } `` | 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. |
|
| `` { `` | 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 | |
|
| `` <c-p> `` | View custom patch options | |
|
||||||
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
|
| `` 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`. |
|
| `` 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`. |
|
||||||
@@ -348,7 +348,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | Checkout | Checkout the selected tag tag as a detached HEAD. |
|
| `` <space> `` | Checkout | Checkout the selected tag as a detached HEAD. |
|
||||||
| `` n `` | New tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
| `` n `` | New tag | 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 `` | 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. |
|
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` ( `` | Decrease rename similarity threshold | Decrease 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. |
|
| `` } `` | 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. |
|
| `` { `` | 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 | |
|
| `` <c-p> `` | View custom patch options | |
|
||||||
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
|
| `` 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`. |
|
| `` 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`. |
|
||||||
@@ -180,7 +180,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | チェックアウト | Checkout the selected tag tag as a detached HEAD. |
|
| `` <space> `` | チェックアウト | Checkout the selected tag as a detached HEAD. |
|
||||||
| `` n `` | タグを作成 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
| `` 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 `` | 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. |
|
| `` P `` | タグをpush | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` ( `` | Decrease rename similarity threshold | Decrease 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 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기 | 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. |
|
| `` { `` | 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 옵션 보기 | |
|
| `` <c-p> `` | 커스텀 Patch 옵션 보기 | |
|
||||||
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
|
| `` 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`. |
|
| `` 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`. |
|
||||||
@@ -321,7 +321,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | 체크아웃 | Checkout the selected tag tag as a detached HEAD. |
|
| `` <space> `` | 체크아웃 | Checkout the selected tag as a detached HEAD. |
|
||||||
| `` n `` | 태그를 생성 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
| `` 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 `` | 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. |
|
| `` P `` | 태그를 push | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` ( `` | Decrease rename similarity threshold | Decrease 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. |
|
| `` } `` | 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. |
|
| `` { `` | 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 | |
|
| `` <c-p> `` | Bekijk aangepaste patch opties | |
|
||||||
| `` m `` | Bekijk merge/rebase opties | View options to abort/continue/skip the current merge/rebase. |
|
| `` 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`. |
|
| `` 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`. |
|
||||||
@@ -348,7 +348,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | Uitchecken | Checkout the selected tag tag as a detached HEAD. |
|
| `` <space> `` | Uitchecken | Checkout the selected tag as a detached HEAD. |
|
||||||
| `` n `` | Creëer tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
| `` n `` | Creëer tag | 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 `` | 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. |
|
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
|||||||
| `` ( `` | Decrease rename similarity threshold | Decrease 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. |
|
| `` } `` | 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. |
|
| `` { `` | 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 | |
|
| `` <c-p> `` | Wyświetl opcje niestandardowej łatki | |
|
||||||
| `` m `` | Pokaż opcje scalania/rebase | Pokaż opcje do przerwania/kontynuowania/pominięcia bieżącego scalania/rebase. |
|
| `` 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`. |
|
| `` 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`. |
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ _Связки клавиш_
|
|||||||
| `` ( `` | Decrease rename similarity threshold | Decrease 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. |
|
| `` } `` | Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | 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. |
|
| `` { `` | Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | 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> `` | Просмотреть пользовательские параметры патча | |
|
| `` <c-p> `` | Просмотреть пользовательские параметры патча | |
|
||||||
| `` m `` | Просмотреть параметры слияния/перебазирования | View options to abort/continue/skip the current merge/rebase. |
|
| `` 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`. |
|
| `` 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`. |
|
||||||
@@ -286,7 +286,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | Переключить | Checkout the selected tag tag as a detached HEAD. |
|
| `` <space> `` | Переключить | Checkout the selected tag as a detached HEAD. |
|
||||||
| `` n `` | Создать тег | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
| `` 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 `` | Delete | View delete options for local/remote tag. |
|
||||||
| `` P `` | Отправить тег | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
| `` P `` | Отправить тег | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
|
|
||||||
# Lazygit 按键绑定
|
# 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,28 +11,28 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` <c-r> `` | 切换到最近的仓库 | |
|
| `` <c-r> `` | 切换到最近的仓库 | |
|
||||||
| `` <pgup> (fn+up/shift+k) `` | 向上滚动主面板 | |
|
| `` <pgup> (fn+up/shift+k) `` | 向上滚动主面板 | |
|
||||||
| `` <pgdown> (fn+down/shift+j) `` | 向下滚动主面板 | |
|
| `` <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 `` | 推送 | 推送当前分支到它的上游。如果上游为配置,你可以在弹窗中配置上游分支。 |
|
||||||
| `` 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 `` | 拉取 | 从当前分支的远程分支获取改动。如果上游为配置,你可以在弹窗中配置上游分支。 |
|
||||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
| `` ) `` | 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. |
|
| `` ( `` | 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. |
|
| `` } `` | 扩大差异视图中显示的上下文范围 | 增加diff视图中围绕更改显示的上下文数量 |
|
||||||
| `` { `` | 缩小差异视图中显示的上下文范围 | Decrease the amount of the context shown around changes in the diff view. |
|
| `` { `` | 缩小差异视图中显示的上下文范围 | 减少diff视图中围绕更改显示的上下文数量 |
|
||||||
| `` : `` | 执行自定义命令 | 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> `` | 查看自定义补丁选项 | |
|
| `` <c-p> `` | 查看自定义补丁选项 | |
|
||||||
| `` m `` | 查看 合并/变基 选项 | View options to abort/continue/skip the current merge/rebase. |
|
| `` m `` | 查看 合并/变基 选项 | 查看当前合并或变基的中止、继续、跳过选项 |
|
||||||
| `` 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`. |
|
| `` 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. |
|
| `` <c-s> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 |
|
||||||
| `` 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. |
|
| `` W `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 |
|
||||||
| `` <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-e> `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 |
|
||||||
| `` q `` | 退出 | |
|
| `` q `` | 退出 | |
|
||||||
| `` <esc> `` | 取消 | |
|
| `` <esc> `` | 取消 | |
|
||||||
| `` <c-w> `` | 切换是否在差异视图中显示空白字符差异 | Toggle whether or not whitespace changes are shown in the diff view. |
|
| `` <c-w> `` | 切换是否在差异视图中显示空白字符差异 | 切换是否在diff视图中显示空白更改 |
|
||||||
| `` 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. |
|
| `` z `` | (通过 reflog)撤销「实验功能」 | Reflog将用于确定运行哪个git命令来撤消最后一个git命令。这并不包括对工作树的更改,只考虑提交。 |
|
||||||
| `` <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-z> `` | (通过 reflog)重做「实验功能」 | Reflog将用于确定运行哪个git命令来重做上一个git命令。这并不包括对工作树的更改,只考虑提交。 |
|
||||||
|
|
||||||
## 列表面板导航
|
## 列表面板导航
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` < `` | 滚动到顶部 | |
|
| `` < `` | 滚动到顶部 | |
|
||||||
| `` > `` | 滚动到底部 | |
|
| `` > `` | 滚动到底部 | |
|
||||||
| `` v `` | 切换拖动选择 | |
|
| `` v `` | 切换拖动选择 | |
|
||||||
| `` <s-down> `` | Range select down | |
|
| `` <s-down> `` | 向下扩展选择范围 | |
|
||||||
| `` <s-up> `` | Range select up | |
|
| `` <s-up> `` | 向上扩展选择范围 | |
|
||||||
| `` / `` | 开始搜索 | |
|
| `` / `` | 开始搜索 | |
|
||||||
| `` H `` | 向左滚动 | |
|
| `` H `` | 向左滚动 | |
|
||||||
| `` L `` | 向右滚动 | |
|
| `` L `` | 向右滚动 | |
|
||||||
@@ -56,27 +56,17 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
||||||
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
|
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如,hash、URL、diff、消息、作者)。 |
|
||||||
| `` o `` | 在浏览器中打开提交 | |
|
| `` o `` | 在浏览器中打开提交 | |
|
||||||
| `` n `` | 从提交创建新分支 | |
|
| `` n `` | 从提交创建新分支 | |
|
||||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||||
| `` 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 `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
|
||||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||||
| `` <enter> `` | 查看提交 | |
|
| `` <enter> `` | 查看提交 | |
|
||||||
| `` w `` | View worktree options | |
|
| `` w `` | 查看工作区选项 | |
|
||||||
| `` / `` | 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 | |
|
|
||||||
|
|
||||||
## 分支页面
|
## 分支页面
|
||||||
|
|
||||||
@@ -84,42 +74,42 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
|
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
|
||||||
| `` i `` | 显示 git-flow 选项 | |
|
| `` i `` | 显示 git-flow 选项 | |
|
||||||
| `` <space> `` | 检出 | Checkout selected item. |
|
| `` <space> `` | 检出 | 检出选中的项目 |
|
||||||
| `` n `` | 新分支 | |
|
| `` n `` | 新分支 | |
|
||||||
| `` o `` | 创建抓取请求 | |
|
| `` o `` | 创建拉取请求 | |
|
||||||
| `` O `` | 创建抓取请求选项 | |
|
| `` O `` | 创建拉取请求选项 | |
|
||||||
| `` <c-y> `` | 将抓取请求 URL 复制到剪贴板 | |
|
| `` <c-y> `` | 将拉取请求 URL 复制到剪贴板 | |
|
||||||
| `` c `` | 按名称检出 | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
|
| `` c `` | 按名称检出 | 按名称检出。在输入框中,您可以输入'-' 来切换到最后一个分支。 |
|
||||||
| `` F `` | 强制检出 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
|
| `` F `` | 强制检出 | 强制检出所选分支。这将在检出所选分支之前放弃工作目录中的所有本地更改。 |
|
||||||
| `` d `` | Delete | View delete options for local/remote branch. |
|
| `` d `` | 删除 | 查看本地/远程分支的删除选项 |
|
||||||
| `` r `` | 将已检出的分支变基到该分支 | Rebase the checked-out branch onto the selected branch. |
|
| `` r `` | 将已检出的分支变基到该分支 | 将检出的分支变基到所选的分支上。 |
|
||||||
| `` M `` | 合并到当前检出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
|
||||||
| `` f `` | 从上游快进此分支 | Fast-forward selected branch from its upstream. |
|
| `` f `` | 从上游快进此分支 | 将当前分支直接移动到远程追踪分支的最新提交 |
|
||||||
| `` T `` | 创建标签 | |
|
| `` T `` | 创建标签 | |
|
||||||
| `` s `` | Sort order | |
|
| `` s `` | 排序 | |
|
||||||
| `` g `` | 查看重置选项 | |
|
| `` g `` | 查看重置选项 | |
|
||||||
| `` R `` | 重命名分支 | |
|
| `` 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> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||||
| `` <enter> `` | 查看提交 | |
|
| `` <enter> `` | 查看提交 | |
|
||||||
| `` w `` | View worktree options | |
|
| `` w `` | 查看工作区选项 | |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|
||||||
## 子提交
|
## 子提交
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
||||||
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
|
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如,hash、URL、diff、消息、作者)。 |
|
||||||
| `` o `` | 在浏览器中打开提交 | |
|
| `` o `` | 在浏览器中打开提交 | |
|
||||||
| `` n `` | 从提交创建新分支 | |
|
| `` n `` | 从提交创建新分支 | |
|
||||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||||
| `` 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 `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
|
||||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||||
| `` <enter> `` | 查看提交的文件 | |
|
| `` <enter> `` | 查看提交的文件 | |
|
||||||
| `` w `` | View worktree options | |
|
| `` w `` | 查看工作区选项 | |
|
||||||
| `` / `` | 开始搜索 | |
|
| `` / `` | 开始搜索 | |
|
||||||
|
|
||||||
## 子模块
|
## 子模块
|
||||||
@@ -127,104 +117,114 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将子模块名称复制到剪贴板 | |
|
| `` <c-o> `` | 将子模块名称复制到剪贴板 | |
|
||||||
| `` <enter> `` | Enter | 输入子模块 |
|
| `` <enter> `` | 进入 | 输入子模块 |
|
||||||
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
|
| `` d `` | 删除 | 删除选定的子模块及其相应的目录 |
|
||||||
| `` u `` | Update | 更新子模块 |
|
| `` u `` | 更新 | 更新子模块 |
|
||||||
| `` n `` | 添加新的子模块 | |
|
| `` n `` | 添加新的子模块 | |
|
||||||
| `` e `` | 更新子模块 URL | |
|
| `` e `` | 更新子模块 URL | |
|
||||||
| `` i `` | Initialize | 初始化子模块 |
|
| `` i `` | 初始化 | 初始化子模块 |
|
||||||
| `` b `` | 查看批量子模块选项 | |
|
| `` b `` | 查看批量子模块选项 | |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|
||||||
|
## 工作区
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` n `` | 新建工作树 | |
|
||||||
|
| `` <space> `` | 切换 | 切换到选中的工作树 |
|
||||||
|
| `` o `` | 在编辑器中编写 | |
|
||||||
|
| `` d `` | 删除 | 删除选定的工作树。这将删除工作树的目录以及 .git 目录中有关工作树的元数据。 |
|
||||||
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|
||||||
## 提交
|
## 提交
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
|
||||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||||
| `` b `` | 查看二分查找选项 | |
|
| `` b `` | 查看二分查找选项 | |
|
||||||
| `` s `` | 压缩 | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
| `` s `` | 压缩(Squash) | 将已选提交压缩到该提交之下。这些选定的提交的消息会附加到该提交的消息之下。 |
|
||||||
| `` f `` | 修正(fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
|
| `` f `` | 修正(fixup) | 将选定的提交合并到其下面的提交中。与压缩类似,但所选提交的消息将被丢弃。 |
|
||||||
| `` r `` | 改写提交 | Reword the selected commit's message. |
|
| `` r `` | 改写提交 | 重写所选提交的消息。 |
|
||||||
| `` 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. |
|
| `` d `` | 删除提交 | 删除选中的提交。这将通过变基从分支中删除该提交,如果该提交修改的内容依赖于后续的提交,则需要解决合并冲突。 |
|
||||||
| `` e `` | Edit (start interactive rebase) | 编辑提交 |
|
| `` e `` | 编辑(开始交互式变基) | 编辑提交 |
|
||||||
| `` 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.
|
| `` i `` | 开始交互式变基 | 为分支上的提交启动交互式变基。这将包括从 HEAD 提交到第一个合并提交或主分支提交的所有提交。
|
||||||
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
|
如果您想从所选提交启动交互式变基,请按 `e`。 |
|
||||||
| `` p `` | Pick | 选择提交(变基过程中) |
|
| `` p `` | 拣选(Pick) | 选择提交(变基过程中) |
|
||||||
| `` F `` | 为此提交创建修正 | 创建修正提交 |
|
| `` F `` | 为此提交创建修正 | 创建修正提交 |
|
||||||
| `` S `` | Apply fixup commits | 压缩在所选提交之上的所有“fixup!”提交(自动压缩) |
|
| `` S `` | 应用该修复提交 | 压缩在所选提交之上的所有“fixup!”提交(自动压缩) |
|
||||||
| `` <c-j> `` | 下移提交 | |
|
| `` <c-j> `` | 下移提交 | |
|
||||||
| `` <c-k> `` | 上移提交 | |
|
| `` <c-k> `` | 上移提交 | |
|
||||||
| `` V `` | 粘贴提交(拣选) | |
|
| `` 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. |
|
| `` B `` | 标记一个主提交用于变基 | 选择下一次变基的主提交。当您变基到一个分支时,只有高于主提交的提交才会被引入。这使用“git rebase --onto”命令。 |
|
||||||
| `` A `` | Amend | 用已暂存的更改来修补提交 |
|
| `` A `` | 修补(Amend) | 用已暂存的变更来修补提交 |
|
||||||
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
|
| `` a `` | 修补提交属性 | 设置或重置提交的作者,或添加其他作者。 |
|
||||||
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
|
| `` t `` | 撤销(Revert) | 为所选提交创建还原提交,这会反向应用所选提交的更改。 |
|
||||||
| `` T `` | 标签提交 | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
|
| `` T `` | 标签提交 | 创建一个新标签指向所选提交。你可以在弹窗中输入标签名称和描述(可选)。 |
|
||||||
| `` <c-l> `` | 打开日志菜单 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
| `` <c-l> `` | 打开日志菜单 | 查看提交日志的选项,例如更改排序顺序、隐藏 git graph、显示整个 git graph。 |
|
||||||
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
|
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如,hash、URL、diff、消息、作者)。 |
|
||||||
| `` o `` | 在浏览器中打开提交 | |
|
| `` o `` | 在浏览器中打开提交 | |
|
||||||
| `` n `` | 从提交创建新分支 | |
|
| `` n `` | 从提交创建新分支 | |
|
||||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||||
| `` 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 `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||||
| `` <enter> `` | 查看提交的文件 | |
|
| `` <enter> `` | 查看提交的文件 | |
|
||||||
| `` w `` | View worktree options | |
|
| `` w `` | 查看工作区选项 | |
|
||||||
| `` / `` | 开始搜索 | |
|
| `` / `` | 开始搜索 | |
|
||||||
|
|
||||||
|
## 提交信息
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | 确认 | |
|
||||||
|
| `` <esc> `` | 关闭 | |
|
||||||
|
|
||||||
## 提交文件
|
## 提交文件
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将文件名复制到剪贴板 | |
|
| `` <c-o> `` | 将文件名复制到剪贴板 | |
|
||||||
| `` c `` | 检出 | 检出文件 |
|
| `` c `` | 检出 | 检出文件 |
|
||||||
| `` d `` | Remove | 放弃对此文件的提交更改 |
|
| `` d `` | 删除 | 放弃对此文件的提交变更 |
|
||||||
| `` o `` | 打开文件 | Open file in default application. |
|
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||||
| `` e `` | Edit | Open file in external editor. |
|
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <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. |
|
| `` <space> `` | 补丁中包含的切换文件 | 切换文件是否包含在自定义补丁中。请参阅 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. |
|
| `` a `` | 操作所有文件 | 添加或删除所有提交中的文件到自定义的补丁中。请参阅 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. |
|
| `` <enter> `` | 输入文件以将所选行添加到补丁中(或切换目录折叠) | 如果已选择一个文件,则Enter进入该文件,以便您可以向自定义补丁添加/删除单独的行。如果选择了目录,则切换目录。 |
|
||||||
| `` ` `` | 切换文件树视图 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
|
| `` ` `` | 切换文件树视图 | 在平铺部署与树布局之间切换文件视图。平铺布局在一个列表中展示所有文件路径,树布局则根据目录分组展示。 |
|
||||||
| `` / `` | 开始搜索 | |
|
| `` / `` | 开始搜索 | |
|
||||||
|
|
||||||
## 提交讯息
|
|
||||||
|
|
||||||
| Key | Action | Info |
|
|
||||||
|-----|--------|-------------|
|
|
||||||
| `` <enter> `` | 确认 | |
|
|
||||||
| `` <esc> `` | 关闭 | |
|
|
||||||
|
|
||||||
## 文件
|
## 文件
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将文件名复制到剪贴板 | |
|
| `` <c-o> `` | 将文件名复制到剪贴板 | |
|
||||||
| `` <space> `` | 切换暂存状态 | Toggle staged for selected file. |
|
| `` <space> `` | 切换暂存状态 | 为选定的文件切换暂存状态 |
|
||||||
| `` <c-b> `` | Filter files by status | |
|
| `` <c-b> `` | 通过状态过滤文件 | |
|
||||||
| `` y `` | Copy to clipboard | |
|
| `` y `` | 复制到剪贴板 | |
|
||||||
| `` c `` | 提交更改 | Commit staged changes. |
|
| `` c `` | 提交变更 | 提交暂存文件 |
|
||||||
| `` w `` | 提交更改而无需预先提交钩子 | |
|
| `` w `` | 提交变更而无需预先提交钩子 | |
|
||||||
| `` A `` | 修补最后一次提交 | |
|
| `` A `` | 修补最后一次提交 | |
|
||||||
| `` C `` | 提交更改(使用编辑器编辑提交信息) | |
|
| `` 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> |
|
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||||
| `` e `` | Edit | Open file in external editor. |
|
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
|
||||||
| `` o `` | 打开文件 | Open file in default application. |
|
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||||
| `` i `` | 忽略文件 | |
|
| `` i `` | 忽略文件 | |
|
||||||
| `` r `` | 刷新文件 | |
|
| `` r `` | 刷新文件 | |
|
||||||
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
|
| `` s `` | 贮藏 | 贮藏所有变更.若要使用其他贮藏变体,请使用查看贮藏选项快捷键 |
|
||||||
| `` S `` | 查看贮藏选项 | View stash options (e.g. stash all, stash staged, stash unstaged). |
|
| `` S `` | 查看贮藏选项 | 查看贮藏选项(例如:贮藏所有、贮藏已暂存变更、贮藏未暂存变更) |
|
||||||
| `` a `` | 切换所有文件的暂存状态 | Toggle staged/unstaged for all files in working tree. |
|
| `` a `` | 切换所有文件的暂存状态 | 切换工作区中所有文件的已暂存/未暂存状态 |
|
||||||
| `` <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. |
|
| `` <enter> `` | 暂存单个 块/行 用于文件, 或 折叠/展开 目录 | 如果选中的是一个文件,则会进入到暂存视图,以便可以暂存单个代码块/行。如果选中的是一个目录,则会折叠/展开这个目录 |
|
||||||
| `` d `` | 查看'放弃更改'选项 | View options for discarding changes to the selected file. |
|
| `` d `` | 查看'放弃变更'选项 | 查看选中文件的放弃变更选项 |
|
||||||
| `` g `` | 查看上游重置选项 | |
|
| `` g `` | 查看上游重置选项 | |
|
||||||
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
|
| `` D `` | 重置 | 查看工作树的重置选项(例如:清除工作树)。 |
|
||||||
| `` ` `` | 切换文件树视图 | 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) | |
|
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||||
| `` M `` | 打开外部合并工具 (git mergetool) | Run `git mergetool`. |
|
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
|
||||||
| `` f `` | 抓取 | Fetch changes from remote. |
|
| `` f `` | 抓取 | 从远程获取变更 |
|
||||||
| `` / `` | 开始搜索 | |
|
| `` / `` | 开始搜索 | |
|
||||||
|
|
||||||
## 构建补丁中
|
## 构建补丁中
|
||||||
@@ -234,10 +234,10 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` <left> `` | 选择上一个区块 | |
|
| `` <left> `` | 选择上一个区块 | |
|
||||||
| `` <right> `` | 选择下一个区块 | |
|
| `` <right> `` | 选择下一个区块 | |
|
||||||
| `` v `` | 切换拖动选择 | |
|
| `` v `` | 切换拖动选择 | |
|
||||||
| `` a `` | 切换选择区块 | Toggle hunk selection mode. |
|
| `` a `` | 切换选择代码块 | 切换代码块选择模式 |
|
||||||
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
|
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
|
||||||
| `` o `` | 打开文件 | Open file in default application. |
|
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||||
| `` e `` | 编辑文件 | Open file in external editor. |
|
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||||
| `` <space> `` | 添加/移除 行到补丁 | |
|
| `` <space> `` | 添加/移除 行到补丁 | |
|
||||||
| `` <esc> `` | 退出逐行模式 | |
|
| `` <esc> `` | 退出逐行模式 | |
|
||||||
| `` / `` | 开始搜索 | |
|
| `` / `` | 开始搜索 | |
|
||||||
@@ -246,15 +246,15 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | 检出 | Checkout the selected tag tag as a detached HEAD. |
|
| `` <space> `` | 检出 | 检出选择的标签作为分离的HEAD |
|
||||||
| `` n `` | 创建标签 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
| `` n `` | 创建标签 | 基于当前提交创建一个新标签。你将在弹窗中输入标签名称和描述(可选)。 |
|
||||||
| `` d `` | Delete | View delete options for local/remote tag. |
|
| `` d `` | 删除 | 查看本地/远程标签的删除选项 |
|
||||||
| `` P `` | 推送标签 | Push the selected tag to a remote. You'll be prompted to select a remote. |
|
| `` P `` | 推送标签 | 推送选择的标签到远端。你将在弹窗中选择一个远端。 |
|
||||||
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
| `` g `` | 重置 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||||
| `` <enter> `` | 查看提交 | |
|
| `` <enter> `` | 查看提交 | |
|
||||||
| `` w `` | View worktree options | |
|
| `` w `` | 查看工作区选项 | |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|
||||||
## 正在合并
|
## 正在合并
|
||||||
|
|
||||||
@@ -266,10 +266,10 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` <down> `` | 选择底部块 | |
|
| `` <down> `` | 选择底部块 | |
|
||||||
| `` <left> `` | 选择上一个冲突 | |
|
| `` <left> `` | 选择上一个冲突 | |
|
||||||
| `` <right> `` | 选择下一个冲突 | |
|
| `` <right> `` | 选择下一个冲突 | |
|
||||||
| `` z `` | 撤销 | Undo last merge conflict resolution. |
|
| `` z `` | 撤销 | 撤消上次合并冲突解决 |
|
||||||
| `` e `` | 编辑文件 | Open file in external editor. |
|
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||||
| `` o `` | 打开文件 | Open file in default application. |
|
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||||
| `` M `` | 打开外部合并工具 (git mergetool) | Run `git mergetool`. |
|
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
|
||||||
| `` <esc> `` | 返回文件面板 | |
|
| `` <esc> `` | 返回文件面板 | |
|
||||||
|
|
||||||
## 正在暂存
|
## 正在暂存
|
||||||
@@ -279,19 +279,19 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` <left> `` | 选择上一个区块 | |
|
| `` <left> `` | 选择上一个区块 | |
|
||||||
| `` <right> `` | 选择下一个区块 | |
|
| `` <right> `` | 选择下一个区块 | |
|
||||||
| `` v `` | 切换拖动选择 | |
|
| `` v `` | 切换拖动选择 | |
|
||||||
| `` a `` | 切换选择区块 | Toggle hunk selection mode. |
|
| `` a `` | 切换选择代码块 | 切换代码块选择模式 |
|
||||||
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
|
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
|
||||||
| `` <space> `` | 切换暂存状态 | 切换行暂存状态 |
|
| `` <space> `` | 切换暂存状态 | 切换行暂存状态 |
|
||||||
| `` d `` | 取消变更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
| `` d `` | 取消变更(git reset) | 当选择未暂存的变更时,使用git reset丢弃该变更。当选择已暂存的变更时,取消暂存该变更 |
|
||||||
| `` o `` | 打开文件 | Open file in default application. |
|
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||||
| `` e `` | 编辑文件 | Open file in external editor. |
|
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||||
| `` <esc> `` | 返回文件面板 | |
|
| `` <esc> `` | 返回文件面板 | |
|
||||||
| `` <tab> `` | 切换到其他面板 | Switch to other view (staged/unstaged changes). |
|
| `` <tab> `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) |
|
||||||
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
|
| `` E `` | 编辑代码块 | 在外部编辑器中编辑选中的代码块 |
|
||||||
| `` c `` | 提交更改 | Commit staged changes. |
|
| `` c `` | 提交变更 | 提交暂存文件 |
|
||||||
| `` w `` | 提交更改而无需预先提交钩子 | |
|
| `` w `` | 提交变更而无需预先提交钩子 | |
|
||||||
| `` C `` | 提交更改(使用编辑器编辑提交信息) | |
|
| `` 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> |
|
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||||
| `` / `` | 开始搜索 | |
|
| `` / `` | 开始搜索 | |
|
||||||
|
|
||||||
## 正常
|
## 正常
|
||||||
@@ -305,8 +305,8 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` o `` | 打开配置文件 | Open file in default application. |
|
| `` o `` | 打开配置文件 | 使用默认程序打开该文件 |
|
||||||
| `` e `` | 编辑配置文件 | Open file in external editor. |
|
| `` e `` | 编辑配置文件 | 使用外部编辑器打开文件 |
|
||||||
| `` u `` | 检查更新 | |
|
| `` u `` | 检查更新 | |
|
||||||
| `` <enter> `` | 切换到最近的仓库 | |
|
| `` <enter> `` | 切换到最近的仓库 | |
|
||||||
| `` a `` | 显示所有分支的日志 | |
|
| `` a `` | 显示所有分支的日志 | |
|
||||||
@@ -324,46 +324,46 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <enter> `` | 执行 | |
|
| `` <enter> `` | 执行 | |
|
||||||
| `` <esc> `` | 关闭 | |
|
| `` <esc> `` | 关闭 | |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|
||||||
## 贮藏
|
## 贮藏
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | 应用 | Apply the stash entry to your working directory. |
|
| `` <space> `` | 应用 | 将贮藏项应用到您的工作目录。 |
|
||||||
| `` g `` | 应用并删除 | Apply the stash entry to your working directory and remove the stash entry. |
|
| `` g `` | 应用并删除 | 将存储项应用到工作目录并删除存储项。 |
|
||||||
| `` d `` | 删除 | Remove the stash entry from the stash list. |
|
| `` d `` | 删除 | 从贮藏列表中删除该贮藏项 |
|
||||||
| `` 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. |
|
| `` n `` | 新分支 | 从选定的贮藏项创建一个新分支。这是通过 git 检查创建贮藏项的提交,从该提交创建一个新分支,然后将贮藏项作为附加提交应用到新分支来实现的。 |
|
||||||
| `` r `` | Rename stash | |
|
| `` r `` | 重命名贮藏 | |
|
||||||
| `` <enter> `` | 查看提交的文件 | |
|
| `` <enter> `` | 查看提交的文件 | |
|
||||||
| `` w `` | View worktree options | |
|
| `` w `` | 查看工作区选项 | |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|
||||||
## 远程分支
|
## 远程分支
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
|
| `` <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 `` | 新分支 | |
|
| `` n `` | 新分支 | |
|
||||||
| `` M `` | 合并到当前检出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
|
||||||
| `` r `` | 将已检出的分支变基到该分支 | Rebase the checked-out branch onto the selected branch. |
|
| `` r `` | 将已检出的分支变基到该分支 | 将检出的分支变基到所选的分支上。 |
|
||||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
| `` d `` | 删除 | 从远程删除远程分支。 |
|
||||||
| `` u `` | Set as upstream | 设置为检出分支的上游 |
|
| `` u `` | 设置为上游 | 设置为检出分支的上游 |
|
||||||
| `` s `` | Sort order | |
|
| `` s `` | 排序 | |
|
||||||
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||||
| `` <enter> `` | 查看提交 | |
|
| `` <enter> `` | 查看提交 | |
|
||||||
| `` w `` | View worktree options | |
|
| `` w `` | 查看工作区选项 | |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|
||||||
## 远程页面
|
## 远程页面
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <enter> `` | View branches | |
|
| `` <enter> `` | 查看分支 | |
|
||||||
| `` n `` | 添加新的远程仓库 | |
|
| `` n `` | 添加新的远程仓库 | |
|
||||||
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
|
| `` d `` | 删除 | 删除选中的远程。从远程跟踪远程分支的任何本地分支都不会受到影响。 |
|
||||||
| `` e `` | Edit | 编辑远程仓库 |
|
| `` e `` | 编辑 | 编辑远程仓库 |
|
||||||
| `` f `` | 抓取 | 抓取远程仓库 |
|
| `` f `` | 抓取 | 抓取远程仓库 |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | 通过文本过滤当前视图 | |
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
|||||||
| `` <pgup> (fn+up/shift+k) `` | 向上捲動主面板 | |
|
| `` <pgup> (fn+up/shift+k) `` | 向上捲動主面板 | |
|
||||||
| `` <pgdown> (fn+down/shift+j) `` | 向下捲動主面板 | |
|
| `` <pgdown> (fn+down/shift+j) `` | 向下捲動主面板 | |
|
||||||
| `` @ `` | 開啟命令記錄選單 | View options for the command log e.g. show/hide the command log and focus the command log. |
|
| `` @ `` | 開啟命令記錄選單 | 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 `` | 推送 | 推送到遠端。如果沒有設定遠端,會開啟設定視窗。 |
|
||||||
| `` 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 `` | 拉取 | 從遠端同步當前分支。如果沒有設定遠端,會開啟設定視窗。 |
|
||||||
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
|
| `` ) `` | 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. |
|
| `` ( `` | 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. |
|
| `` } `` | 增加差異檢視中顯示變更周圍上下文的大小 | 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. |
|
| `` { `` | 減小差異檢視中顯示變更周圍上下文的大小 | 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> `` | 檢視自訂補丁選項 | |
|
| `` <c-p> `` | 檢視自訂補丁選項 | |
|
||||||
| `` m `` | 查看合併/變基選項 | View options to abort/continue/skip the current merge/rebase. |
|
| `` 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`. |
|
| `` 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`. |
|
||||||
@@ -60,8 +60,8 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
|||||||
| `` v `` | 切換拖曳選擇 | |
|
| `` v `` | 切換拖曳選擇 | |
|
||||||
| `` a `` | 切換選擇程式碼塊 | Toggle hunk selection mode. |
|
| `` a `` | 切換選擇程式碼塊 | Toggle hunk selection mode. |
|
||||||
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
|
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
|
||||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||||
| `` e `` | 編輯檔案 | Open file in external editor. |
|
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||||
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
|
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
|
||||||
| `` <esc> `` | 退出自訂補丁建立器 | |
|
| `` <esc> `` | 退出自訂補丁建立器 | |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
@@ -84,9 +84,9 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
|||||||
| `` <left> `` | 選擇上一個衝突 | |
|
| `` <left> `` | 選擇上一個衝突 | |
|
||||||
| `` <right> `` | 選擇下一個衝突 | |
|
| `` <right> `` | 選擇下一個衝突 | |
|
||||||
| `` z `` | 復原 | Undo last merge conflict resolution. |
|
| `` z `` | 復原 | Undo last merge conflict resolution. |
|
||||||
| `` e `` | 編輯檔案 | Open file in external editor. |
|
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||||
| `` M `` | 開啟外部合併工具 (git mergetool) | Run `git mergetool`. |
|
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
|
||||||
| `` <esc> `` | 返回檔案面板 | |
|
| `` <esc> `` | 返回檔案面板 | |
|
||||||
|
|
||||||
## 主面板(預存)
|
## 主面板(預存)
|
||||||
@@ -100,12 +100,12 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
|||||||
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
|
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
|
||||||
| `` <space> `` | 切換預存 | 切換現有行的狀態 (已預存/未預存) |
|
| `` <space> `` | 切換預存 | 切換現有行的狀態 (已預存/未預存) |
|
||||||
| `` d `` | 刪除變更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
| `` 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. |
|
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||||
| `` e `` | 編輯檔案 | Open file in external editor. |
|
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||||
| `` <esc> `` | 返回檔案面板 | |
|
| `` <esc> `` | 返回檔案面板 | |
|
||||||
| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
|
| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
|
||||||
| `` E `` | 編輯程式碼塊 | Edit selected hunk in external editor. |
|
| `` E `` | 編輯程式碼塊 | Edit selected hunk in external editor. |
|
||||||
| `` c `` | 提交變更 | Commit staged changes. |
|
| `` c `` | 提交變更 | 提交暫存區變更 |
|
||||||
| `` w `` | 沒有預提交 hook 就提交更改 | |
|
| `` w `` | 沒有預提交 hook 就提交更改 | |
|
||||||
| `` C `` | 使用 git 編輯器提交變更 | |
|
| `` 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> |
|
| `` <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> |
|
||||||
@@ -131,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. |
|
| `` 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 `` | 複製提交 (揀選) | 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-r> `` | 重設選定的揀選 (複製) 提交 | |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||||
| `` <enter> `` | 檢視所選項目的檔案 | |
|
| `` <enter> `` | 檢視所選項目的檔案 | |
|
||||||
| `` w `` | 檢視工作目錄選項 | |
|
| `` w `` | 檢視工作目錄選項 | |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
@@ -156,7 +156,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
|||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` n `` | New worktree | |
|
| `` n `` | New worktree | |
|
||||||
| `` <space> `` | Switch | Switch to the selected 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. |
|
| `` 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. |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
|
|
||||||
@@ -169,22 +169,22 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
|||||||
| `` b `` | 查看二分選項 | |
|
| `` b `` | 查看二分選項 | |
|
||||||
| `` s `` | 壓縮 (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
| `` s `` | 壓縮 (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||||
| `` f `` | 修復 (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
|
| `` f `` | 修復 (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
|
||||||
| `` r `` | 改寫提交 | Reword the selected commit's message. |
|
| `` r `` | 改寫提交 | 改寫選中的提交訊息 |
|
||||||
| `` R `` | 使用編輯器改寫提交 | |
|
| `` 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. |
|
| `` 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) | 編輯提交 |
|
| `` e `` | 編輯(開始互動變基) | 編輯提交 |
|
||||||
| `` 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.
|
| `` 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`. |
|
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
|
||||||
| `` p `` | Pick | 挑選提交 (於變基過程中) |
|
| `` p `` | 挑選 | 挑選提交 (於變基過程中) |
|
||||||
| `` F `` | 建立修復提交 | 為此提交建立修復提交 |
|
| `` F `` | 建立修復提交 | 為此提交建立修復提交 |
|
||||||
| `` S `` | 壓縮上方所有「fixup」提交(自動壓縮) | 是否壓縮上方 {{.commit}} 所有「fixup」提交? |
|
| `` S `` | 壓縮上方所有「fixup」提交(自動壓縮) | 是否壓縮上方 {{.commit}} 所有「fixup」提交? |
|
||||||
| `` <c-j> `` | 向下移動提交 | |
|
| `` <c-j> `` | 向下移動提交 | |
|
||||||
| `` <c-k> `` | 向上移動提交 | |
|
| `` <c-k> `` | 向上移動提交 | |
|
||||||
| `` V `` | 貼上提交 (揀選) | |
|
| `` V `` | 貼上提交 (揀選) | |
|
||||||
| `` B `` | 為了變基已標注提交為基準提交 | 請為了下一次變基選擇一項基準提交;此將執行 `git rebase --onto`。 |
|
| `` B `` | 為了變基已標注提交為基準提交 | 請為了下一次變基選擇一項基準提交;此將執行 `git rebase --onto`。 |
|
||||||
| `` A `` | Amend | 使用已預存的更改修正提交 |
|
| `` A `` | 修改 | 使用已預存的更改修正提交 |
|
||||||
| `` a `` | 設定/重設提交作者 | Set/Reset commit author or set co-author. |
|
| `` 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. |
|
| `` 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. |
|
| `` <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. |
|
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
|
||||||
@@ -193,7 +193,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` n `` | 從提交建立新分支 | |
|
| `` n `` | 從提交建立新分支 | |
|
||||||
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
| `` 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 `` | 複製提交 (揀選) | 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> `` | 檢視所選項目的檔案 | |
|
| `` <enter> `` | 檢視所選項目的檔案 | |
|
||||||
| `` w `` | 檢視工作目錄選項 | |
|
| `` w `` | 檢視工作目錄選項 | |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
@@ -212,9 +212,9 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
||||||
| `` c `` | 檢出 | 檢出檔案 |
|
| `` 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. |
|
| `` 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. |
|
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||||
| `` e `` | Edit | Open file in external editor. |
|
| `` e `` | 編輯 | 使用外部編輯器開啟 |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <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. |
|
| `` <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. |
|
| `` 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. |
|
| `` <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. |
|
||||||
@@ -246,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. |
|
| `` 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 `` | 複製提交 (揀選) | 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-r> `` | 重設選定的揀選 (複製) 提交 | |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||||
| `` <enter> `` | 檢視提交 | |
|
| `` <enter> `` | 檢視提交 | |
|
||||||
| `` w `` | 檢視工作目錄選項 | |
|
| `` w `` | 檢視工作目錄選項 | |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
@@ -257,23 +257,23 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
|
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
|
||||||
| `` i `` | 顯示 git-flow 選項 | |
|
| `` i `` | 顯示 git-flow 選項 | |
|
||||||
| `` <space> `` | 檢出 | Checkout selected item. |
|
| `` <space> `` | 檢出 | 檢出選定的項目。 |
|
||||||
| `` n `` | 新分支 | |
|
| `` n `` | 新分支 | |
|
||||||
| `` o `` | 建立拉取請求 | |
|
| `` o `` | 建立拉取請求 | |
|
||||||
| `` O `` | 建立拉取請求選項 | |
|
| `` O `` | 建立拉取請求選項 | |
|
||||||
| `` <c-y> `` | 複製拉取請求的 URL 到剪貼板 | |
|
| `` <c-y> `` | 複製拉取請求的 URL 到剪貼板 | |
|
||||||
| `` c `` | 根據名稱檢出 | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
|
| `` 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. |
|
| `` 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. |
|
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
|
||||||
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
||||||
| `` f `` | 從上游快進此分支 | Fast-forward selected branch from its upstream. |
|
| `` f `` | 從上游快進此分支 | 從遠端快進所選的分支 |
|
||||||
| `` T `` | 建立標籤 | |
|
| `` T `` | 建立標籤 | |
|
||||||
| `` s `` | Sort order | |
|
| `` s `` | 排序規則 | |
|
||||||
| `` g `` | 檢視重設選項 | |
|
| `` g `` | 檢視重設選項 | |
|
||||||
| `` R `` | 重新命名分支 | |
|
| `` R `` | 重新命名分支 | |
|
||||||
| `` u `` | 檢視上游設定 | 檢視有關上游分支的設定(例如重設至上游) |
|
| `` u `` | 檢視遠端設定 | 檢視有關遠端分支的設定(例如重設至遠端) |
|
||||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||||
| `` <enter> `` | 檢視提交 | |
|
| `` <enter> `` | 檢視提交 | |
|
||||||
| `` w `` | 檢視工作目錄選項 | |
|
| `` w `` | 檢視工作目錄選項 | |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
@@ -282,12 +282,12 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` <space> `` | 檢出 | Checkout the selected tag tag as a detached HEAD. |
|
| `` <space> `` | 檢出 | Checkout the selected tag as a detached HEAD. |
|
||||||
| `` n `` | 建立標籤 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
|
| `` 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. |
|
| `` 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> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||||
| `` <enter> `` | 檢視提交 | |
|
| `` <enter> `` | 檢視提交 | |
|
||||||
| `` w `` | 檢視工作目錄選項 | |
|
| `` w `` | 檢視工作目錄選項 | |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
@@ -299,35 +299,35 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
||||||
| `` <space> `` | 切換預存 | Toggle staged for selected file. |
|
| `` <space> `` | 切換預存 | Toggle staged for selected file. |
|
||||||
| `` <c-b> `` | 篩選檔案 (預存/未預存) | |
|
| `` <c-b> `` | 篩選檔案 (預存/未預存) | |
|
||||||
| `` y `` | Copy to clipboard | |
|
| `` y `` | 複製到剪貼簿 | |
|
||||||
| `` c `` | 提交變更 | Commit staged changes. |
|
| `` c `` | 提交變更 | 提交暫存區變更 |
|
||||||
| `` w `` | 沒有預提交 hook 就提交更改 | |
|
| `` w `` | 沒有預提交 hook 就提交更改 | |
|
||||||
| `` A `` | 修改上次提交 | |
|
| `` A `` | 修改上次提交 | |
|
||||||
| `` C `` | 使用 git 編輯器提交變更 | |
|
| `` 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> |
|
| `` <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. |
|
| `` e `` | 編輯 | 使用外部編輯器開啟 |
|
||||||
| `` o `` | 開啟檔案 | Open file in default application. |
|
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||||
| `` i `` | 忽略或排除檔案 | |
|
| `` i `` | 忽略或排除檔案 | |
|
||||||
| `` r `` | 重新整理檔案 | |
|
| `` 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). |
|
| `` S `` | 檢視收藏選項 | View stash options (e.g. stash all, stash staged, stash unstaged). |
|
||||||
| `` a `` | 全部預存/取消預存 | Toggle staged/unstaged for all files in working tree. |
|
| `` 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. |
|
| `` <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. |
|
| `` d `` | 捨棄 | 檢視選中變動進行捨棄復原 |
|
||||||
| `` g `` | 檢視上游重設選項 | |
|
| `` g `` | 檢視遠端重設選項 | |
|
||||||
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
|
| `` 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. |
|
| `` ` `` | 顯示檔案樹狀視圖 | 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) | |
|
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||||
| `` M `` | 開啟外部合併工具 (git mergetool) | Run `git mergetool`. |
|
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
|
||||||
| `` f `` | 擷取 | Fetch changes from remote. |
|
| `` f `` | 擷取 | 同步遠端異動 |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
|
|
||||||
## 狀態
|
## 狀態
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|-----|--------|-------------|
|
|-----|--------|-------------|
|
||||||
| `` o `` | 開啟設定檔案 | Open file in default application. |
|
| `` o `` | 開啟設定檔案 | 使用預設軟體開啟 |
|
||||||
| `` e `` | 編輯設定檔案 | Open file in external editor. |
|
| `` e `` | 編輯設定檔案 | 使用外部編輯器開啟 |
|
||||||
| `` u `` | 檢查更新 | |
|
| `` u `` | 檢查更新 | |
|
||||||
| `` <enter> `` | 切換到最近使用的版本庫 | |
|
| `` <enter> `` | 切換到最近使用的版本庫 | |
|
||||||
| `` a `` | 顯示所有分支日誌 | |
|
| `` a `` | 顯示所有分支日誌 | |
|
||||||
@@ -346,7 +346,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` <enter> `` | View branches | |
|
| `` <enter> `` | View branches | |
|
||||||
| `` n `` | 新增遠端 | |
|
| `` n `` | 新增遠端 | |
|
||||||
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
|
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
|
||||||
| `` e `` | Edit | 編輯遠端 |
|
| `` e `` | 編輯 | 編輯遠端 |
|
||||||
| `` f `` | 擷取 | 擷取遠端 |
|
| `` f `` | 擷取 | 擷取遠端 |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
|
|
||||||
@@ -359,11 +359,11 @@ If you would instead like to start an interactive rebase from the selected commi
|
|||||||
| `` n `` | 新分支 | |
|
| `` n `` | 新分支 | |
|
||||||
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
|
| `` 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. |
|
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
|
||||||
| `` d `` | Delete | Delete the remote branch from the remote. |
|
| `` d `` | 刪除 | Delete the remote branch from the remote. |
|
||||||
| `` u `` | Set as upstream | 將此分支設為當前分支之上游 |
|
| `` u `` | 設置為遠端 | 將此分支設為當前分支之遠端 |
|
||||||
| `` s `` | Sort order | |
|
| `` s `` | 排序規則 | |
|
||||||
| `` g `` | 檢視重設選項 | 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> `` | Open external diff tool (git difftool) | |
|
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||||
| `` <enter> `` | 檢視提交 | |
|
| `` <enter> `` | 檢視提交 | |
|
||||||
| `` w `` | 檢視工作目錄選項 | |
|
| `` w `` | 檢視工作目錄選項 | |
|
||||||
| `` / `` | 搜尋 | |
|
| `` / `` | 搜尋 | |
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ require (
|
|||||||
github.com/integrii/flaggy v1.4.0
|
github.com/integrii/flaggy v1.4.0
|
||||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b
|
github.com/jesseduffield/gocui v0.3.1-0.20250107151125-716b1eb82fb4
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
|
||||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
|
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
|
||||||
github.com/kyokomi/emoji/v2 v2.2.8
|
github.com/kyokomi/emoji/v2 v2.2.8
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
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/mgutz/str v1.2.0
|
||||||
github.com/mitchellh/go-ps v1.0.0
|
github.com/mitchellh/go-ps v1.0.0
|
||||||
github.com/sahilm/fuzzy v0.1.0
|
github.com/sahilm/fuzzy v0.1.0
|
||||||
@@ -38,7 +38,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
|
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
|
||||||
golang.org/x/sync v0.7.0
|
golang.org/x/sync v0.10.0
|
||||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
@@ -73,10 +73,10 @@ require (
|
|||||||
github.com/sergi/go-diff v1.1.0 // indirect
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/net v0.7.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/term v0.21.0 // indirect
|
golang.org/x/term v0.28.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -188,10 +188,10 @@ 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/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 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/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b h1:oxCq0DvR2GMGf4UaoaASb0nQK/fJMQW3c3PNCLWCjS8=
|
github.com/jesseduffield/gocui v0.3.1-0.20250107151125-716b1eb82fb4 h1:hSAimLVb4b5ktU3uJRtBsZW0P2dtXECReTcsHYfOy58=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
|
github.com/jesseduffield/gocui v0.3.1-0.20250107151125-716b1eb82fb4/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a h1:UDeJ3EBk04bXDLOPvuqM3on8HvyJfISw0+UMqW+0a4g=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a/go.mod h1:FSWDLKT0NQpntbDd1H3lbz51fhCVlMzy/J0S6nM727Q=
|
||||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
|
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
||||||
@@ -235,8 +235,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-isatty v0.0.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 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
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.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 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
@@ -326,8 +327,9 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -400,8 +402,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -423,8 +425,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-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.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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.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-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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -474,14 +476,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.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.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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.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-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.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.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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -492,8 +494,8 @@ 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.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
|||||||
+15
-17
@@ -63,22 +63,20 @@ func Run(
|
|||||||
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
||||||
userConfig := config.GetUserConfig()
|
userConfig := config.GetUserConfig()
|
||||||
appState := config.GetAppState()
|
appState := config.GetAppState()
|
||||||
|
|
||||||
var err error
|
|
||||||
log := newLogger(config)
|
log := newLogger(config)
|
||||||
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
|
// Initialize with English for the time being; the real translation set for
|
||||||
if err != nil {
|
// the configured language will be read after reading the user config
|
||||||
return nil, err
|
tr := i18n.EnglishTranslationSet()
|
||||||
}
|
|
||||||
|
|
||||||
return &common.Common{
|
cmn := &common.Common{
|
||||||
Log: log,
|
Log: log,
|
||||||
Tr: tr,
|
Tr: tr,
|
||||||
UserConfig: userConfig,
|
AppState: appState,
|
||||||
AppState: appState,
|
Debug: config.GetDebug(),
|
||||||
Debug: config.GetDebug(),
|
Fs: afero.NewOsFs(),
|
||||||
Fs: afero.NewOsFs(),
|
}
|
||||||
}, nil
|
cmn.SetUserConfig(userConfig)
|
||||||
|
return cmn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
|
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
|
||||||
@@ -118,11 +116,11 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
|
|||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not in a repo, repoPaths will be nil. The error is moot for us
|
// 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
|
// at this stage, since we'll try to init a new repo in setupRepo(), below
|
||||||
repoPaths, err := git_commands.GetRepoPaths(app.OSCommand.Cmd, gitVersion)
|
repoPaths, err := git_commands.GetRepoPaths(app.OSCommand.Cmd, gitVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
common.Log.Infof("Error getting repo paths: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
showRecentRepos, err := app.setupRepo(repoPaths)
|
showRecentRepos, err := app.setupRepo(repoPaths)
|
||||||
@@ -195,7 +193,7 @@ func (app *App) setupRepo(
|
|||||||
|
|
||||||
var shouldInitRepo bool
|
var shouldInitRepo bool
|
||||||
initialBranchArg := ""
|
initialBranchArg := ""
|
||||||
switch app.UserConfig.NotARepository {
|
switch app.UserConfig().NotARepository {
|
||||||
case "prompt":
|
case "prompt":
|
||||||
// Offer to initialize a new repository in current directory.
|
// Offer to initialize a new repository in current directory.
|
||||||
fmt.Print(app.Tr.CreateRepo)
|
fmt.Print(app.Tr.CreateRepo)
|
||||||
|
|||||||
+42
-18
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/stefanhaller/git-todo-parser/todo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sometimes lazygit will be invoked in daemon mode from a parent lazygit process.
|
// Sometimes lazygit will be invoked in daemon mode from a parent lazygit process.
|
||||||
@@ -39,6 +38,7 @@ const (
|
|||||||
DaemonKindMoveTodosDown
|
DaemonKindMoveTodosDown
|
||||||
DaemonKindInsertBreak
|
DaemonKindInsertBreak
|
||||||
DaemonKindChangeTodoActions
|
DaemonKindChangeTodoActions
|
||||||
|
DaemonKindDropMergeCommit
|
||||||
DaemonKindMoveFixupCommitDown
|
DaemonKindMoveFixupCommitDown
|
||||||
DaemonKindWriteRebaseTodo
|
DaemonKindWriteRebaseTodo
|
||||||
)
|
)
|
||||||
@@ -58,6 +58,7 @@ func getInstruction() Instruction {
|
|||||||
DaemonKindRemoveUpdateRefsForCopiedBranch: deserializeInstruction[*RemoveUpdateRefsForCopiedBranchInstruction],
|
DaemonKindRemoveUpdateRefsForCopiedBranch: deserializeInstruction[*RemoveUpdateRefsForCopiedBranchInstruction],
|
||||||
DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction],
|
DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction],
|
||||||
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
|
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
|
||||||
|
DaemonKindDropMergeCommit: deserializeInstruction[*DropMergeCommitInstruction],
|
||||||
DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction],
|
DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction],
|
||||||
DaemonKindMoveTodosUp: deserializeInstruction[*MoveTodosUpInstruction],
|
DaemonKindMoveTodosUp: deserializeInstruction[*MoveTodosUpInstruction],
|
||||||
DaemonKindMoveTodosDown: deserializeInstruction[*MoveTodosDownInstruction],
|
DaemonKindMoveTodosDown: deserializeInstruction[*MoveTodosDownInstruction],
|
||||||
@@ -235,7 +236,6 @@ func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
|
|||||||
changes := lo.Map(self.Changes, func(c ChangeTodoAction, _ int) utils.TodoChange {
|
changes := lo.Map(self.Changes, func(c ChangeTodoAction, _ int) utils.TodoChange {
|
||||||
return utils.TodoChange{
|
return utils.TodoChange{
|
||||||
Hash: c.Hash,
|
Hash: c.Hash,
|
||||||
OldAction: todo.Pick,
|
|
||||||
NewAction: c.NewAction,
|
NewAction: c.NewAction,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -244,18 +244,44 @@ func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes the hash of some commit, and the hash of a fixup commit that was created
|
type DropMergeCommitInstruction struct {
|
||||||
// at the end of the branch, then moves the fixup commit down to right after the
|
Hash string
|
||||||
// original commit, changing its type to "fixup"
|
|
||||||
type MoveFixupCommitDownInstruction struct {
|
|
||||||
OriginalHash string
|
|
||||||
FixupHash string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMoveFixupCommitDownInstruction(originalHash string, fixupHash string) Instruction {
|
func NewDropMergeCommitInstruction(hash string) Instruction {
|
||||||
|
return &DropMergeCommitInstruction{
|
||||||
|
Hash: hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DropMergeCommitInstruction) Kind() DaemonKind {
|
||||||
|
return DaemonKindDropMergeCommit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DropMergeCommitInstruction) SerializedInstructions() string {
|
||||||
|
return serializeInstruction(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DropMergeCommitInstruction) run(common *common.Common) error {
|
||||||
|
return handleInteractiveRebase(common, func(path string) error {
|
||||||
|
return utils.DropMergeCommit(path, self.Hash, getCommentChar())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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" (only if ChangeToFixup is true)
|
||||||
|
type MoveFixupCommitDownInstruction struct {
|
||||||
|
OriginalHash string
|
||||||
|
FixupHash string
|
||||||
|
ChangeToFixup bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMoveFixupCommitDownInstruction(originalHash string, fixupHash string, changeToFixup bool) Instruction {
|
||||||
return &MoveFixupCommitDownInstruction{
|
return &MoveFixupCommitDownInstruction{
|
||||||
OriginalHash: originalHash,
|
OriginalHash: originalHash,
|
||||||
FixupHash: fixupHash,
|
FixupHash: fixupHash,
|
||||||
|
ChangeToFixup: changeToFixup,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +295,7 @@ func (self *MoveFixupCommitDownInstruction) SerializedInstructions() string {
|
|||||||
|
|
||||||
func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
|
func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
|
||||||
return handleInteractiveRebase(common, func(path string) 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())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,13 +320,12 @@ func (self *MoveTodosUpInstruction) SerializedInstructions() string {
|
|||||||
func (self *MoveTodosUpInstruction) run(common *common.Common) error {
|
func (self *MoveTodosUpInstruction) run(common *common.Common) error {
|
||||||
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
|
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
|
||||||
return utils.Todo{
|
return utils.Todo{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Action: todo.Pick,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return handleInteractiveRebase(common, func(path string) error {
|
return handleInteractiveRebase(common, func(path string) error {
|
||||||
return utils.MoveTodosUp(path, todosToMove, getCommentChar())
|
return utils.MoveTodosUp(path, todosToMove, false, getCommentChar())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,13 +350,12 @@ func (self *MoveTodosDownInstruction) SerializedInstructions() string {
|
|||||||
func (self *MoveTodosDownInstruction) run(common *common.Common) error {
|
func (self *MoveTodosDownInstruction) run(common *common.Common) error {
|
||||||
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
|
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
|
||||||
return utils.Todo{
|
return utils.Todo{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Action: todo.Pick,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return handleInteractiveRebase(common, func(path string) error {
|
return handleInteractiveRebase(common, func(path string) error {
|
||||||
return utils.MoveTodosDown(path, todosToMove, getCommentChar())
|
return utils.MoveTodosDown(path, todosToMove, false, getCommentChar())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-5
@@ -29,16 +29,17 @@ type cliArgs struct {
|
|||||||
RepoPath string
|
RepoPath string
|
||||||
FilterPath string
|
FilterPath string
|
||||||
GitArg string
|
GitArg string
|
||||||
|
UseConfigDir string
|
||||||
|
WorkTree string
|
||||||
|
GitDir string
|
||||||
|
CustomConfigFile string
|
||||||
|
ScreenMode string
|
||||||
PrintVersionInfo bool
|
PrintVersionInfo bool
|
||||||
Debug bool
|
Debug bool
|
||||||
TailLogs bool
|
TailLogs bool
|
||||||
Profile bool
|
Profile bool
|
||||||
PrintDefaultConfig bool
|
PrintDefaultConfig bool
|
||||||
PrintConfigDir bool
|
PrintConfigDir bool
|
||||||
UseConfigDir string
|
|
||||||
WorkTree string
|
|
||||||
GitDir string
|
|
||||||
CustomConfigFile string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuildInfo struct {
|
type BuildInfo struct {
|
||||||
@@ -136,6 +137,12 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
|||||||
|
|
||||||
if integrationTest != nil {
|
if integrationTest != nil {
|
||||||
integrationTest.SetupConfig(appConfig)
|
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)
|
common, err := NewCommon(appConfig)
|
||||||
@@ -158,7 +165,7 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
|||||||
|
|
||||||
parsedGitArg := parseGitArg(cliArgs.GitArg)
|
parsedGitArg := parseGitArg(cliArgs.GitArg)
|
||||||
|
|
||||||
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest))
|
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, cliArgs.ScreenMode, integrationTest))
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCliArgsAndEnvVars() *cliArgs {
|
func parseCliArgsAndEnvVars() *cliArgs {
|
||||||
@@ -203,6 +210,9 @@ func parseCliArgsAndEnvVars() *cliArgs {
|
|||||||
customConfigFile := ""
|
customConfigFile := ""
|
||||||
flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)")
|
flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)")
|
||||||
|
|
||||||
|
screenMode := ""
|
||||||
|
flaggy.String(&screenMode, "sm", "screen-mode", "The initial screen-mode, which determines the size of the focused panel. Valid options: 'normal' (default), 'half', 'full'")
|
||||||
|
|
||||||
flaggy.Parse()
|
flaggy.Parse()
|
||||||
|
|
||||||
if os.Getenv("DEBUG") == "TRUE" {
|
if os.Getenv("DEBUG") == "TRUE" {
|
||||||
@@ -223,6 +233,7 @@ func parseCliArgsAndEnvVars() *cliArgs {
|
|||||||
WorkTree: workTree,
|
WorkTree: workTree,
|
||||||
GitDir: gitDir,
|
GitDir: gitDir,
|
||||||
CustomConfigFile: customConfigFile,
|
CustomConfigFile: customConfigFile,
|
||||||
|
ScreenMode: screenMode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import (
|
|||||||
|
|
||||||
// StartArgs is the struct that represents some things we want to do on program start
|
// StartArgs is the struct that represents some things we want to do on program start
|
||||||
type StartArgs struct {
|
type StartArgs struct {
|
||||||
// FilterPath determines which path we're going to filter on so that we only see commits from that file.
|
|
||||||
FilterPath string
|
|
||||||
// GitArg determines what context we open in
|
// GitArg determines what context we open in
|
||||||
GitArg GitArg
|
GitArg GitArg
|
||||||
// integration test (only relevant when invoking lazygit in the context of an integration test)
|
// integration test (only relevant when invoking lazygit in the context of an integration test)
|
||||||
IntegrationTest integrationTypes.IntegrationTest
|
IntegrationTest integrationTypes.IntegrationTest
|
||||||
|
// FilterPath determines which path we're going to filter on so that we only see commits from that file.
|
||||||
|
FilterPath string
|
||||||
|
// ScreenMode determines the initial Screen Mode (normal, half or full) to use
|
||||||
|
ScreenMode string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GitArg string
|
type GitArg string
|
||||||
@@ -24,10 +26,11 @@ const (
|
|||||||
GitArgStash GitArg = "stash"
|
GitArgStash GitArg = "stash"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewStartArgs(filterPath string, gitArg GitArg, test integrationTypes.IntegrationTest) StartArgs {
|
func NewStartArgs(filterPath string, gitArg GitArg, screenMode string, test integrationTypes.IntegrationTest) StartArgs {
|
||||||
return StartArgs{
|
return StartArgs{
|
||||||
FilterPath: filterPath,
|
FilterPath: filterPath,
|
||||||
GitArg: gitArg,
|
GitArg: gitArg,
|
||||||
|
ScreenMode: screenMode,
|
||||||
IntegrationTest: test,
|
IntegrationTest: test,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,11 @@ func generateAtDir(cheatsheetDir string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
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)
|
mApp, _ := app.NewApp(mConfig, nil, common)
|
||||||
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
||||||
file, err := os.Create(path)
|
file, err := os.Create(path)
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
|
|||||||
return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar)
|
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 {
|
func (self *gitCmdObjBuilder) Quote(str string) string {
|
||||||
return self.innerBuilder.Quote(str)
|
return self.innerBuilder.Quote(str)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/mgutz/str"
|
"github.com/mgutz/str"
|
||||||
@@ -108,10 +109,10 @@ func (self *BranchCommands) CurrentBranchName() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LocalDelete delete branch locally
|
// LocalDelete delete branch locally
|
||||||
func (self *BranchCommands) LocalDelete(branch string, force bool) error {
|
func (self *BranchCommands) LocalDelete(branches []string, force bool) error {
|
||||||
cmdArgs := NewGitCmd("branch").
|
cmdArgs := NewGitCmd("branch").
|
||||||
ArgIfElse(force, "-D", "-d").
|
ArgIfElse(force, "-D", "-d").
|
||||||
Arg(branch).
|
Arg(branches...).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(cmdArgs).Run()
|
return self.cmd.New(cmdArgs).Run()
|
||||||
@@ -145,7 +146,7 @@ func (self *BranchCommands) GetGraph(branchName string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj {
|
func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj {
|
||||||
branchLogCmdTemplate := self.UserConfig.Git.BranchLogCmd
|
branchLogCmdTemplate := self.UserConfig().Git.BranchLogCmd
|
||||||
templateValues := map[string]string{
|
templateValues := map[string]string{
|
||||||
"branchName": self.cmd.Quote(branchName),
|
"branchName": self.cmd.Quote(branchName),
|
||||||
}
|
}
|
||||||
@@ -236,7 +237,7 @@ func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
|||||||
}
|
}
|
||||||
cmdArgs := NewGitCmd("merge").
|
cmdArgs := NewGitCmd("merge").
|
||||||
Arg("--no-edit").
|
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.FastForwardOnly, "--ff-only").
|
||||||
ArgIf(opts.Squash, "--squash", "--ff").
|
ArgIf(opts.Squash, "--squash", "--ff").
|
||||||
Arg(branchName).
|
Arg(branchName).
|
||||||
@@ -248,9 +249,9 @@ func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
|||||||
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
|
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
|
||||||
// Only choose between non-empty, non-identical commands
|
// Only choose between non-empty, non-identical commands
|
||||||
candidates := lo.Uniq(lo.WithoutEmpty(append([]string{
|
candidates := lo.Uniq(lo.WithoutEmpty(append([]string{
|
||||||
self.UserConfig.Git.AllBranchesLogCmd,
|
self.UserConfig().Git.AllBranchesLogCmd,
|
||||||
},
|
},
|
||||||
self.UserConfig.Git.AllBranchesLogCmds...,
|
self.UserConfig().Git.AllBranchesLogCmds...,
|
||||||
)))
|
)))
|
||||||
|
|
||||||
n := len(candidates)
|
n := len(candidates)
|
||||||
@@ -260,3 +261,26 @@ func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
|
|||||||
|
|
||||||
return self.cmd.New(str.ToArgv(candidates[i])).DontLog()
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if loadBehindCounts && self.UserConfig.Gui.ShowDivergenceFromBaseBranch != "none" {
|
if loadBehindCounts && self.UserConfig().Gui.ShowDivergenceFromBaseBranch != "none" {
|
||||||
onWorker(func() error {
|
onWorker(func() error {
|
||||||
return self.GetBehindBaseBranchValuesForAllBranches(branches, mainBranches, renderFunc)
|
return self.GetBehindBaseBranchValuesForAllBranches(branches, mainBranches, renderFunc)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -62,36 +62,57 @@ func TestBranchNewBranch(t *testing.T) {
|
|||||||
|
|
||||||
func TestBranchDeleteBranch(t *testing.T) {
|
func TestBranchDeleteBranch(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
force bool
|
branchNames []string
|
||||||
runner *oscommands.FakeCmdObjRunner
|
force bool
|
||||||
test func(error)
|
runner *oscommands.FakeCmdObjRunner
|
||||||
|
test func(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"Delete a branch",
|
"Delete a branch",
|
||||||
|
[]string{"test"},
|
||||||
false,
|
false,
|
||||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-d", "test"}, "", nil),
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-d", "test"}, "", nil),
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Delete multiple branches",
|
||||||
|
[]string{"test1", "test2", "test3"},
|
||||||
|
false,
|
||||||
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-d", "test1", "test2", "test3"}, "", nil),
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Force delete a branch",
|
"Force delete a branch",
|
||||||
|
[]string{"test"},
|
||||||
true,
|
true,
|
||||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-D", "test"}, "", nil),
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-D", "test"}, "", nil),
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Force delete multiple branches",
|
||||||
|
[]string{"test1", "test2", "test3"},
|
||||||
|
true,
|
||||||
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-D", "test1", "test2", "test3"}, "", nil),
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
instance := buildBranchCommands(commonDeps{runner: s.runner})
|
instance := buildBranchCommands(commonDeps{runner: s.runner})
|
||||||
|
|
||||||
s.test(instance.LocalDelete("test", s.force))
|
s.test(instance.LocalDelete(s.branchNames, s.force))
|
||||||
s.runner.CheckForMissingCalls()
|
s.runner.CheckForMissingCalls()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func (self *CommitCommands) ResetToCommit(hash string, strength string, envVars
|
|||||||
func (self *CommitCommands) CommitCmdObj(summary string, description string) oscommands.ICmdObj {
|
func (self *CommitCommands) CommitCmdObj(summary string, description string) oscommands.ICmdObj {
|
||||||
messageArgs := self.commitMessageArgs(summary, description)
|
messageArgs := self.commitMessageArgs(summary, description)
|
||||||
|
|
||||||
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
|
skipHookPrefix := self.UserConfig().Git.SkipHookPrefix
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("commit").
|
cmdArgs := NewGitCmd("commit").
|
||||||
ArgIf(skipHookPrefix != "" && strings.HasPrefix(summary, skipHookPrefix), "--no-verify").
|
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
|
// 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)
|
messageArgs := self.commitMessageArgs(summary, description)
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("commit").
|
cmdArgs := NewGitCmd("commit").
|
||||||
@@ -125,7 +125,7 @@ func (self *CommitCommands) RewordLastCommit(summary string, description string)
|
|||||||
Arg(messageArgs...).
|
Arg(messageArgs...).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(cmdArgs).Run()
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) commitMessageArgs(summary string, description string) []string {
|
func (self *CommitCommands) commitMessageArgs(summary string, description string) []string {
|
||||||
@@ -148,7 +148,7 @@ func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) signoffFlag() string {
|
func (self *CommitCommands) signoffFlag() string {
|
||||||
if self.UserConfig.Git.Commit.SignOff {
|
if self.UserConfig().Git.Commit.SignOff {
|
||||||
return "--signoff"
|
return "--signoff"
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return ""
|
||||||
@@ -189,7 +189,7 @@ type Author struct {
|
|||||||
|
|
||||||
func (self *CommitCommands) GetCommitAuthor(commitHash string) (Author, error) {
|
func (self *CommitCommands) GetCommitAuthor(commitHash string) (Author, error) {
|
||||||
cmdArgs := NewGitCmd("show").
|
cmdArgs := NewGitCmd("show").
|
||||||
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitHash).
|
Arg("--no-patch", "--pretty=format:%an%x00%ae", commitHash).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
@@ -258,13 +258,13 @@ func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
|
|||||||
func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommands.ICmdObj {
|
func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommands.ICmdObj {
|
||||||
contextSize := self.AppState.DiffContextSize
|
contextSize := self.AppState.DiffContextSize
|
||||||
|
|
||||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||||
cmdArgs := NewGitCmd("show").
|
cmdArgs := NewGitCmd("show").
|
||||||
Config("diff.noprefix=false").
|
Config("diff.noprefix=false").
|
||||||
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
|
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
|
||||||
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff").
|
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff").
|
||||||
Arg("--submodule").
|
Arg("--submodule").
|
||||||
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
|
Arg("--color="+self.UserConfig().Git.Paging.ColorArg).
|
||||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||||
Arg("--stat").
|
Arg("--stat").
|
||||||
Arg("--decorate").
|
Arg("--decorate").
|
||||||
|
|||||||
@@ -322,9 +322,9 @@ func TestGetCommits(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
common.UserConfig.Git.MainBranches = scenario.mainBranches
|
common.UserConfig().Git.MainBranches = scenario.mainBranches
|
||||||
opts := scenario.opts
|
opts := scenario.opts
|
||||||
opts.MainBranches = NewMainBranches(scenario.mainBranches, cmd)
|
opts.MainBranches = NewMainBranches(common, cmd)
|
||||||
commits, err := builder.GetCommits(opts)
|
commits, err := builder.GetCommits(opts)
|
||||||
|
|
||||||
assert.Equal(t, scenario.expectedCommits, commits)
|
assert.Equal(t, scenario.expectedCommits, commits)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func TestCommitRewordCommit(t *testing.T) {
|
|||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
instance := buildCommitCommands(commonDeps{runner: s.runner})
|
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()
|
s.runner.CheckForMissingCalls()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
filterPath string
|
filterPath string
|
||||||
contextSize int
|
contextSize uint64
|
||||||
similarityThreshold int
|
similarityThreshold int
|
||||||
ignoreWhitespace bool
|
ignoreWhitespace bool
|
||||||
extDiffCmd string
|
extDiffCmd string
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func (self *ConfigCommands) ConfiguredPager() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *ConfigCommands) GetPager(width int) string {
|
func (self *ConfigCommands) GetPager(width int) string {
|
||||||
useConfig := self.UserConfig.Git.Paging.UseConfig
|
useConfig := self.UserConfig().Git.Paging.UseConfig
|
||||||
if useConfig {
|
if useConfig {
|
||||||
pager := self.ConfiguredPager()
|
pager := self.ConfiguredPager()
|
||||||
return strings.Split(pager, "| less")[0]
|
return strings.Split(pager, "| less")[0]
|
||||||
@@ -53,14 +53,14 @@ func (self *ConfigCommands) GetPager(width int) string {
|
|||||||
"columnWidth": strconv.Itoa(width/2 - 6),
|
"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)
|
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsingGpg tells us whether the user has gpg enabled so that we can know
|
// 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
|
// whether we need to run a subprocess to allow them to enter their password
|
||||||
func (self *ConfigCommands) UsingGpg() bool {
|
func (self *ConfigCommands) UsingGpg() bool {
|
||||||
overrideGpg := self.UserConfig.Git.OverrideGpg
|
overrideGpg := self.UserConfig().Git.OverrideGpg
|
||||||
if overrideGpg {
|
if overrideGpg {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ func buildGitCommon(deps commonDeps) *GitCommon {
|
|||||||
}
|
}
|
||||||
gitCommon.cmd = cmd
|
gitCommon.cmd = cmd
|
||||||
|
|
||||||
gitCommon.Common.UserConfig = deps.userConfig
|
gitCommon.Common.SetUserConfig(deps.userConfig)
|
||||||
if gitCommon.Common.UserConfig == nil {
|
if gitCommon.Common.UserConfig() == nil {
|
||||||
gitCommon.Common.UserConfig = config.GetDefaultConfig()
|
gitCommon.Common.SetUserConfig(config.GetDefaultConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
gitCommon.version = deps.gitVersion
|
gitCommon.version = deps.gitVersion
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is for generating diffs to be shown in the UI (e.g. rendering a range
|
||||||
|
// diff to the main view). It uses a custom pager if one is configured.
|
||||||
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||||
useExtDiff := extDiffCmd != ""
|
useExtDiff := extDiffCmd != ""
|
||||||
|
ignoreWhitespace := self.AppState.IgnoreWhitespaceInDiffView
|
||||||
|
|
||||||
return self.cmd.New(
|
return self.cmd.New(
|
||||||
NewGitCmd("diff").
|
NewGitCmd("diff").
|
||||||
@@ -26,34 +29,30 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
|||||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||||
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
|
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
|
||||||
Arg("--submodule").
|
Arg("--submodule").
|
||||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
|
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
|
||||||
|
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||||
|
Arg(fmt.Sprintf("--unified=%d", self.AppState.DiffContextSize)).
|
||||||
Arg(diffArgs...).
|
Arg(diffArgs...).
|
||||||
Dir(self.repoPaths.worktreePath).
|
Dir(self.repoPaths.worktreePath).
|
||||||
ToArgv(),
|
ToArgv(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *DiffCommands) internalDiffCmdObj(diffArgs ...string) *GitCommandBuilder {
|
// This is a basic generic diff command that can be used for any diff operation
|
||||||
return NewGitCmd("diff").
|
// (e.g. copying a diff to the clipboard). It will not use a custom pager, and
|
||||||
Config("diff.noprefix=false").
|
// does not use user configs such as ignore whitespace.
|
||||||
Arg("--no-ext-diff", "--no-color").
|
// If you want to diff specific refs (one or two), you need to add them yourself
|
||||||
Arg(diffArgs...).
|
// in additionalArgs; it is recommended to also pass `--` after that. If you
|
||||||
Dir(self.repoPaths.worktreePath)
|
// want to restrict the diff to specific paths, pass them in additionalArgs
|
||||||
}
|
// after the `--`.
|
||||||
|
func (self *DiffCommands) GetDiff(staged bool, additionalArgs ...string) (string, error) {
|
||||||
func (self *DiffCommands) GetPathDiff(path string, staged bool) (string, error) {
|
|
||||||
return self.cmd.New(
|
return self.cmd.New(
|
||||||
self.internalDiffCmdObj().
|
NewGitCmd("diff").
|
||||||
ArgIf(staged, "--staged").
|
Config("diff.noprefix=false").
|
||||||
Arg(path).
|
Arg("--no-ext-diff", "--no-color").
|
||||||
ToArgv(),
|
|
||||||
).RunWithOutput()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *DiffCommands) GetAllDiff(staged bool) (string, error) {
|
|
||||||
return self.cmd.New(
|
|
||||||
self.internalDiffCmdObj().
|
|
||||||
ArgIf(staged, "--staged").
|
ArgIf(staged, "--staged").
|
||||||
|
Dir(self.repoPaths.worktreePath).
|
||||||
|
Arg(additionalArgs...).
|
||||||
ToArgv(),
|
ToArgv(),
|
||||||
).RunWithOutput()
|
).RunWithOutput()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (self *FileCommands) Cat(fileName string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (string, error) {
|
func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (string, error) {
|
||||||
editor := self.UserConfig.OS.EditCommand
|
editor := self.UserConfig().OS.EditCommand
|
||||||
|
|
||||||
if editor == "" {
|
if editor == "" {
|
||||||
editor = self.config.GetCoreEditor()
|
editor = self.config.GetCoreEditor()
|
||||||
@@ -60,7 +60,7 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
|
|||||||
"line": strconv.Itoa(lineNumber),
|
"line": strconv.Itoa(lineNumber),
|
||||||
}
|
}
|
||||||
|
|
||||||
editCmdTemplate := self.UserConfig.OS.EditCommandTemplate
|
editCmdTemplate := self.UserConfig().OS.EditCommandTemplate
|
||||||
if len(editCmdTemplate) == 0 {
|
if len(editCmdTemplate) == 0 {
|
||||||
switch editor {
|
switch editor {
|
||||||
case "emacs", "nano", "vi", "vim", "nvim":
|
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) {
|
func (self *FileCommands) GetEditCmdStr(filenames []string) (string, bool) {
|
||||||
// Legacy support for old config; to be removed at some point
|
// 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.
|
// If multiple files are selected, we'll simply edit just the first one.
|
||||||
// It's not worth fixing this for the legacy support.
|
// It's not worth fixing this for the legacy support.
|
||||||
if cmdStr, err := self.GetEditCmdStrLegacy(filenames[0], 1); err == nil {
|
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) })
|
quotedFilenames := lo.Map(filenames, func(filename string, _ int) string { return self.cmd.Quote(filename) })
|
||||||
|
|
||||||
templateValues := map[string]string{
|
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) {
|
func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (string, bool) {
|
||||||
// Legacy support for old config; to be removed at some point
|
// 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 {
|
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
|
||||||
return cmdStr, true
|
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{
|
templateValues := map[string]string{
|
||||||
"filename": self.cmd.Quote(filename),
|
"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 {
|
func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber int) string {
|
||||||
// Legacy support for old config; to be removed at some point
|
// 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 {
|
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
|
||||||
return cmdStr
|
return cmdStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
|
||||||
|
|
||||||
templateValues := map[string]string{
|
templateValues := map[string]string{
|
||||||
"filename": self.cmd.Quote(filename),
|
"filename": self.cmd.Quote(filename),
|
||||||
@@ -136,7 +136,7 @@ func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) (string, bool) {
|
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{
|
templateValues := map[string]string{
|
||||||
"dir": self.cmd.Quote(path),
|
"dir": self.cmd.Quote(path),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package git_commands
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
@@ -48,6 +49,14 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
|||||||
}
|
}
|
||||||
files := []*models.File{}
|
files := []*models.File{}
|
||||||
|
|
||||||
|
fileDiffs := map[string]FileDiff{}
|
||||||
|
if self.GitCommon.Common.UserConfig().Gui.ShowNumstatInFilesView {
|
||||||
|
fileDiffs, err = self.getFileDiffs()
|
||||||
|
if err != nil {
|
||||||
|
self.Log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, status := range statuses {
|
for _, status := range statuses {
|
||||||
if strings.HasPrefix(status.StatusString, "warning") {
|
if strings.HasPrefix(status.StatusString, "warning") {
|
||||||
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
||||||
@@ -60,6 +69,11 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
|||||||
DisplayString: status.StatusString,
|
DisplayString: status.StatusString,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if diff, ok := fileDiffs[status.Name]; ok {
|
||||||
|
file.LinesAdded = diff.LinesAdded
|
||||||
|
file.LinesDeleted = diff.LinesDeleted
|
||||||
|
}
|
||||||
|
|
||||||
models.SetStatusFields(file, status.Change)
|
models.SetStatusFields(file, status.Change)
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
@@ -87,6 +101,45 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
|||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileDiff struct {
|
||||||
|
LinesAdded int
|
||||||
|
LinesDeleted int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fileLoader *FileLoader) getFileDiffs() (map[string]FileDiff, error) {
|
||||||
|
diffs, err := fileLoader.gitDiffNumStat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
splitLines := strings.Split(diffs, "\x00")
|
||||||
|
|
||||||
|
fileDiffs := map[string]FileDiff{}
|
||||||
|
for _, line := range splitLines {
|
||||||
|
splitLine := strings.Split(line, "\t")
|
||||||
|
if len(splitLine) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
linesAdded, err := strconv.Atoi(splitLine[0])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
linesDeleted, err := strconv.Atoi(splitLine[1])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := splitLine[2]
|
||||||
|
fileDiffs[fileName] = FileDiff{
|
||||||
|
LinesAdded: linesAdded,
|
||||||
|
LinesDeleted: linesDeleted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileDiffs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GitStatus returns the file status of the repo
|
// GitStatus returns the file status of the repo
|
||||||
type GitStatusOptions struct {
|
type GitStatusOptions struct {
|
||||||
NoRenames bool
|
NoRenames bool
|
||||||
@@ -100,6 +153,16 @@ type FileStatus struct {
|
|||||||
PreviousName string
|
PreviousName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fileLoader *FileLoader) gitDiffNumStat() (string, error) {
|
||||||
|
return fileLoader.cmd.New(
|
||||||
|
NewGitCmd("diff").
|
||||||
|
Arg("--numstat").
|
||||||
|
Arg("-z").
|
||||||
|
Arg("HEAD").
|
||||||
|
ToArgv(),
|
||||||
|
).DontLog().RunWithOutput()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||||
cmdArgs := NewGitCmd("status").
|
cmdArgs := NewGitCmd("status").
|
||||||
Arg(opts.UntrackedFilesArg).
|
Arg(opts.UntrackedFilesArg).
|
||||||
|
|||||||
@@ -11,29 +11,35 @@ import (
|
|||||||
|
|
||||||
func TestFileGetStatusFiles(t *testing.T) {
|
func TestFileGetStatusFiles(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
similarityThreshold int
|
similarityThreshold int
|
||||||
runner oscommands.ICmdObjRunner
|
runner oscommands.ICmdObjRunner
|
||||||
expectedFiles []*models.File
|
showNumstatInFilesView bool
|
||||||
|
expectedFiles []*models.File
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"No files found",
|
testName: "No files found",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
|
||||||
[]*models.File{},
|
expectedFiles: []*models.File{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Several files found",
|
testName: "Several files found",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
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",
|
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
|
||||||
nil,
|
nil,
|
||||||
|
).
|
||||||
|
ExpectGitArgs([]string{"diff", "--numstat", "-z", "HEAD"},
|
||||||
|
"4\t1\tfile1.txt\x001\t0\tfile2.txt\x002\t2\tfile3.txt\x000\t2\tfile4.txt\x002\t2\tfile5.txt",
|
||||||
|
nil,
|
||||||
),
|
),
|
||||||
[]*models.File{
|
showNumstatInFilesView: true,
|
||||||
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "file1.txt",
|
Name: "file1.txt",
|
||||||
HasStagedChanges: true,
|
HasStagedChanges: true,
|
||||||
@@ -45,6 +51,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "MM file1.txt",
|
DisplayString: "MM file1.txt",
|
||||||
ShortStatus: "MM",
|
ShortStatus: "MM",
|
||||||
|
LinesAdded: 4,
|
||||||
|
LinesDeleted: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file3.txt",
|
Name: "file3.txt",
|
||||||
@@ -57,6 +65,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "A file3.txt",
|
DisplayString: "A file3.txt",
|
||||||
ShortStatus: "A ",
|
ShortStatus: "A ",
|
||||||
|
LinesAdded: 2,
|
||||||
|
LinesDeleted: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file2.txt",
|
Name: "file2.txt",
|
||||||
@@ -69,6 +79,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "AM file2.txt",
|
DisplayString: "AM file2.txt",
|
||||||
ShortStatus: "AM",
|
ShortStatus: "AM",
|
||||||
|
LinesAdded: 1,
|
||||||
|
LinesDeleted: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file4.txt",
|
Name: "file4.txt",
|
||||||
@@ -81,6 +93,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "?? file4.txt",
|
DisplayString: "?? file4.txt",
|
||||||
ShortStatus: "??",
|
ShortStatus: "??",
|
||||||
|
LinesAdded: 0,
|
||||||
|
LinesDeleted: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file5.txt",
|
Name: "file5.txt",
|
||||||
@@ -93,15 +107,17 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
HasInlineMergeConflicts: true,
|
HasInlineMergeConflicts: true,
|
||||||
DisplayString: "UU file5.txt",
|
DisplayString: "UU file5.txt",
|
||||||
ShortStatus: "UU",
|
ShortStatus: "UU",
|
||||||
|
LinesAdded: 2,
|
||||||
|
LinesDeleted: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"File with new line char",
|
testName: "File with new line char",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
|
||||||
[]*models.File{
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "a\nb.txt",
|
Name: "a\nb.txt",
|
||||||
HasStagedChanges: true,
|
HasStagedChanges: true,
|
||||||
@@ -117,14 +133,14 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Renamed files",
|
testName: "Renamed files",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||||
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
|
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
[]*models.File{
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "after1.txt",
|
Name: "after1.txt",
|
||||||
PreviousName: "before1.txt",
|
PreviousName: "before1.txt",
|
||||||
@@ -154,14 +170,14 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"File with arrow in name",
|
testName: "File with arrow in name",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||||
`?? a -> b.txt`,
|
`?? a -> b.txt`,
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
[]*models.File{
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "a -> b.txt",
|
Name: "a -> b.txt",
|
||||||
HasStagedChanges: false,
|
HasStagedChanges: false,
|
||||||
@@ -185,8 +201,14 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
appState := &config.AppState{}
|
appState := &config.AppState{}
|
||||||
appState.RenameSimilarityThreshold = s.similarityThreshold
|
appState.RenameSimilarityThreshold = s.similarityThreshold
|
||||||
|
|
||||||
|
userConfig := &config.UserConfig{
|
||||||
|
Gui: config.GuiConfig{
|
||||||
|
ShowNumstatInFilesView: s.showNumstatInFilesView,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
loader := &FileLoader{
|
loader := &FileLoader{
|
||||||
GitCommon: buildGitCommon(commonDeps{appState: appState}),
|
GitCommon: buildGitCommon(commonDeps{appState: appState, userConfig: userConfig}),
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
|
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
|
||||||
getFileType: func(string) string { return "file" },
|
getFileType: func(string) string { return "file" },
|
||||||
|
|||||||
@@ -76,6 +76,14 @@ func (self *GitCommandBuilder) Worktree(path string) *GitCommandBuilder {
|
|||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) WorktreePathIf(condition bool, path string) *GitCommandBuilder {
|
||||||
|
if condition {
|
||||||
|
return self.Worktree(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
// Note, you may prefer to use the Dir method instead of this one
|
// Note, you may prefer to use the Dir method instead of this one
|
||||||
func (self *GitCommandBuilder) GitDir(path string) *GitCommandBuilder {
|
func (self *GitCommandBuilder) GitDir(path string) *GitCommandBuilder {
|
||||||
// git dir arg comes before the command
|
// git dir arg comes before the command
|
||||||
|
|||||||
@@ -5,32 +5,34 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/sasha-s/go-deadlock"
|
"github.com/sasha-s/go-deadlock"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MainBranches struct {
|
type MainBranches struct {
|
||||||
// List of main branches configured by the user. Just the bare names.
|
c *common.Common
|
||||||
configuredMainBranches []string
|
// Which of the configured main branches actually exist in the repository. Full
|
||||||
// Which of these actually exist in the repository. Full ref names, and it
|
// ref names, and it could be either "refs/heads/..." or "refs/remotes/origin/..."
|
||||||
// could be either "refs/heads/..." or "refs/remotes/origin/..." depending
|
// depending on which one exists for a given bare name.
|
||||||
// on which one exists for a given bare name.
|
|
||||||
existingMainBranches []string
|
existingMainBranches []string
|
||||||
|
|
||||||
|
previousMainBranches []string
|
||||||
|
|
||||||
cmd oscommands.ICmdObjBuilder
|
cmd oscommands.ICmdObjBuilder
|
||||||
mutex *deadlock.Mutex
|
mutex *deadlock.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMainBranches(
|
func NewMainBranches(
|
||||||
configuredMainBranches []string,
|
cmn *common.Common,
|
||||||
cmd oscommands.ICmdObjBuilder,
|
cmd oscommands.ICmdObjBuilder,
|
||||||
) *MainBranches {
|
) *MainBranches {
|
||||||
return &MainBranches{
|
return &MainBranches{
|
||||||
configuredMainBranches: configuredMainBranches,
|
c: cmn,
|
||||||
existingMainBranches: nil,
|
existingMainBranches: nil,
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
mutex: &deadlock.Mutex{},
|
mutex: &deadlock.Mutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +42,11 @@ func (self *MainBranches) Get() []string {
|
|||||||
self.mutex.Lock()
|
self.mutex.Lock()
|
||||||
defer self.mutex.Unlock()
|
defer self.mutex.Unlock()
|
||||||
|
|
||||||
if self.existingMainBranches == nil {
|
configuredMainBranches := self.c.UserConfig().Git.MainBranches
|
||||||
self.existingMainBranches = self.determineMainBranches()
|
|
||||||
|
if self.existingMainBranches == nil || !utils.EqualSlices(self.previousMainBranches, configuredMainBranches) {
|
||||||
|
self.existingMainBranches = self.determineMainBranches(configuredMainBranches)
|
||||||
|
self.previousMainBranches = configuredMainBranches
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.existingMainBranches
|
return self.existingMainBranches
|
||||||
@@ -71,13 +76,13 @@ func (self *MainBranches) GetMergeBase(refName string) string {
|
|||||||
return ignoringWarnings(output)
|
return ignoringWarnings(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MainBranches) determineMainBranches() []string {
|
func (self *MainBranches) determineMainBranches(configuredMainBranches []string) []string {
|
||||||
var existingBranches []string
|
var existingBranches []string
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
existingBranches = make([]string, len(self.configuredMainBranches))
|
existingBranches = make([]string, len(configuredMainBranches))
|
||||||
|
|
||||||
for i, branchName := range self.configuredMainBranches {
|
for i, branchName := range configuredMainBranches {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|||||||
@@ -35,9 +35,8 @@ func NewRebaseCommands(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, summary string, description string) error {
|
func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, summary string, description string) error {
|
||||||
if models.IsHeadCommit(commits, index) {
|
if self.config.UsingGpg() {
|
||||||
// we've selected the top commit so no rebase is required
|
return errors.New(self.Tr.DisabledForGPG)
|
||||||
return self.commit.RewordLastCommit(summary, description)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -146,11 +145,11 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, startIdx
|
|||||||
|
|
||||||
baseHashOrRoot := getBaseHashOrRoot(commits, baseIndex)
|
baseHashOrRoot := getBaseHashOrRoot(commits, baseIndex)
|
||||||
|
|
||||||
changes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) daemon.ChangeTodoAction {
|
changes := lo.FilterMap(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) (daemon.ChangeTodoAction, bool) {
|
||||||
return daemon.ChangeTodoAction{
|
return daemon.ChangeTodoAction{
|
||||||
Hash: commit.Hash,
|
Hash: commit.Hash,
|
||||||
NewAction: action,
|
NewAction: action,
|
||||||
}
|
}, !commit.IsMerge()
|
||||||
})
|
})
|
||||||
|
|
||||||
self.os.LogCommand(logTodoChanges(changes), false)
|
self.os.LogCommand(logTodoChanges(changes), false)
|
||||||
@@ -285,6 +284,11 @@ func (self *RebaseCommands) GitRebaseEditTodo(todosFileContent []byte) error {
|
|||||||
return cmdObj.Run()
|
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
|
// AmendTo amends the given commit with whatever files are staged
|
||||||
func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) error {
|
func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) error {
|
||||||
commit := commits[commitIndex]
|
commit := commits[commitIndex]
|
||||||
@@ -293,9 +297,7 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the hash of the commit we just created
|
fixupHash, err := self.getHashOfLastCommitMade()
|
||||||
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
|
|
||||||
fixupHash, err := self.cmd.New(cmdArgs).RunWithOutput()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -303,15 +305,28 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
|||||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||||
baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
|
baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
|
||||||
overrideEditor: true,
|
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()
|
}).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func todoFromCommit(commit *models.Commit) utils.Todo {
|
func todoFromCommit(commit *models.Commit) utils.Todo {
|
||||||
if commit.Action == todo.UpdateRef {
|
if commit.Action == todo.UpdateRef {
|
||||||
return utils.Todo{Ref: commit.Name, Action: commit.Action}
|
return utils.Todo{Ref: commit.Name}
|
||||||
} else {
|
} else {
|
||||||
return utils.Todo{Hash: commit.Hash, Action: commit.Action}
|
return utils.Todo{Hash: commit.Hash}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +335,6 @@ func (self *RebaseCommands) EditRebaseTodo(commits []*models.Commit, action todo
|
|||||||
commitsWithAction := lo.Map(commits, func(commit *models.Commit, _ int) utils.TodoChange {
|
commitsWithAction := lo.Map(commits, func(commit *models.Commit, _ int) utils.TodoChange {
|
||||||
return utils.TodoChange{
|
return utils.TodoChange{
|
||||||
Hash: commit.Hash,
|
Hash: commit.Hash,
|
||||||
OldAction: commit.Action,
|
|
||||||
NewAction: action,
|
NewAction: action,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -355,7 +369,7 @@ func (self *RebaseCommands) MoveTodosDown(commits []*models.Commit) error {
|
|||||||
return todoFromCommit(commit)
|
return todoFromCommit(commit)
|
||||||
})
|
})
|
||||||
|
|
||||||
return utils.MoveTodosDown(fileName, todosToMove, self.config.GetCoreCommentChar())
|
return utils.MoveTodosDown(fileName, todosToMove, true, self.config.GetCoreCommentChar())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
|
func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
|
||||||
@@ -364,7 +378,7 @@ func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
|
|||||||
return todoFromCommit(commit)
|
return todoFromCommit(commit)
|
||||||
})
|
})
|
||||||
|
|
||||||
return utils.MoveTodosUp(fileName, todosToMove, self.config.GetCoreCommentChar())
|
return utils.MoveTodosUp(fileName, todosToMove, true, self.config.GetCoreCommentChar())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
|
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
|
||||||
@@ -550,6 +564,13 @@ func (self *RebaseCommands) CherryPickCommitsDuringRebase(commits []*models.Comm
|
|||||||
return utils.PrependStrToTodoFile(filePath, []byte(todo))
|
return utils.PrependStrToTodoFile(filePath, []byte(todo))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *RebaseCommands) DropMergeCommit(commits []*models.Commit, commitIndex int) error {
|
||||||
|
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||||
|
baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
|
||||||
|
instruction: daemon.NewDropMergeCommitInstruction(commits[commitIndex].Hash),
|
||||||
|
}).Run()
|
||||||
|
}
|
||||||
|
|
||||||
// we can't start an interactive rebase from the first commit without passing the
|
// we can't start an interactive rebase from the first commit without passing the
|
||||||
// '--root' arg
|
// '--root' arg
|
||||||
func getBaseHashOrRoot(commits []*models.Commit, index int) string {
|
func getBaseHashOrRoot(commits []*models.Commit, index int) string {
|
||||||
|
|||||||
@@ -49,9 +49,10 @@ func (self *RemoteCommands) UpdateRemoteUrl(remoteName string, updatedUrl string
|
|||||||
return self.cmd.New(cmdArgs).Run()
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName string, branchName string) error {
|
func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName string, branchNames []string) error {
|
||||||
cmdArgs := NewGitCmd("push").
|
cmdArgs := NewGitCmd("push").
|
||||||
Arg(remoteName, "--delete", branchName).
|
Arg(remoteName, "--delete").
|
||||||
|
Arg(branchNames...).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package git_commands
|
|||||||
import (
|
import (
|
||||||
ioFs "io/fs"
|
ioFs "io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -64,9 +63,9 @@ func (self *RepoPaths) IsBareRepo() bool {
|
|||||||
func MockRepoPaths(currentPath string) *RepoPaths {
|
func MockRepoPaths(currentPath string) *RepoPaths {
|
||||||
return &RepoPaths{
|
return &RepoPaths{
|
||||||
worktreePath: currentPath,
|
worktreePath: currentPath,
|
||||||
worktreeGitDirPath: path.Join(currentPath, ".git"),
|
worktreeGitDirPath: filepath.Join(currentPath, ".git"),
|
||||||
repoPath: currentPath,
|
repoPath: currentPath,
|
||||||
repoGitDirPath: path.Join(currentPath, ".git"),
|
repoGitDirPath: filepath.Join(currentPath, ".git"),
|
||||||
repoName: "lazygit",
|
repoName: "lazygit",
|
||||||
isBareRepo: false,
|
isBareRepo: false,
|
||||||
}
|
}
|
||||||
@@ -116,9 +115,9 @@ func GetRepoPathsForDir(
|
|||||||
if isSubmodule {
|
if isSubmodule {
|
||||||
repoPath = worktreePath
|
repoPath = worktreePath
|
||||||
} else {
|
} else {
|
||||||
repoPath = path.Dir(repoGitDirPath)
|
repoPath = filepath.Dir(repoGitDirPath)
|
||||||
}
|
}
|
||||||
repoName := path.Base(repoPath)
|
repoName := filepath.Base(repoPath)
|
||||||
|
|
||||||
return &RepoPaths{
|
return &RepoPaths{
|
||||||
worktreePath: worktreePath,
|
worktreePath: worktreePath,
|
||||||
@@ -154,7 +153,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
|
|||||||
result := []string{}
|
result := []string{}
|
||||||
// For each directory in this path we're going to cat the `gitdir` file and append its contents to our result
|
// 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.
|
// 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
|
// ensure the directory exists
|
||||||
_, err := fs.Stat(worktreeGitDirsPath)
|
_, err := fs.Stat(worktreeGitDirsPath)
|
||||||
@@ -171,7 +170,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
gitDirPath := path.Join(currPath, "gitdir")
|
gitDirPath := filepath.Join(currPath, "gitdir")
|
||||||
gitDirBytes, err := afero.ReadFile(fs, gitDirPath)
|
gitDirBytes, err := afero.ReadFile(fs, gitDirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// ignoring error
|
// ignoring error
|
||||||
@@ -179,7 +178,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
|
|||||||
}
|
}
|
||||||
trimmedGitDir := strings.TrimSpace(string(gitDirBytes))
|
trimmedGitDir := strings.TrimSpace(string(gitDirBytes))
|
||||||
// removing the .git part
|
// removing the .git part
|
||||||
worktreeDir := path.Dir(trimmedGitDir)
|
worktreeDir := filepath.Dir(trimmedGitDir)
|
||||||
result = append(result, worktreeDir)
|
result = append(result, worktreeDir)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package git_commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
"github.com/samber/lo"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,7 +31,17 @@ func TestGetRepoPaths(t *testing.T) {
|
|||||||
Name: "typical case",
|
Name: "typical case",
|
||||||
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
||||||
// setup for main worktree
|
// 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
|
// --show-toplevel
|
||||||
"/path/to/repo",
|
"/path/to/repo",
|
||||||
// --git-dir
|
// --git-dir
|
||||||
@@ -39,28 +51,45 @@ func TestGetRepoPaths(t *testing.T) {
|
|||||||
// --is-bare-repository
|
// --is-bare-repository
|
||||||
"false",
|
"false",
|
||||||
// --show-superproject-working-tree
|
// --show-superproject-working-tree
|
||||||
}
|
})
|
||||||
runner.ExpectGitArgs(
|
runner.ExpectGitArgs(
|
||||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
||||||
strings.Join(expectedOutput, "\n"),
|
strings.Join(mockOutput, "\n"),
|
||||||
nil)
|
nil)
|
||||||
},
|
},
|
||||||
Path: "/path/to/repo",
|
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",
|
worktreePath: "/path/to/repo",
|
||||||
worktreeGitDirPath: "/path/to/repo/.git",
|
worktreeGitDirPath: "/path/to/repo/.git",
|
||||||
repoPath: "/path/to/repo",
|
repoPath: "/path/to/repo",
|
||||||
repoGitDirPath: "/path/to/repo/.git",
|
repoGitDirPath: "/path/to/repo/.git",
|
||||||
repoName: "repo",
|
repoName: "repo",
|
||||||
isBareRepo: false,
|
isBareRepo: false,
|
||||||
},
|
}),
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "bare repo",
|
Name: "bare repo",
|
||||||
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
||||||
// setup for main worktree
|
// setup for main worktree
|
||||||
expectedOutput := []string{
|
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
|
// --show-toplevel
|
||||||
"/path/to/repo",
|
"/path/to/repo",
|
||||||
// --git-dir
|
// --git-dir
|
||||||
@@ -70,27 +99,45 @@ func TestGetRepoPaths(t *testing.T) {
|
|||||||
// --is-bare-repository
|
// --is-bare-repository
|
||||||
"true",
|
"true",
|
||||||
// --show-superproject-working-tree
|
// --show-superproject-working-tree
|
||||||
}
|
})
|
||||||
runner.ExpectGitArgs(
|
runner.ExpectGitArgs(
|
||||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
||||||
strings.Join(expectedOutput, "\n"),
|
strings.Join(mockOutput, "\n"),
|
||||||
nil)
|
nil)
|
||||||
},
|
},
|
||||||
Path: "/path/to/repo",
|
Path: "/path/to/repo",
|
||||||
Expected: &RepoPaths{
|
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",
|
worktreePath: "/path/to/repo",
|
||||||
worktreeGitDirPath: "/path/to/bare_repo/bare.git",
|
worktreeGitDirPath: "/path/to/bare_repo/bare.git",
|
||||||
repoPath: "/path/to/bare_repo",
|
repoPath: "/path/to/bare_repo",
|
||||||
repoGitDirPath: "/path/to/bare_repo/bare.git",
|
repoGitDirPath: "/path/to/bare_repo/bare.git",
|
||||||
repoName: "bare_repo",
|
repoName: "bare_repo",
|
||||||
isBareRepo: true,
|
isBareRepo: true,
|
||||||
},
|
}),
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "submodule",
|
Name: "submodule",
|
||||||
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
|
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
|
// --show-toplevel
|
||||||
"/path/to/repo/submodule1",
|
"/path/to/repo/submodule1",
|
||||||
// --git-dir
|
// --git-dir
|
||||||
@@ -101,21 +148,28 @@ func TestGetRepoPaths(t *testing.T) {
|
|||||||
"false",
|
"false",
|
||||||
// --show-superproject-working-tree
|
// --show-superproject-working-tree
|
||||||
"/path/to/repo",
|
"/path/to/repo",
|
||||||
}
|
})
|
||||||
runner.ExpectGitArgs(
|
runner.ExpectGitArgs(
|
||||||
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
|
||||||
strings.Join(expectedOutput, "\n"),
|
strings.Join(mockOutput, "\n"),
|
||||||
nil)
|
nil)
|
||||||
},
|
},
|
||||||
Path: "/path/to/repo/submodule1",
|
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",
|
worktreePath: "/path/to/repo/submodule1",
|
||||||
worktreeGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
worktreeGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
||||||
repoPath: "/path/to/repo/submodule1",
|
repoPath: "/path/to/repo/submodule1",
|
||||||
repoGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
repoGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
||||||
repoName: "submodule1",
|
repoName: "submodule1",
|
||||||
isBareRepo: false,
|
isBareRepo: false,
|
||||||
},
|
}),
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
|
|||||||
cmdArgs := NewGitCmd("stash").Arg("show").
|
cmdArgs := NewGitCmd("stash").Arg("show").
|
||||||
Arg("-p").
|
Arg("-p").
|
||||||
Arg("--stat").
|
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)).
|
Arg(fmt.Sprintf("--unified=%d", self.AppState.DiffContextSize)).
|
||||||
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||||
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
|
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
index int
|
index int
|
||||||
contextSize int
|
contextSize uint64
|
||||||
similarityThreshold int
|
similarityThreshold int
|
||||||
ignoreWhitespace bool
|
ignoreWhitespace bool
|
||||||
expected []string
|
expected []string
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func (self *SyncCommands) fetchCommandBuilder(fetchAll bool) *GitCommandBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) FetchCmdObj(task gocui.Task) oscommands.ICmdObj {
|
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 := self.cmd.New(cmdArgs)
|
||||||
cmdObj.PromptOnCredentialRequest(task)
|
cmdObj.PromptOnCredentialRequest(task)
|
||||||
@@ -72,7 +72,7 @@ func (self *SyncCommands) Fetch(task gocui.Task) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) FetchBackgroundCmdObj() oscommands.ICmdObj {
|
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 := self.cmd.New(cmdArgs)
|
||||||
cmdObj.DontLog().FailOnCredentialRequest()
|
cmdObj.DontLog().FailOnCredentialRequest()
|
||||||
@@ -88,6 +88,7 @@ type PullOptions struct {
|
|||||||
BranchName string
|
BranchName string
|
||||||
FastForwardOnly bool
|
FastForwardOnly bool
|
||||||
WorktreeGitDir string
|
WorktreeGitDir string
|
||||||
|
WorktreePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
||||||
@@ -95,8 +96,9 @@ func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
|||||||
Arg("--no-edit").
|
Arg("--no-edit").
|
||||||
ArgIf(opts.FastForwardOnly, "--ff-only").
|
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||||
ArgIf(opts.RemoteName != "", opts.RemoteName).
|
ArgIf(opts.RemoteName != "", opts.RemoteName).
|
||||||
ArgIf(opts.BranchName != "", opts.BranchName).
|
ArgIf(opts.BranchName != "", "refs/heads/"+opts.BranchName).
|
||||||
GitDirIf(opts.WorktreeGitDir != "", opts.WorktreeGitDir).
|
GitDirIf(opts.WorktreeGitDir != "", opts.WorktreeGitDir).
|
||||||
|
WorktreePathIf(opts.WorktreePath != "", opts.WorktreePath).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
|
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
|
||||||
@@ -112,7 +114,7 @@ func (self *SyncCommands) FastForward(
|
|||||||
) error {
|
) error {
|
||||||
cmdArgs := self.fetchCommandBuilder(false).
|
cmdArgs := self.fetchCommandBuilder(false).
|
||||||
Arg(remoteName).
|
Arg(remoteName).
|
||||||
Arg(remoteBranchName + ":" + branchName).
|
Arg("refs/heads/" + remoteBranchName + ":" + branchName).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ func TestSyncFetch(t *testing.T) {
|
|||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
instance := buildSyncCommands(commonDeps{})
|
instance := buildSyncCommands(commonDeps{})
|
||||||
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
|
instance.UserConfig().Git.FetchAll = s.fetchAllConfig
|
||||||
task := gocui.NewFakeTask()
|
task := gocui.NewFakeTask()
|
||||||
s.test(instance.FetchCmdObj(task))
|
s.test(instance.FetchCmdObj(task))
|
||||||
})
|
})
|
||||||
@@ -171,7 +171,7 @@ func TestSyncFetchBackground(t *testing.T) {
|
|||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
instance := buildSyncCommands(commonDeps{})
|
instance := buildSyncCommands(commonDeps{})
|
||||||
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
|
instance.UserConfig().Git.FetchAll = s.fetchAllConfig
|
||||||
s.test(instance.FetchBackgroundCmdObj())
|
s.test(instance.FetchBackgroundCmdObj())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package git_commands
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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
|
// Exclude adds a file to the .git/info/exclude for the repo
|
||||||
func (self *WorkingTreeCommands) Exclude(filename string) error {
|
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)
|
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 {
|
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 {
|
if plain {
|
||||||
colorArg = "never"
|
colorArg = "never"
|
||||||
}
|
}
|
||||||
@@ -253,7 +253,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
|
|||||||
contextSize := self.AppState.DiffContextSize
|
contextSize := self.AppState.DiffContextSize
|
||||||
prevPath := node.GetPreviousPath()
|
prevPath := node.GetPreviousPath()
|
||||||
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
|
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
|
||||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||||
useExtDiff := extDiffCmd != "" && !plain
|
useExtDiff := extDiffCmd != "" && !plain
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("diff").
|
cmdArgs := NewGitCmd("diff").
|
||||||
@@ -285,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 {
|
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj {
|
||||||
contextSize := self.AppState.DiffContextSize
|
contextSize := self.AppState.DiffContextSize
|
||||||
|
|
||||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
colorArg := self.UserConfig().Git.Paging.ColorArg
|
||||||
if plain {
|
if plain {
|
||||||
colorArg = "never"
|
colorArg = "never"
|
||||||
}
|
}
|
||||||
|
|
||||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||||
useExtDiff := extDiffCmd != "" && !plain
|
useExtDiff := extDiffCmd != "" && !plain
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("diff").
|
cmdArgs := NewGitCmd("diff").
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
@@ -100,7 +99,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|||||||
Added: true,
|
Added: true,
|
||||||
},
|
},
|
||||||
removeFile: func(string) error {
|
removeFile: func(string) error {
|
||||||
return fmt.Errorf("an error occurred when removing file")
|
return errors.New("an error occurred when removing file")
|
||||||
},
|
},
|
||||||
runner: oscommands.NewFakeRunner(t),
|
runner: oscommands.NewFakeRunner(t),
|
||||||
expectedError: "an error occurred when removing file",
|
expectedError: "an error occurred when removing file",
|
||||||
@@ -210,7 +209,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
|||||||
plain bool
|
plain bool
|
||||||
cached bool
|
cached bool
|
||||||
ignoreWhitespace bool
|
ignoreWhitespace bool
|
||||||
contextSize int
|
contextSize uint64
|
||||||
similarityThreshold int
|
similarityThreshold int
|
||||||
runner *oscommands.FakeCmdObjRunner
|
runner *oscommands.FakeCmdObjRunner
|
||||||
}
|
}
|
||||||
@@ -352,7 +351,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
|||||||
reverse bool
|
reverse bool
|
||||||
plain bool
|
plain bool
|
||||||
ignoreWhitespace bool
|
ignoreWhitespace bool
|
||||||
contextSize int
|
contextSize uint64
|
||||||
runner *oscommands.FakeCmdObjRunner
|
runner *oscommands.FakeCmdObjRunner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func (self *CachedGitConfig) Get(key string) string {
|
|||||||
defer self.mutex.Unlock()
|
defer self.mutex.Unlock()
|
||||||
|
|
||||||
if value, ok := self.cache[key]; ok {
|
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
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ func (self *CachedGitConfig) GetGeneral(args string) string {
|
|||||||
defer self.mutex.Unlock()
|
defer self.mutex.Unlock()
|
||||||
|
|
||||||
if value, ok := self.cache[args]; ok {
|
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
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ func (self *CachedGitConfig) getGeneralAux(args string) string {
|
|||||||
cmd := getGitConfigGeneralCmd(args)
|
cmd := getGitConfigGeneralCmd(args)
|
||||||
value, err := self.runGitConfigCmd(cmd)
|
value, err := self.runGitConfigCmd(cmd)
|
||||||
if err != nil {
|
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 ""
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(value)
|
return strings.TrimSpace(value)
|
||||||
@@ -79,7 +79,7 @@ func (self *CachedGitConfig) getAux(key string) string {
|
|||||||
cmd := getGitConfigCmd(key)
|
cmd := getGitConfigCmd(key)
|
||||||
value, err := self.runGitConfigCmd(cmd)
|
value, err := self.runGitConfigCmd(cmd)
|
||||||
if err != nil {
|
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 ""
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(value)
|
return strings.TrimSpace(value)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ var azdoServiceDef = ServiceDefinition{
|
|||||||
regexStrings: []string{
|
regexStrings: []string{
|
||||||
`^git@ssh.dev.azure.com.*/(?P<org>.*)/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
|
`^git@ssh.dev.azure.com.*/(?P<org>.*)/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
|
||||||
`^https://.*@dev.azure.com/(?P<org>.*?)/(?P<project>.*?)/_git/(?P<repo>.*?)(?:\.git)?$`,
|
`^https://.*@dev.azure.com/(?P<org>.*?)/(?P<project>.*?)/_git/(?P<repo>.*?)(?:\.git)?$`,
|
||||||
|
`^https://.*/(?P<org>.*?)/(?P<project>.*?)/_git/(?P<repo>.*?)(?:\.git)?$`,
|
||||||
},
|
},
|
||||||
repoURLTemplate: "https://{{.webDomain}}/{{.org}}/{{.project}}/_git/{{.repo}}",
|
repoURLTemplate: "https://{{.webDomain}}/{{.org}}/{{.project}}/_git/{{.repo}}",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,19 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
assert.Equal(t, "https://dev.azure.com/myorg/myproject/_git/myrepo/pullrequestcreate?sourceRef=feature%2Fnew&targetRef=dev", url)
|
assert.Equal(t, "https://dev.azure.com/myorg/myproject/_git/myrepo/pullrequestcreate?sourceRef=feature%2Fnew&targetRef=dev", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
testName: "Opens a link to new pull request on Azure DevOps Server (HTTP)",
|
||||||
|
from: "feature/new",
|
||||||
|
remoteUrl: "https://mycompany.azuredevops.com/collection/myproject/_git/myrepo",
|
||||||
|
configServiceDomains: map[string]string{
|
||||||
|
// valid configuration for a azure devops server URL
|
||||||
|
"mycompany.azuredevops.com": "azuredevops:mycompany.azuredevops.com",
|
||||||
|
},
|
||||||
|
test: func(url string, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "https://mycompany.azuredevops.com/collection/myproject/_git/myrepo/pullrequestcreate?sourceRef=feature%2Fnew", url)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
testName: "Opens a link to new pull request on Bitbucket Server (SSH)",
|
testName: "Opens a link to new pull request on Bitbucket Server (SSH)",
|
||||||
from: "feature/new",
|
from: "feature/new",
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ func (b *Branch) RefName() string {
|
|||||||
return b.Name
|
return b.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Branch) ShortRefName() string {
|
||||||
|
return b.RefName()
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Branch) ParentRefName() string {
|
func (b *Branch) ParentRefName() string {
|
||||||
return b.RefName() + "^"
|
return b.RefName() + "^"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ func (c *Commit) RefName() string {
|
|||||||
return c.Hash
|
return c.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Commit) ShortRefName() string {
|
||||||
|
return c.Hash[:7]
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Commit) ParentRefName() string {
|
func (c *Commit) ParentRefName() string {
|
||||||
if c.IsFirstCommit() {
|
if c.IsFirstCommit() {
|
||||||
return EmptyTreeCommitHash
|
return EmptyTreeCommitHash
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ type File struct {
|
|||||||
HasInlineMergeConflicts bool
|
HasInlineMergeConflicts bool
|
||||||
DisplayString string
|
DisplayString string
|
||||||
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
|
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
|
||||||
|
LinesDeleted int
|
||||||
|
LinesAdded int
|
||||||
|
|
||||||
// If true, this must be a worktree folder
|
// If true, this must be a worktree folder
|
||||||
IsWorktree bool
|
IsWorktree bool
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ func (r *RemoteBranch) RefName() string {
|
|||||||
return r.FullName()
|
return r.FullName()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RemoteBranch) ShortRefName() string {
|
||||||
|
return r.RefName()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RemoteBranch) ParentRefName() string {
|
func (r *RemoteBranch) ParentRefName() string {
|
||||||
return r.RefName() + "^"
|
return r.RefName() + "^"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ func (s *StashEntry) RefName() string {
|
|||||||
return fmt.Sprintf("stash@{%d}", s.Index)
|
return fmt.Sprintf("stash@{%d}", s.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StashEntry) ShortRefName() string {
|
||||||
|
return s.RefName()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StashEntry) ParentRefName() string {
|
func (s *StashEntry) ParentRefName() string {
|
||||||
return s.RefName() + "^"
|
return s.RefName() + "^"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ func (t *Tag) RefName() string {
|
|||||||
return t.Name
|
return t.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tag) ShortRefName() string {
|
||||||
|
return t.RefName()
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tag) ParentRefName() string {
|
func (t *Tag) ParentRefName() string {
|
||||||
return t.RefName() + "^"
|
return t.RefName() + "^"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ type ICmdObjBuilder interface {
|
|||||||
New(args []string) ICmdObj
|
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 takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
|
||||||
NewShell(commandStr string) ICmdObj
|
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 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
|
Quote(str string) string
|
||||||
}
|
}
|
||||||
@@ -43,10 +45,23 @@ func (self *CmdObjBuilder) NewWithEnviron(args []string, env []string) ICmdObj {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CmdObjBuilder) NewShell(commandStr 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 + self.platform.InteractiveShellExit)
|
||||||
|
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
|
// Windows does not seem to like quotes around the command
|
||||||
if self.platform.OS == "windows" {
|
if self.platform.OS == "windows" {
|
||||||
quotedCommand = strings.NewReplacer(
|
return strings.NewReplacer(
|
||||||
"^", "^^",
|
"^", "^^",
|
||||||
"&", "^&",
|
"&", "^&",
|
||||||
"|", "^|",
|
"|", "^|",
|
||||||
@@ -54,13 +69,9 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
|
|||||||
">", "^>",
|
">", "^>",
|
||||||
"%", "^%",
|
"%", "^%",
|
||||||
).Replace(commandStr)
|
).Replace(commandStr)
|
||||||
} else {
|
|
||||||
quotedCommand = self.Quote(commandStr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
|
return self.Quote(commandStr)
|
||||||
|
|
||||||
return self.New(cmdArgs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {
|
func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ func (self *cmdObjRunner) getCredentialPromptFn(cmdObj ICmdObj) (func(Credential
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runAndDetectCredentialRequest detect a username / password / passphrase question in a command
|
// 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
|
// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
|
||||||
func (self *cmdObjRunner) runAndDetectCredentialRequest(
|
func (self *cmdObjRunner) runAndDetectCredentialRequest(
|
||||||
cmdObj ICmdObj,
|
cmdObj ICmdObj,
|
||||||
|
|||||||
@@ -51,11 +51,14 @@ func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dummyPlatform = &Platform{
|
var dummyPlatform = &Platform{
|
||||||
OS: "darwin",
|
OS: "darwin",
|
||||||
Shell: "bash",
|
Shell: "bash",
|
||||||
ShellArg: "-c",
|
InteractiveShell: "bash",
|
||||||
OpenCommand: "open {{filename}}",
|
ShellArg: "-c",
|
||||||
OpenLinkCommand: "open {{link}}",
|
InteractiveShellArg: "-i",
|
||||||
|
InteractiveShellExit: "; exit $?",
|
||||||
|
OpenCommand: "open {{filename}}",
|
||||||
|
OpenLinkCommand: "open {{link}}",
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand {
|
func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand {
|
||||||
|
|||||||
@@ -35,11 +35,14 @@ type OSCommand struct {
|
|||||||
|
|
||||||
// Platform stores the os state
|
// Platform stores the os state
|
||||||
type Platform struct {
|
type Platform struct {
|
||||||
OS string
|
OS string
|
||||||
Shell string
|
Shell string
|
||||||
ShellArg string
|
InteractiveShell string
|
||||||
OpenCommand string
|
ShellArg string
|
||||||
OpenLinkCommand string
|
InteractiveShellArg string
|
||||||
|
InteractiveShellExit string
|
||||||
|
OpenCommand string
|
||||||
|
OpenLinkCommand string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOSCommand os command runner
|
// NewOSCommand os command runner
|
||||||
@@ -78,10 +81,10 @@ func FileType(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) OpenFile(filename string) error {
|
func (c *OSCommand) OpenFile(filename string) error {
|
||||||
commandTemplate := c.UserConfig.OS.Open
|
commandTemplate := c.UserConfig().OS.Open
|
||||||
if commandTemplate == "" {
|
if commandTemplate == "" {
|
||||||
// Legacy support
|
// Legacy support
|
||||||
commandTemplate = c.UserConfig.OS.OpenCommand
|
commandTemplate = c.UserConfig().OS.OpenCommand
|
||||||
}
|
}
|
||||||
if commandTemplate == "" {
|
if commandTemplate == "" {
|
||||||
commandTemplate = config.GetPlatformDefaultConfig().Open
|
commandTemplate = config.GetPlatformDefaultConfig().Open
|
||||||
@@ -94,10 +97,10 @@ func (c *OSCommand) OpenFile(filename string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) OpenLink(link string) error {
|
func (c *OSCommand) OpenLink(link string) error {
|
||||||
commandTemplate := c.UserConfig.OS.OpenLink
|
commandTemplate := c.UserConfig().OS.OpenLink
|
||||||
if commandTemplate == "" {
|
if commandTemplate == "" {
|
||||||
// Legacy support
|
// Legacy support
|
||||||
commandTemplate = c.UserConfig.OS.OpenLinkCommand
|
commandTemplate = c.UserConfig().OS.OpenLinkCommand
|
||||||
}
|
}
|
||||||
if commandTemplate == "" {
|
if commandTemplate == "" {
|
||||||
commandTemplate = config.GetPlatformDefaultConfig().OpenLink
|
commandTemplate = config.GetPlatformDefaultConfig().OpenLink
|
||||||
@@ -292,8 +295,8 @@ func (c *OSCommand) CopyToClipboard(str string) error {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
c.LogCommand(msg, false)
|
c.LogCommand(msg, false)
|
||||||
if c.UserConfig.OS.CopyToClipboardCmd != "" {
|
if c.UserConfig().OS.CopyToClipboardCmd != "" {
|
||||||
cmdStr := utils.ResolvePlaceholderString(c.UserConfig.OS.CopyToClipboardCmd, map[string]string{
|
cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{
|
||||||
"text": c.Cmd.Quote(str),
|
"text": c.Cmd.Quote(str),
|
||||||
})
|
})
|
||||||
return c.Cmd.NewShell(cmdStr).Run()
|
return c.Cmd.NewShell(cmdStr).Run()
|
||||||
@@ -305,8 +308,8 @@ func (c *OSCommand) CopyToClipboard(str string) error {
|
|||||||
func (c *OSCommand) PasteFromClipboard() (string, error) {
|
func (c *OSCommand) PasteFromClipboard() (string, error) {
|
||||||
var s string
|
var s string
|
||||||
var err error
|
var err error
|
||||||
if c.UserConfig.OS.CopyToClipboardCmd != "" {
|
if c.UserConfig().OS.CopyToClipboardCmd != "" {
|
||||||
cmdStr := c.UserConfig.OS.ReadFromClipboardCmd
|
cmdStr := c.UserConfig().OS.ReadFromClipboardCmd
|
||||||
s, err = c.Cmd.NewShell(cmdStr).RunWithOutput()
|
s, err = c.Cmd.NewShell(cmdStr).RunWithOutput()
|
||||||
} else {
|
} else {
|
||||||
s, err = clipboard.ReadAll()
|
s, err = clipboard.ReadAll()
|
||||||
|
|||||||
@@ -4,15 +4,40 @@
|
|||||||
package oscommands
|
package oscommands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPlatform() *Platform {
|
func GetPlatform() *Platform {
|
||||||
|
shell := getUserShell()
|
||||||
|
|
||||||
|
interactiveShell := shell
|
||||||
|
interactiveShellArg := "-i"
|
||||||
|
interactiveShellExit := "; exit $?"
|
||||||
|
|
||||||
|
if !(strings.HasSuffix(shell, "bash") || strings.HasSuffix(shell, "zsh")) {
|
||||||
|
interactiveShell = "bash"
|
||||||
|
interactiveShellArg = ""
|
||||||
|
interactiveShellExit = ""
|
||||||
|
}
|
||||||
|
|
||||||
return &Platform{
|
return &Platform{
|
||||||
OS: runtime.GOOS,
|
OS: runtime.GOOS,
|
||||||
Shell: "bash",
|
Shell: "bash",
|
||||||
ShellArg: "-c",
|
InteractiveShell: interactiveShell,
|
||||||
OpenCommand: "open {{filename}}",
|
ShellArg: "-c",
|
||||||
OpenLinkCommand: "open {{link}}",
|
InteractiveShellArg: interactiveShellArg,
|
||||||
|
InteractiveShellExit: interactiveShellExit,
|
||||||
|
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 {
|
for _, s := range scenarios {
|
||||||
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
||||||
oSCmd.Platform.OS = "darwin"
|
oSCmd.Platform.OS = "darwin"
|
||||||
oSCmd.UserConfig.OS.Open = "open {{filename}}"
|
oSCmd.UserConfig().OS.Open = "open {{filename}}"
|
||||||
|
|
||||||
s.test(oSCmd.OpenFile(s.filename))
|
s.test(oSCmd.OpenFile(s.filename))
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ func TestOSCommandOpenFileLinux(t *testing.T) {
|
|||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
||||||
oSCmd.Platform.OS = "linux"
|
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))
|
s.test(oSCmd.OpenFile(s.filename))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ func TestOSCommandFileType(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"nonExistant",
|
"nonExistent",
|
||||||
func() {},
|
func() {},
|
||||||
func(output string) {
|
func(output string) {
|
||||||
assert.EqualValues(t, "other", output)
|
assert.EqualValues(t, "other", output)
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package oscommands
|
|||||||
|
|
||||||
func GetPlatform() *Platform {
|
func GetPlatform() *Platform {
|
||||||
return &Platform{
|
return &Platform{
|
||||||
OS: "windows",
|
OS: "windows",
|
||||||
Shell: "cmd",
|
Shell: "cmd",
|
||||||
ShellArg: "/c",
|
InteractiveShell: "cmd",
|
||||||
|
ShellArg: "/c",
|
||||||
|
InteractiveShellArg: "",
|
||||||
|
InteractiveShellExit: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
|||||||
}
|
}
|
||||||
oSCmd.Platform = platform
|
oSCmd.Platform = platform
|
||||||
oSCmd.Cmd.platform = platform
|
oSCmd.Cmd.platform = platform
|
||||||
oSCmd.UserConfig.OS.OpenCommand = `start "" {{filename}}`
|
oSCmd.UserConfig().OS.OpenCommand = `start "" {{filename}}`
|
||||||
|
|
||||||
s.test(oSCmd.OpenFile(s.filename))
|
s.test(oSCmd.OpenFile(s.filename))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,3 +154,24 @@ func (self *Patch) LineCount() int {
|
|||||||
func (self *Patch) HunkCount() int {
|
func (self *Patch) HunkCount() int {
|
||||||
return len(self.hunks)
|
return len(self.hunks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust the given line number (one-based) according to the current patch. The
|
||||||
|
// patch is supposed to be a diff of an old file state against the working
|
||||||
|
// directory; the line number is a line number in that old file, and the
|
||||||
|
// function returns the corresponding line number in the working directory file.
|
||||||
|
func (self *Patch) AdjustLineNumber(lineNumber int) int {
|
||||||
|
adjustedLineNumber := lineNumber
|
||||||
|
for _, hunk := range self.hunks {
|
||||||
|
if hunk.oldStart >= lineNumber {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if hunk.oldStart+hunk.oldLength() > lineNumber {
|
||||||
|
return hunk.newStart
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustedLineNumber += hunk.newLength() - hunk.oldLength()
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustedLineNumber
|
||||||
|
}
|
||||||
|
|||||||
@@ -639,3 +639,59 @@ func TestGetNextStageableLineIndex(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAdjustLineNumber(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
oldLineNumbers []int
|
||||||
|
expectedResults []int
|
||||||
|
}
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
oldLineNumbers: []int{1, 2, 3, 4, 5, 6, 7},
|
||||||
|
expectedResults: []int{1, 2, 2, 3, 4, 7, 8},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following diff was generated from old.txt:
|
||||||
|
// 1
|
||||||
|
// 2a
|
||||||
|
// 2b
|
||||||
|
// 3
|
||||||
|
// 4
|
||||||
|
// 7
|
||||||
|
// 8
|
||||||
|
// against new.txt:
|
||||||
|
// 1
|
||||||
|
// 2
|
||||||
|
// 3
|
||||||
|
// 4
|
||||||
|
// 5
|
||||||
|
// 6
|
||||||
|
// 7
|
||||||
|
// 8
|
||||||
|
|
||||||
|
// This test setup makes the test easy to understand, because the resulting
|
||||||
|
// adjusted line numbers are the same as the content of the lines in new.txt.
|
||||||
|
|
||||||
|
diff := `--- old.txt 2024-12-16 18:04:29
|
||||||
|
+++ new.txt 2024-12-16 18:04:27
|
||||||
|
@@ -2,2 +2 @@
|
||||||
|
-2a
|
||||||
|
-2b
|
||||||
|
+2
|
||||||
|
@@ -5,0 +5,2 @@
|
||||||
|
+5
|
||||||
|
+6
|
||||||
|
`
|
||||||
|
|
||||||
|
patch := Parse(diff)
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run("TestAdjustLineNumber", func(t *testing.T) {
|
||||||
|
for idx, oldLineNumber := range s.oldLineNumbers {
|
||||||
|
result := patch.AdjustLineNumber(oldLineNumber)
|
||||||
|
assert.Equal(t, s.expectedResults[idx], result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+11
-1
@@ -1,6 +1,8 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@@ -11,10 +13,18 @@ import (
|
|||||||
type Common struct {
|
type Common struct {
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
Tr *i18n.TranslationSet
|
Tr *i18n.TranslationSet
|
||||||
UserConfig *config.UserConfig
|
userConfig atomic.Pointer[config.UserConfig]
|
||||||
AppState *config.AppState
|
AppState *config.AppState
|
||||||
Debug bool
|
Debug bool
|
||||||
// for interacting with the filesystem. We use afero rather than the default
|
// for interacting with the filesystem. We use afero rather than the default
|
||||||
// `os` package for the sake of mocking the filesystem in tests
|
// `os` package for the sake of mocking the filesystem in tests
|
||||||
Fs afero.Fs
|
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
-72
@@ -2,29 +2,31 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
"github.com/adrg/xdg"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
|
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
|
||||||
|
"github.com/samber/lo"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppConfig contains the base configuration fields required for lazygit.
|
// AppConfig contains the base configuration fields required for lazygit.
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Debug bool `long:"debug" env:"DEBUG" default:"false"`
|
debug bool `long:"debug" env:"DEBUG" default:"false"`
|
||||||
Version string `long:"version" env:"VERSION" default:"unversioned"`
|
version string `long:"version" env:"VERSION" default:"unversioned"`
|
||||||
BuildDate string `long:"build-date" env:"BUILD_DATE"`
|
buildDate string `long:"build-date" env:"BUILD_DATE"`
|
||||||
Name string `long:"name" env:"NAME" default:"lazygit"`
|
name string `long:"name" env:"NAME" default:"lazygit"`
|
||||||
BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
|
buildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
|
||||||
UserConfig *UserConfig
|
userConfig *UserConfig
|
||||||
UserConfigPaths []string
|
globalUserConfigFiles []*ConfigFile
|
||||||
DeafultConfFiles bool
|
userConfigFiles []*ConfigFile
|
||||||
UserConfigDir string
|
userConfigDir string
|
||||||
TempDir string
|
tempDir string
|
||||||
AppState *AppState
|
appState *AppState
|
||||||
IsNewRepo bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppConfigurer interface {
|
type AppConfigurer interface {
|
||||||
@@ -38,13 +40,29 @@ type AppConfigurer interface {
|
|||||||
GetUserConfig() *UserConfig
|
GetUserConfig() *UserConfig
|
||||||
GetUserConfigPaths() []string
|
GetUserConfigPaths() []string
|
||||||
GetUserConfigDir() string
|
GetUserConfigDir() string
|
||||||
ReloadUserConfig() error
|
ReloadUserConfigForRepo(repoConfigFiles []*ConfigFile) error
|
||||||
|
ReloadChangedUserConfigFiles() (error, bool)
|
||||||
GetTempDir() string
|
GetTempDir() string
|
||||||
|
|
||||||
GetAppState() *AppState
|
GetAppState() *AppState
|
||||||
SaveAppState() error
|
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
|
// NewAppConfig makes a new app config
|
||||||
func NewAppConfig(
|
func NewAppConfig(
|
||||||
name string,
|
name string,
|
||||||
@@ -60,17 +78,22 @@ func NewAppConfig(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var userConfigPaths []string
|
var configFiles []*ConfigFile
|
||||||
customConfigFiles := os.Getenv("LG_CONFIG_FILE")
|
customConfigFiles := os.Getenv("LG_CONFIG_FILE")
|
||||||
if customConfigFiles != "" {
|
if customConfigFiles != "" {
|
||||||
// Load user defined config files
|
// 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 {
|
} else {
|
||||||
// Load default config files
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -92,26 +115,22 @@ func NewAppConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
appConfig := &AppConfig{
|
appConfig := &AppConfig{
|
||||||
Name: name,
|
name: name,
|
||||||
Version: version,
|
version: version,
|
||||||
BuildDate: date,
|
buildDate: date,
|
||||||
Debug: debuggingFlag,
|
debug: debuggingFlag,
|
||||||
BuildSource: buildSource,
|
buildSource: buildSource,
|
||||||
UserConfig: userConfig,
|
userConfig: userConfig,
|
||||||
UserConfigPaths: userConfigPaths,
|
globalUserConfigFiles: configFiles,
|
||||||
UserConfigDir: configDir,
|
userConfigFiles: configFiles,
|
||||||
TempDir: tempDir,
|
userConfigDir: configDir,
|
||||||
AppState: appState,
|
tempDir: tempDir,
|
||||||
IsNewRepo: false,
|
appState: appState,
|
||||||
}
|
}
|
||||||
|
|
||||||
return appConfig, nil
|
return appConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCustomConfigFile(path string) bool {
|
|
||||||
return path != filepath.Join(ConfigDir(), ConfigFilename)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConfigDir() string {
|
func ConfigDir() string {
|
||||||
_, filePath := findConfigFile("config.yml")
|
_, filePath := findConfigFile("config.yml")
|
||||||
|
|
||||||
@@ -123,32 +142,48 @@ func findOrCreateConfigDir() (string, error) {
|
|||||||
return folder, os.MkdirAll(folder, 0o755)
|
return folder, os.MkdirAll(folder, 0o755)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadUserConfigWithDefaults(configFiles []string) (*UserConfig, error) {
|
func loadUserConfigWithDefaults(configFiles []*ConfigFile) (*UserConfig, error) {
|
||||||
return loadUserConfig(configFiles, GetDefaultConfig())
|
return loadUserConfig(configFiles, GetDefaultConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error) {
|
func loadUserConfig(configFiles []*ConfigFile, base *UserConfig) (*UserConfig, error) {
|
||||||
for _, path := range configFiles {
|
for _, configFile := range configFiles {
|
||||||
if _, err := os.Stat(path); err != nil {
|
path := configFile.Path
|
||||||
|
statInfo, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
configFile.exists = true
|
||||||
|
configFile.modDate = statInfo.ModTime()
|
||||||
|
} else {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if use has supplied their own custom config file path(s), we assume
|
switch configFile.Policy {
|
||||||
// the files have already been created, so we won't go and create them here.
|
case ConfigFilePolicyErrorIfMissing:
|
||||||
if isCustomConfigFile(path) {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.Create(path)
|
case ConfigFilePolicySkipIfMissing:
|
||||||
if err != nil {
|
configFile.exists = false
|
||||||
if os.IsPermission(err) {
|
continue
|
||||||
// apparently when people have read-only permissions they prefer us to fail silently
|
|
||||||
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)
|
content, err := os.ReadFile(path)
|
||||||
@@ -161,10 +196,14 @@ func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
existingCustomCommands := base.CustomCommands
|
||||||
|
|
||||||
if err := yaml.Unmarshal(content, base); err != nil {
|
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)
|
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 {
|
if err := base.Validate(); err != nil {
|
||||||
return nil, fmt.Errorf("The config at `%s` has a validation error.\n%w", path, err)
|
return nil, fmt.Errorf("The config at `%s` has a validation error.\n%w", path, err)
|
||||||
}
|
}
|
||||||
@@ -178,10 +217,23 @@ func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error)
|
|||||||
// from one container to another, or changing the type of a key (e.g. from bool
|
// from one container to another, or changing the type of a key (e.g. from bool
|
||||||
// to an enum).
|
// to an enum).
|
||||||
func migrateUserConfig(path string, content []byte) ([]byte, error) {
|
func migrateUserConfig(path string, content []byte) ([]byte, error) {
|
||||||
changedContent, err := yaml_utils.RenameYamlKey(content, []string{"gui", "skipUnstageLineWarning"},
|
changedContent := content
|
||||||
"skipDiscardChangeWarning")
|
|
||||||
if err != nil {
|
pathsToReplace := []struct {
|
||||||
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
|
oldPath []string
|
||||||
|
newName string
|
||||||
|
}{
|
||||||
|
{[]string{"gui", "skipUnstageLineWarning"}, "skipDiscardChangeWarning"},
|
||||||
|
{[]string{"keybinding", "universal", "executeCustomCommand"}, "executeShellCommand"},
|
||||||
|
{[]string{"gui", "windowSize"}, "screenMode"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for _, pathToReplace := range pathsToReplace {
|
||||||
|
changedContent, err = yaml_utils.RenameYamlKey(changedContent, pathToReplace.oldPath, pathToReplace.newName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Couldn't migrate config file at `%s` for key %s: %s", path, strings.Join(pathToReplace.oldPath, "."), err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changedContent, err = changeNullKeybindingsToDisabled(changedContent)
|
changedContent, err = changeNullKeybindingsToDisabled(changedContent)
|
||||||
@@ -214,53 +266,81 @@ func changeNullKeybindingsToDisabled(changedContent []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *AppConfig) GetDebug() bool {
|
func (c *AppConfig) GetDebug() bool {
|
||||||
return c.Debug
|
return c.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AppConfig) GetVersion() string {
|
func (c *AppConfig) GetVersion() string {
|
||||||
return c.Version
|
return c.version
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AppConfig) GetName() string {
|
func (c *AppConfig) GetName() string {
|
||||||
return c.Name
|
return c.name
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBuildSource returns the source of the build. For builds from goreleaser
|
// GetBuildSource returns the source of the build. For builds from goreleaser
|
||||||
// this will be binaryBuild
|
// this will be binaryBuild
|
||||||
func (c *AppConfig) GetBuildSource() string {
|
func (c *AppConfig) GetBuildSource() string {
|
||||||
return c.BuildSource
|
return c.buildSource
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserConfig returns the user config
|
// GetUserConfig returns the user config
|
||||||
func (c *AppConfig) GetUserConfig() *UserConfig {
|
func (c *AppConfig) GetUserConfig() *UserConfig {
|
||||||
return c.UserConfig
|
return c.userConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAppState returns the app state
|
// GetAppState returns the app state
|
||||||
func (c *AppConfig) GetAppState() *AppState {
|
func (c *AppConfig) GetAppState() *AppState {
|
||||||
return c.AppState
|
return c.appState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AppConfig) GetUserConfigPaths() []string {
|
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 {
|
func (c *AppConfig) GetUserConfigDir() string {
|
||||||
return c.UserConfigDir
|
return c.userConfigDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AppConfig) ReloadUserConfig() error {
|
func (c *AppConfig) ReloadUserConfigForRepo(repoConfigFiles []*ConfigFile) error {
|
||||||
userConfig, err := loadUserConfigWithDefaults(c.UserConfigPaths)
|
configFiles := append(c.globalUserConfigFiles, repoConfigFiles...)
|
||||||
|
userConfig, err := loadUserConfigWithDefaults(configFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.UserConfig = userConfig
|
c.userConfig = userConfig
|
||||||
|
c.userConfigFiles = configFiles
|
||||||
return nil
|
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 {
|
func (c *AppConfig) GetTempDir() string {
|
||||||
return c.TempDir
|
return c.tempDir
|
||||||
}
|
}
|
||||||
|
|
||||||
// findConfigFile looks for a possibly existing config file.
|
// findConfigFile looks for a possibly existing config file.
|
||||||
@@ -299,14 +379,9 @@ func stateFilePath(filename string) (string, error) {
|
|||||||
return xdg.StateFile(filepath.Join("lazygit", filename))
|
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
|
// SaveAppState marshalls the AppState struct and writes it to the disk
|
||||||
func (c *AppConfig) SaveAppState() error {
|
func (c *AppConfig) SaveAppState() error {
|
||||||
marshalledAppState, err := yaml.Marshal(c.AppState)
|
marshalledAppState, err := yaml.Marshal(c.appState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -357,6 +432,24 @@ func loadAppState() (*AppState, error) {
|
|||||||
return appState, nil
|
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
|
// AppState stores data between runs of the app like when the last update check
|
||||||
// was performed and which other repos have been checked out
|
// was performed and which other repos have been checked out
|
||||||
type AppState struct {
|
type AppState struct {
|
||||||
@@ -365,11 +458,13 @@ type AppState struct {
|
|||||||
StartupPopupVersion int
|
StartupPopupVersion int
|
||||||
LastVersion string // this is the last version the user was using, for the purpose of showing release notes
|
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
|
// these are for shell commands typed in directly, not for custom commands in the lazygit config.
|
||||||
CustomCommandsHistory []string
|
// For backwards compatibility we keep the old name in yaml files.
|
||||||
|
ShellCommandsHistory []string `yaml:"customcommandshistory"`
|
||||||
|
|
||||||
HideCommandLog bool
|
HideCommandLog bool
|
||||||
IgnoreWhitespaceInDiffView bool
|
IgnoreWhitespaceInDiffView bool
|
||||||
DiffContextSize int
|
DiffContextSize uint64
|
||||||
RenameSimilarityThreshold int
|
RenameSimilarityThreshold int
|
||||||
LocalBranchSortOrder string
|
LocalBranchSortOrder string
|
||||||
RemoteBranchSortOrder string
|
RemoteBranchSortOrder string
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ func isContainer() bool {
|
|||||||
func GetPlatformDefaultConfig() OSConfig {
|
func GetPlatformDefaultConfig() OSConfig {
|
||||||
if isWSL() && !isContainer() {
|
if isWSL() && !isContainer() {
|
||||||
return OSConfig{
|
return OSConfig{
|
||||||
Open: `powershell.exe start explorer.exe {{filename}} >/dev/null`,
|
Open: `powershell.exe start explorer.exe "$(wslpath -w {{filename}})" >/dev/null`,
|
||||||
OpenLink: `powershell.exe start {{link}} >/dev/null`,
|
OpenLink: `powershell.exe start '{{link}}' >/dev/null`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import (
|
|||||||
// NewDummyAppConfig creates a new dummy AppConfig for testing
|
// NewDummyAppConfig creates a new dummy AppConfig for testing
|
||||||
func NewDummyAppConfig() *AppConfig {
|
func NewDummyAppConfig() *AppConfig {
|
||||||
appConfig := &AppConfig{
|
appConfig := &AppConfig{
|
||||||
Name: "lazygit",
|
name: "lazygit",
|
||||||
Version: "unversioned",
|
version: "unversioned",
|
||||||
Debug: false,
|
debug: false,
|
||||||
UserConfig: GetDefaultConfig(),
|
userConfig: GetDefaultConfig(),
|
||||||
AppState: &AppState{},
|
appState: &AppState{},
|
||||||
}
|
}
|
||||||
_ = yaml.Unmarshal([]byte{}, appConfig.AppState)
|
_ = yaml.Unmarshal([]byte{}, appConfig.appState)
|
||||||
return appConfig
|
return appConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,9 +66,15 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
|||||||
return !ok
|
return !ok
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"lvim": standardTerminalEditorPreset("lvim"),
|
"lvim": standardTerminalEditorPreset("lvim"),
|
||||||
"emacs": standardTerminalEditorPreset("emacs"),
|
"emacs": standardTerminalEditorPreset("emacs"),
|
||||||
"micro": standardTerminalEditorPreset("micro"),
|
"micro": {
|
||||||
|
editTemplate: "micro {{filename}}",
|
||||||
|
editAtLineTemplate: "micro +{{line}} {{filename}}",
|
||||||
|
editAtLineAndWaitTemplate: "micro +{{line}} {{filename}}",
|
||||||
|
openDirInEditorTemplate: "micro {{dir}}",
|
||||||
|
suspend: returnBool(true),
|
||||||
|
},
|
||||||
"nano": standardTerminalEditorPreset("nano"),
|
"nano": standardTerminalEditorPreset("nano"),
|
||||||
"kakoune": standardTerminalEditorPreset("kak"),
|
"kakoune": standardTerminalEditorPreset("kak"),
|
||||||
"helix": {
|
"helix": {
|
||||||
@@ -113,6 +119,13 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
|||||||
openDirInEditorTemplate: "xed -- {{dir}}",
|
openDirInEditorTemplate: "xed -- {{dir}}",
|
||||||
suspend: returnBool(false),
|
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.
|
// Some of our presets have a different name than the editor they are using.
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ type GuiConfig struct {
|
|||||||
// - 'left': split the window horizontally (side panel on the left, main view on the right)
|
// - '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)
|
// - 'top': split the window vertically (side panel on top, main view below)
|
||||||
EnlargedSideViewLocation string `yaml:"enlargedSideViewLocation"`
|
EnlargedSideViewLocation string `yaml:"enlargedSideViewLocation"`
|
||||||
|
// If true, wrap lines in the staging view to the width of the view. This
|
||||||
|
// makes it much easier to work with diffs that have long lines, e.g.
|
||||||
|
// paragraphs of markdown text.
|
||||||
|
WrapLinesInStagingView bool `yaml:"wrapLinesInStagingView"`
|
||||||
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||||
Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`
|
Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`
|
||||||
// Format used when displaying time e.g. commit time.
|
// Format used when displaying time e.g. commit time.
|
||||||
@@ -109,6 +113,8 @@ type GuiConfig struct {
|
|||||||
// If true, display the files in the file views as a tree. If false, display the files as a flat list.
|
// 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.
|
// This can be toggled from within Lazygit with the '~' key, but that will not change the default.
|
||||||
ShowFileTree bool `yaml:"showFileTree"`
|
ShowFileTree bool `yaml:"showFileTree"`
|
||||||
|
// If true, show the number of lines changed per file in the Files view
|
||||||
|
ShowNumstatInFilesView bool `yaml:"showNumstatInFilesView"`
|
||||||
// If true, show a random tip in the command log when Lazygit starts
|
// If true, show a random tip in the command log when Lazygit starts
|
||||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||||
// If true, show the command log
|
// If true, show the command log
|
||||||
@@ -142,9 +148,9 @@ type GuiConfig struct {
|
|||||||
// One of: 'auto' | 'always'
|
// One of: 'auto' | 'always'
|
||||||
// If 'auto', only split the main window when a file has both staged and unstaged changes
|
// If 'auto', only split the main window when a file has both staged and unstaged changes
|
||||||
SplitDiff string `yaml:"splitDiff" jsonschema:"enum=auto,enum=always"`
|
SplitDiff string `yaml:"splitDiff" jsonschema:"enum=auto,enum=always"`
|
||||||
// Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).
|
// Default size for focused window. Can be changed from within Lazygit with '+' and '_' (but this won't change the default).
|
||||||
// One of: 'normal' (default) | 'half' | 'full'
|
// One of: 'normal' (default) | 'half' | 'full'
|
||||||
WindowSize string `yaml:"windowSize" jsonschema:"enum=normal,enum=half,enum=full"`
|
ScreenMode string `yaml:"screenMode" jsonschema:"enum=normal,enum=half,enum=full"`
|
||||||
// Window border style.
|
// Window border style.
|
||||||
// One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
// One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
||||||
Border string `yaml:"border" jsonschema:"enum=single,enum=double,enum=rounded,enum=hidden"`
|
Border string `yaml:"border" jsonschema:"enum=single,enum=double,enum=rounded,enum=hidden"`
|
||||||
@@ -161,6 +167,12 @@ type GuiConfig struct {
|
|||||||
// Status panel view.
|
// Status panel view.
|
||||||
// One of 'dashboard' (default) | 'allBranchesLog'
|
// One of 'dashboard' (default) | 'allBranchesLog'
|
||||||
StatusPanelView string `yaml:"statusPanelView" jsonschema:"enum=dashboard,enum=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 {
|
func (c *GuiConfig) UseFuzzySearch() bool {
|
||||||
@@ -224,10 +236,15 @@ type GitConfig struct {
|
|||||||
AutoRefresh bool `yaml:"autoRefresh"`
|
AutoRefresh bool `yaml:"autoRefresh"`
|
||||||
// If true, pass the --all arg to git fetch
|
// If true, pass the --all arg to git fetch
|
||||||
FetchAll bool `yaml:"fetchAll"`
|
FetchAll bool `yaml:"fetchAll"`
|
||||||
|
// If true, lazygit will automatically stage files that used to have merge
|
||||||
|
// 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
|
// Command used when displaying the current branch git log in the main window
|
||||||
BranchLogCmd string `yaml:"branchLogCmd"`
|
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.
|
// Deprecated: Use `allBranchesLogCmds` instead.
|
||||||
AllBranchesLogCmd string `yaml:"allBranchesLogCmd"`
|
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
|
// 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"`
|
AllBranchesLogCmds []string `yaml:"allBranchesLogCmds"`
|
||||||
@@ -385,7 +402,7 @@ type KeybindingUniversalConfig struct {
|
|||||||
ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"`
|
ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"`
|
||||||
ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"`
|
ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"`
|
||||||
ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"`
|
ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"`
|
||||||
ExecuteCustomCommand string `yaml:"executeCustomCommand"`
|
ExecuteShellCommand string `yaml:"executeShellCommand"`
|
||||||
CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"`
|
CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"`
|
||||||
Push string `yaml:"pushFiles"` // 'Files' appended for legacy reasons
|
Push string `yaml:"pushFiles"` // 'Files' appended for legacy reasons
|
||||||
Pull string `yaml:"pullFiles"` // 'Files' appended for legacy reasons
|
Pull string `yaml:"pullFiles"` // 'Files' appended for legacy reasons
|
||||||
@@ -541,7 +558,7 @@ type OSConfig struct {
|
|||||||
|
|
||||||
// A built-in preset that sets all of the above settings. Supported presets
|
// A built-in preset that sets all of the above settings. Supported presets
|
||||||
// are defined in the getPreset function in editor_presets.go.
|
// 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
|
// Command for opening a file, as if the file is double-clicked. Should
|
||||||
// contain "{{filename}}", but doesn't support "{{line}}".
|
// contain "{{filename}}", but doesn't support "{{line}}".
|
||||||
@@ -679,6 +696,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
ExpandedSidePanelWeight: 2,
|
ExpandedSidePanelWeight: 2,
|
||||||
MainPanelSplitMode: "flexible",
|
MainPanelSplitMode: "flexible",
|
||||||
EnlargedSideViewLocation: "left",
|
EnlargedSideViewLocation: "left",
|
||||||
|
WrapLinesInStagingView: true,
|
||||||
Language: "auto",
|
Language: "auto",
|
||||||
TimeFormat: "02 Jan 06",
|
TimeFormat: "02 Jan 06",
|
||||||
ShortTimeFormat: time.Kitchen,
|
ShortTimeFormat: time.Kitchen,
|
||||||
@@ -703,6 +721,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
ShowBottomLine: true,
|
ShowBottomLine: true,
|
||||||
ShowPanelJumps: true,
|
ShowPanelJumps: true,
|
||||||
ShowFileTree: true,
|
ShowFileTree: true,
|
||||||
|
ShowNumstatInFilesView: false,
|
||||||
ShowRandomTip: true,
|
ShowRandomTip: true,
|
||||||
ShowIcons: false,
|
ShowIcons: false,
|
||||||
NerdFontsVersion: "",
|
NerdFontsVersion: "",
|
||||||
@@ -715,7 +734,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
CommandLogSize: 8,
|
CommandLogSize: 8,
|
||||||
SplitDiff: "auto",
|
SplitDiff: "auto",
|
||||||
SkipRewordInEditorWarning: false,
|
SkipRewordInEditorWarning: false,
|
||||||
WindowSize: "normal",
|
ScreenMode: "normal",
|
||||||
Border: "rounded",
|
Border: "rounded",
|
||||||
AnimateExplosion: true,
|
AnimateExplosion: true,
|
||||||
PortraitMode: "auto",
|
PortraitMode: "auto",
|
||||||
@@ -724,7 +743,10 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
Frames: []string{"|", "/", "-", "\\"},
|
Frames: []string{"|", "/", "-", "\\"},
|
||||||
Rate: 50,
|
Rate: 50,
|
||||||
},
|
},
|
||||||
StatusPanelView: "dashboard",
|
StatusPanelView: "dashboard",
|
||||||
|
SwitchToFilesAfterStashPop: true,
|
||||||
|
SwitchToFilesAfterStashApply: true,
|
||||||
|
SwitchTabsWithPanelJumpKeys: false,
|
||||||
},
|
},
|
||||||
Git: GitConfig{
|
Git: GitConfig{
|
||||||
Paging: PagingConfig{
|
Paging: PagingConfig{
|
||||||
@@ -753,6 +775,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
AutoFetch: true,
|
AutoFetch: true,
|
||||||
AutoRefresh: true,
|
AutoRefresh: true,
|
||||||
FetchAll: true,
|
FetchAll: true,
|
||||||
|
AutoStageResolvedConflicts: true,
|
||||||
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
|
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
|
||||||
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium",
|
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium",
|
||||||
DisableForcePushing: false,
|
DisableForcePushing: false,
|
||||||
@@ -824,7 +847,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
ScrollDownMainAlt1: "J",
|
ScrollDownMainAlt1: "J",
|
||||||
ScrollUpMainAlt2: "<c-u>",
|
ScrollUpMainAlt2: "<c-u>",
|
||||||
ScrollDownMainAlt2: "<c-d>",
|
ScrollDownMainAlt2: "<c-d>",
|
||||||
ExecuteCustomCommand: ":",
|
ExecuteShellCommand: ":",
|
||||||
CreateRebaseOptionsMenu: "m",
|
CreateRebaseOptionsMenu: "m",
|
||||||
Push: "P",
|
Push: "P",
|
||||||
Pull: "p",
|
Pull: "p",
|
||||||
|
|||||||
+12
-16
@@ -3,7 +3,6 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
@@ -25,7 +24,7 @@ func (self *BackgroundRoutineMgr) PauseBackgroundRefreshes(pause bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||||
userConfig := self.gui.UserConfig
|
userConfig := self.gui.UserConfig()
|
||||||
|
|
||||||
if userConfig.Git.AutoFetch {
|
if userConfig.Git.AutoFetch {
|
||||||
fetchInterval := userConfig.Refresher.FetchInterval
|
fetchInterval := userConfig.Refresher.FetchInterval
|
||||||
@@ -76,21 +75,18 @@ func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
|||||||
func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
||||||
self.gui.waitForIntro.Wait()
|
self.gui.waitForIntro.Wait()
|
||||||
|
|
||||||
isNew := self.gui.IsNewRepo
|
fetch := func() error {
|
||||||
userConfig := self.gui.UserConfig
|
err := self.backgroundFetch()
|
||||||
if !isNew {
|
self.gui.c.Render()
|
||||||
time.After(time.Duration(userConfig.Refresher.FetchInterval) * time.Second)
|
return err
|
||||||
}
|
|
||||||
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)
|
|
||||||
} else {
|
|
||||||
self.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), self.gui.stopChan, func() error {
|
|
||||||
err := self.backgroundFetch()
|
|
||||||
self.gui.c.Render()
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We want an immediate fetch at startup, and since goEvery starts by
|
||||||
|
// waiting for the interval, we need to trigger one manually first
|
||||||
|
_ = fetch()
|
||||||
|
|
||||||
|
userConfig := self.gui.UserConfig()
|
||||||
|
self.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), self.gui.stopChan, fetch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BackgroundRoutineMgr) startBackgroundFilesRefresh(refreshInterval int) {
|
func (self *BackgroundRoutineMgr) startBackgroundFilesRefresh(refreshInterval int) {
|
||||||
|
|||||||
@@ -55,11 +55,11 @@ func (gui *Gui) LogCommand(cmdStr string, commandLine bool) {
|
|||||||
func (gui *Gui) printCommandLogHeader() {
|
func (gui *Gui) printCommandLogHeader() {
|
||||||
introStr := fmt.Sprintf(
|
introStr := fmt.Sprintf(
|
||||||
gui.c.Tr.CommandLogHeader,
|
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))
|
fmt.Fprintln(gui.Views.Extras, style.FgCyan.Sprint(introStr))
|
||||||
|
|
||||||
if gui.c.UserConfig.Gui.ShowRandomTip {
|
if gui.c.UserConfig().Gui.ShowRandomTip {
|
||||||
fmt.Fprintf(
|
fmt.Fprintf(
|
||||||
gui.Views.Extras,
|
gui.Views.Extras,
|
||||||
"%s: %s",
|
"%s: %s",
|
||||||
@@ -70,7 +70,7 @@ func (gui *Gui) printCommandLogHeader() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getRandomTip() string {
|
func (gui *Gui) getRandomTip() string {
|
||||||
config := gui.c.UserConfig.Keybinding
|
config := gui.c.UserConfig().Keybinding
|
||||||
|
|
||||||
formattedKey := func(key string) string {
|
formattedKey := func(key string) string {
|
||||||
return keybindings.Label(key)
|
return keybindings.Label(key)
|
||||||
|
|||||||
+33
-66
@@ -1,7 +1,6 @@
|
|||||||
package gui
|
package gui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"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
|
// use when you don't want to return to the original context upon
|
||||||
// hitting escape: you want to go that context's parent instead.
|
// 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() {
|
if !c.IsFocusable() {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.Lock()
|
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)
|
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 {
|
if len(opts) > 1 {
|
||||||
return errors.New("cannot pass multiple opts to Push")
|
panic("cannot pass multiple opts to Push")
|
||||||
}
|
}
|
||||||
|
|
||||||
singleOpts := types.OnFocusOpts{}
|
singleOpts := types.OnFocusOpts{}
|
||||||
@@ -68,22 +67,18 @@ func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !c.IsFocusable() {
|
if !c.IsFocusable() {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
contextsToDeactivate, contextToActivate := self.pushToContextStack(c)
|
contextsToDeactivate, contextToActivate := self.pushToContextStack(c)
|
||||||
|
|
||||||
for _, contextToDeactivate := range contextsToDeactivate {
|
for _, contextToDeactivate := range contextsToDeactivate {
|
||||||
if err := self.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
|
self.deactivate(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()})
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if contextToActivate == nil {
|
if contextToActivate != nil {
|
||||||
return nil
|
self.Activate(contextToActivate, singleOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.ActivateContext(contextToActivate, singleOpts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjusts the context stack based on the context that's being pushed and
|
// Adjusts the context stack based on the context that's being pushed and
|
||||||
@@ -144,13 +139,13 @@ func (self *ContextMgr) pushToContextStack(c types.Context) ([]types.Context, ty
|
|||||||
return contextsToDeactivate, c
|
return contextsToDeactivate, c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ContextMgr) Pop() error {
|
func (self *ContextMgr) Pop() {
|
||||||
self.Lock()
|
self.Lock()
|
||||||
|
|
||||||
if len(self.ContextStack) == 1 {
|
if len(self.ContextStack) == 1 {
|
||||||
// cannot escape from bottommost context
|
// cannot escape from bottommost context
|
||||||
self.Unlock()
|
self.Unlock()
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentContext types.Context
|
var currentContext types.Context
|
||||||
@@ -160,44 +155,12 @@ func (self *ContextMgr) Pop() error {
|
|||||||
|
|
||||||
self.Unlock()
|
self.Unlock()
|
||||||
|
|
||||||
if err := self.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
|
self.deactivate(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.ActivateContext(newContext, types.OnFocusOpts{})
|
self.Activate(newContext, types.OnFocusOpts{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ContextMgr) RemoveContexts(contextsToRemove []types.Context) error {
|
func (self *ContextMgr) deactivate(c types.Context, opts types.OnFocusLostOpts) {
|
||||||
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 {
|
|
||||||
view, _ := self.gui.c.GocuiGui().View(c.GetViewName())
|
view, _ := self.gui.c.GocuiGui().View(c.GetViewName())
|
||||||
|
|
||||||
if opts.NewContextKey != context.SEARCH_CONTEXT_KEY {
|
if opts.NewContextKey != context.SEARCH_CONTEXT_KEY {
|
||||||
@@ -213,18 +176,14 @@ func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLos
|
|||||||
view.Visible = false
|
view.Visible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.HandleFocusLost(opts); err != nil {
|
c.HandleFocusLost(opts)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts) error {
|
func (self *ContextMgr) Activate(c types.Context, opts types.OnFocusOpts) {
|
||||||
viewName := c.GetViewName()
|
viewName := c.GetViewName()
|
||||||
v, err := self.gui.c.GocuiGui().View(viewName)
|
v, err := self.gui.c.GocuiGui().View(viewName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gui.helpers.Window.SetWindowContext(c)
|
self.gui.helpers.Window.SetWindowContext(c)
|
||||||
@@ -235,7 +194,7 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts)
|
|||||||
oldView.HighlightInactive = true
|
oldView.HighlightInactive = true
|
||||||
}
|
}
|
||||||
if _, err := self.gui.c.GocuiGui().SetCurrentView(viewName); err != nil {
|
if _, err := self.gui.c.GocuiGui().SetCurrentView(viewName); err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gui.helpers.Search.RenderSearchStatus(c)
|
self.gui.helpers.Search.RenderSearchStatus(c)
|
||||||
@@ -249,11 +208,7 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts)
|
|||||||
|
|
||||||
self.gui.c.GocuiGui().Cursor = v.Editable
|
self.gui.c.GocuiGui().Cursor = v.Editable
|
||||||
|
|
||||||
if err := c.HandleFocus(opts); err != nil {
|
c.HandleFocus(opts)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ContextMgr) Current() types.Context {
|
func (self *ContextMgr) Current() types.Context {
|
||||||
@@ -330,6 +285,18 @@ func (self *ContextMgr) IsCurrent(c types.Context) bool {
|
|||||||
return self.Current().GetKey() == c.GetKey()
|
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 {
|
func (self *ContextMgr) AllFilterable() []types.IFilterableContext {
|
||||||
var result []types.IFilterableContext
|
var result []types.IFilterableContext
|
||||||
|
|
||||||
@@ -392,7 +359,7 @@ func (self *ContextMgr) ContextForKey(key types.ContextKey) types.Context {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ContextMgr) PopupContexts() []types.Context {
|
func (self *ContextMgr) CurrentPopup() []types.Context {
|
||||||
self.RLock()
|
self.RLock()
|
||||||
defer self.RUnlock()
|
defer self.RUnlock()
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type BaseContext struct {
|
|||||||
keybindingsFns []types.KeybindingsFn
|
keybindingsFns []types.KeybindingsFn
|
||||||
mouseKeybindingsFns []types.MouseKeybindingsFn
|
mouseKeybindingsFns []types.MouseKeybindingsFn
|
||||||
onClickFn func() error
|
onClickFn func() error
|
||||||
onRenderToMainFn func() error
|
onRenderToMainFn func()
|
||||||
onFocusFn onFocusFn
|
onFocusFn onFocusFn
|
||||||
onFocusLostFn onFocusLostFn
|
onFocusLostFn onFocusLostFn
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ type BaseContext struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
onFocusFn = func(types.OnFocusOpts) error
|
onFocusFn = func(types.OnFocusOpts)
|
||||||
onFocusLostFn = func(types.OnFocusLostOpts) error
|
onFocusLostFn = func(types.OnFocusLostOpts)
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ types.IBaseContext = &BaseContext{}
|
var _ types.IBaseContext = &BaseContext{}
|
||||||
@@ -148,13 +148,13 @@ func (self *BaseContext) GetOnClick() func() error {
|
|||||||
return self.onClickFn
|
return self.onClickFn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BaseContext) AddOnRenderToMainFn(fn func() error) {
|
func (self *BaseContext) AddOnRenderToMainFn(fn func()) {
|
||||||
if fn != nil {
|
if fn != nil {
|
||||||
self.onRenderToMainFn = fn
|
self.onRenderToMainFn = fn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BaseContext) GetOnRenderToMain() func() error {
|
func (self *BaseContext) GetOnRenderToMain() func() {
|
||||||
return self.onRenderToMainFn
|
return self.onRenderToMainFn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,3 +212,7 @@ func (self *BaseContext) NeedsRerenderOnHeightChange() bool {
|
|||||||
func (self *BaseContext) Title() string {
|
func (self *BaseContext) Title() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) TotalContentHeight() int {
|
||||||
|
return self.view.ViewLinesHeight()
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
|||||||
c.State().GetItemOperation,
|
c.State().GetItemOperation,
|
||||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||||
c.Modes().Diffing.Ref,
|
c.Modes().Diffing.Ref,
|
||||||
c.Views().Branches.Width(),
|
c.Views().Branches.InnerWidth(),
|
||||||
c.Tr,
|
c.Tr,
|
||||||
c.UserConfig,
|
c.UserConfig(),
|
||||||
c.Model().Worktrees,
|
c.Model().Worktrees,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -80,6 +80,14 @@ func (self *BranchesContext) GetDiffTerminals() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BranchesContext) RefForAdjustingLineNumberInDiff() string {
|
||||||
|
branch := self.GetSelected()
|
||||||
|
if branch != nil {
|
||||||
|
return branch.ID()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (self *BranchesContext) ShowBranchHeadsInSubCommits() bool {
|
func (self *BranchesContext) ShowBranchHeadsInSubCommits() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
@@ -28,7 +30,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
|||||||
viewModel := filetree.NewCommitFileTreeViewModel(
|
viewModel := filetree.NewCommitFileTreeViewModel(
|
||||||
func() []*models.CommitFile { return c.Model().CommitFiles },
|
func() []*models.CommitFile { return c.Model().CommitFiles },
|
||||||
c.Log,
|
c.Log,
|
||||||
c.UserConfig.Gui.ShowFileTree,
|
c.UserConfig().Gui.ShowFileTree,
|
||||||
)
|
)
|
||||||
|
|
||||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||||
@@ -36,7 +38,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
|||||||
return [][]string{{style.FgRed.Sprint("(none)")}}
|
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)
|
lines := presentation.RenderCommitFileTree(viewModel, c.Git().Patch.PatchBuilder, showFileIcons)
|
||||||
return lo.Map(lines, func(line string, _ int) []string {
|
return lo.Map(lines, func(line string, _ int) []string {
|
||||||
return []string{line}
|
return []string{line}
|
||||||
@@ -75,6 +77,32 @@ func (self *CommitFilesContext) GetDiffTerminals() []string {
|
|||||||
return []string{self.GetRef().RefName()}
|
return []string{self.GetRef().RefName()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesContext) RefForAdjustingLineNumberInDiff() string {
|
||||||
|
if refs := self.GetRefRange(); refs != nil {
|
||||||
|
return refs.To.RefName()
|
||||||
|
}
|
||||||
|
return 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 {
|
func (self *CommitFilesContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||||
return nil
|
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()
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ type CommitMessageViewModel struct {
|
|||||||
// if true, then upon escaping from the commit message panel, we will preserve
|
// if true, then upon escaping from the commit message panel, we will preserve
|
||||||
// the message so that it's still shown next time we open the panel
|
// the message so that it's still shown next time we open the panel
|
||||||
preserveMessage bool
|
preserveMessage bool
|
||||||
|
// we remember the initial message so that we can tell whether we should preserve
|
||||||
|
// the message; if it's still identical to the initial message, we don't
|
||||||
|
initialMessage string
|
||||||
// the full preserved message (combined summary and description)
|
// the full preserved message (combined summary and description)
|
||||||
preservedMessage string
|
preservedMessage string
|
||||||
// invoked when pressing enter in the commit message panel
|
// invoked when pressing enter in the commit message panel
|
||||||
@@ -84,6 +87,10 @@ func (self *CommitMessageContext) SetPreservedMessage(message string) {
|
|||||||
self.viewModel.preservedMessage = message
|
self.viewModel.preservedMessage = message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitMessageContext) GetInitialMessage() string {
|
||||||
|
return strings.TrimSpace(self.viewModel.initialMessage)
|
||||||
|
}
|
||||||
|
|
||||||
func (self *CommitMessageContext) GetHistoryMessage() string {
|
func (self *CommitMessageContext) GetHistoryMessage() string {
|
||||||
return self.viewModel.historyMessage
|
return self.viewModel.historyMessage
|
||||||
}
|
}
|
||||||
@@ -101,11 +108,13 @@ func (self *CommitMessageContext) SetPanelState(
|
|||||||
summaryTitle string,
|
summaryTitle string,
|
||||||
descriptionTitle string,
|
descriptionTitle string,
|
||||||
preserveMessage bool,
|
preserveMessage bool,
|
||||||
|
initialMessage string,
|
||||||
onConfirm func(string, string) error,
|
onConfirm func(string, string) error,
|
||||||
onSwitchToEditor func(string) error,
|
onSwitchToEditor func(string) error,
|
||||||
) {
|
) {
|
||||||
self.viewModel.selectedindex = index
|
self.viewModel.selectedindex = index
|
||||||
self.viewModel.preserveMessage = preserveMessage
|
self.viewModel.preserveMessage = preserveMessage
|
||||||
|
self.viewModel.initialMessage = initialMessage
|
||||||
self.viewModel.onConfirm = onConfirm
|
self.viewModel.onConfirm = onConfirm
|
||||||
self.viewModel.onSwitchToEditor = onSwitchToEditor
|
self.viewModel.onSwitchToEditor = onSwitchToEditor
|
||||||
self.GetView().Title = summaryTitle
|
self.GetView().Title = summaryTitle
|
||||||
@@ -113,19 +122,19 @@ func (self *CommitMessageContext) SetPanelState(
|
|||||||
|
|
||||||
self.c.Views().CommitDescription.Subtitle = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionSubTitle,
|
self.c.Views().CommitDescription.Subtitle = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionSubTitle,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.Universal.TogglePanel),
|
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig().Keybinding.Universal.TogglePanel),
|
||||||
"commitMenuKeybinding": keybindings.Label(self.c.UserConfig.Keybinding.CommitMessage.CommitMenu),
|
"commitMenuKeybinding": keybindings.Label(self.c.UserConfig().Keybinding.CommitMessage.CommitMenu),
|
||||||
})
|
})
|
||||||
|
|
||||||
self.c.Views().CommitDescription.Visible = true
|
self.c.Views().CommitDescription.Visible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitMessageContext) RenderCommitLength() {
|
func (self *CommitMessageContext) RenderCommitLength() {
|
||||||
if !self.c.UserConfig.Gui.CommitLength.Show {
|
if self.c.UserConfig().Gui.CommitLength.Show {
|
||||||
return
|
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 {
|
func getBufferLength(view *gocui.View) string {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (self *ListContextTrait) FocusLine() {
|
|||||||
} else if self.renderOnlyVisibleLines {
|
} else if self.renderOnlyVisibleLines {
|
||||||
newOrigin, _ := self.GetViewTrait().ViewPortYBounds()
|
newOrigin, _ := self.GetViewTrait().ViewPortYBounds()
|
||||||
if oldOrigin != newOrigin {
|
if oldOrigin != newOrigin {
|
||||||
return self.HandleRender()
|
self.HandleRender()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -72,26 +72,26 @@ func formatListFooter(selectedLineIdx int, length int) string {
|
|||||||
return fmt.Sprintf("%d of %d", selectedLineIdx+1, length)
|
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.FocusLine()
|
||||||
|
|
||||||
self.GetViewTrait().SetHighlight(self.list.Len() > 0)
|
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)
|
self.GetViewTrait().SetOriginX(0)
|
||||||
|
|
||||||
if self.refreshViewportOnChange {
|
if self.refreshViewportOnChange {
|
||||||
self.refreshViewport()
|
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
|
// 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()
|
self.list.ClampSelection()
|
||||||
if self.renderOnlyVisibleLines {
|
if self.renderOnlyVisibleLines {
|
||||||
// Rendering only the visible area can save a lot of cell memory for
|
// Rendering only the visible area can save a lot of cell memory for
|
||||||
@@ -110,13 +110,12 @@ func (self *ListContextTrait) HandleRender() error {
|
|||||||
}
|
}
|
||||||
self.c.Render()
|
self.c.Render()
|
||||||
self.setFooter()
|
self.setFooter()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
|
func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
|
||||||
self.GetList().SetSelection(self.ViewIndexToModelIndex(selectedLineIdx))
|
self.GetList().SetSelection(self.ViewIndexToModelIndex(selectedLineIdx))
|
||||||
return self.HandleFocus(types.OnFocusOpts{})
|
self.HandleFocus(types.OnFocusOpts{})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool {
|
func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool {
|
||||||
@@ -132,7 +131,7 @@ func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, list contexts supporta range select
|
// By default, list contexts supports range select
|
||||||
func (self *ListContextTrait) RangeSelectEnabled() bool {
|
func (self *ListContextTrait) RangeSelectEnabled() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -140,3 +139,11 @@ func (self *ListContextTrait) RangeSelectEnabled() bool {
|
|||||||
func (self *ListContextTrait) RenderOnlyVisibleLines() bool {
|
func (self *ListContextTrait) RenderOnlyVisibleLines() bool {
|
||||||
return self.renderOnlyVisibleLines
|
return self.renderOnlyVisibleLines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ListContextTrait) TotalContentHeight() int {
|
||||||
|
result := self.list.Len()
|
||||||
|
if self.getNonModelItems != nil {
|
||||||
|
result += len(self.getNonModelItems())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -121,7 +121,10 @@ func (self *ListRenderer) insertNonModelItems(
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if item.Index+offset >= startIdx {
|
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)
|
lines = slices.Insert(lines, item.Index+offset-startIdx, padding+item.Content)
|
||||||
}
|
}
|
||||||
offset++
|
offset++
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
|||||||
getDisplayStrings := func(startIdx int, endIdx int) [][]string {
|
getDisplayStrings := func(startIdx int, endIdx int) [][]string {
|
||||||
selectedCommitHash := ""
|
selectedCommitHash := ""
|
||||||
|
|
||||||
if c.CurrentContext().GetKey() == LOCAL_COMMITS_CONTEXT_KEY {
|
if c.Context().Current().GetKey() == LOCAL_COMMITS_CONTEXT_KEY {
|
||||||
selectedCommit := viewModel.GetSelected()
|
selectedCommit := viewModel.GetSelected()
|
||||||
if selectedCommit != nil {
|
if selectedCommit != nil {
|
||||||
selectedCommitHash = selectedCommit.Hash
|
selectedCommitHash = selectedCommit.Hash
|
||||||
@@ -54,10 +54,10 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
|||||||
c.Modes().CherryPicking.SelectedHashSet(),
|
c.Modes().CherryPicking.SelectedHashSet(),
|
||||||
c.Modes().Diffing.Ref,
|
c.Modes().Diffing.Ref,
|
||||||
c.Modes().MarkedBaseCommit.GetHash(),
|
c.Modes().MarkedBaseCommit.GetHash(),
|
||||||
c.UserConfig.Gui.TimeFormat,
|
c.UserConfig().Gui.TimeFormat,
|
||||||
c.UserConfig.Gui.ShortTimeFormat,
|
c.UserConfig().Gui.ShortTimeFormat,
|
||||||
time.Now(),
|
time.Now(),
|
||||||
c.UserConfig.Git.ParseEmoji,
|
c.UserConfig().Git.ParseEmoji,
|
||||||
selectedCommitHash,
|
selectedCommitHash,
|
||||||
startIdx,
|
startIdx,
|
||||||
endIdx,
|
endIdx,
|
||||||
@@ -110,7 +110,7 @@ func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *ContextCommon
|
|||||||
self := &LocalCommitsViewModel{
|
self := &LocalCommitsViewModel{
|
||||||
ListViewModel: NewListViewModel(getModel),
|
ListViewModel: NewListViewModel(getModel),
|
||||||
limitCommits: true,
|
limitCommits: true,
|
||||||
showWholeGitGraph: c.UserConfig.Git.Log.ShowWholeGraph,
|
showWholeGitGraph: c.UserConfig().Git.Log.ShowWholeGraph,
|
||||||
}
|
}
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -128,6 +128,19 @@ func (self *LocalCommitsContext) GetSelectedRef() types.Ref {
|
|||||||
return commit
|
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
|
// Returns the commit hash of the selected commit, or an empty string if no
|
||||||
// commit is selected
|
// commit is selected
|
||||||
func (self *LocalCommitsContext) GetSelectedCommitHash() string {
|
func (self *LocalCommitsContext) GetSelectedCommitHash() string {
|
||||||
@@ -157,6 +170,14 @@ func (self *LocalCommitsContext) GetDiffTerminals() []string {
|
|||||||
return []string{itemId}
|
return []string{itemId}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *LocalCommitsContext) RefForAdjustingLineNumberInDiff() string {
|
||||||
|
commits, _, _ := self.GetSelectedItems()
|
||||||
|
if commits == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return commits[0].Hash
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
func (self *LocalCommitsContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||||
return searchModelCommits(caseSensitive, self.GetCommits(), self.ColumnPositions(), searchStr)
|
return searchModelCommits(caseSensitive, self.GetCommits(), self.ColumnPositions(), searchStr)
|
||||||
}
|
}
|
||||||
@@ -202,6 +223,15 @@ func shouldShowGraph(c *ContextCommon) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func searchModelCommits(caseSensitive bool, commits []*models.Commit, columnPositions []int, searchStr string) []gocui.SearchPosition {
|
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)
|
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) {
|
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
|
// The XStart and XEnd values are only used if the search string can't
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ func (self *MenuViewModel) GetNonModelItems() []*NonModelItem {
|
|||||||
// Don't display section headers when we are filtering, and the filter mode
|
// 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
|
// 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.
|
// (they are sorted by best match), so all the sections would be messed up.
|
||||||
if self.FilteredListViewModel.IsFiltering() && self.c.UserConfig.Gui.UseFuzzySearch() {
|
if self.FilteredListViewModel.IsFiltering() && self.c.UserConfig().Gui.UseFuzzySearch() {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,9 +197,7 @@ func (self *MenuContext) OnMenuPress(selectedItem *types.MenuItem) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.c.PopContext(); err != nil {
|
self.c.Context().Pop()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if selectedItem == nil {
|
if selectedItem == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -68,13 +68,11 @@ func (self *MergeConflictsContext) IsUserScrolling() bool {
|
|||||||
return self.viewModel.userVerticalScrolling
|
return self.viewModel.userVerticalScrolling
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsContext) RenderAndFocus() error {
|
func (self *MergeConflictsContext) RenderAndFocus() {
|
||||||
self.setContent()
|
self.setContent()
|
||||||
self.FocusSelection()
|
self.FocusSelection()
|
||||||
|
|
||||||
self.c.Render()
|
self.c.Render()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsContext) Render() error {
|
func (self *MergeConflictsContext) Render() error {
|
||||||
@@ -99,7 +97,7 @@ func (self *MergeConflictsContext) setContent() {
|
|||||||
|
|
||||||
func (self *MergeConflictsContext) FocusSelection() {
|
func (self *MergeConflictsContext) FocusSelection() {
|
||||||
if !self.IsUserScrolling() {
|
if !self.IsUserScrolling() {
|
||||||
_ = self.GetView().SetOriginY(self.GetOriginY())
|
self.GetView().SetOriginY(self.GetOriginY())
|
||||||
}
|
}
|
||||||
|
|
||||||
self.SetSelectedLineRange()
|
self.SetSelectedLineRange()
|
||||||
@@ -117,5 +115,5 @@ func (self *MergeConflictsContext) SetSelectedLineRange() {
|
|||||||
func (self *MergeConflictsContext) GetOriginY() int {
|
func (self *MergeConflictsContext) GetOriginY() int {
|
||||||
view := self.GetView()
|
view := self.GetView()
|
||||||
conflictMiddle := self.GetState().GetConflictMiddle()
|
conflictMiddle := self.GetState().GetConflictMiddle()
|
||||||
return int(math.Max(0, float64(conflictMiddle-(view.Height()/2))))
|
return int(math.Max(0, float64(conflictMiddle-(view.InnerHeight()/2))))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,14 @@ import "github.com/jesseduffield/lazygit/pkg/gui/types"
|
|||||||
|
|
||||||
type ParentContextMgr struct {
|
type ParentContextMgr struct {
|
||||||
ParentContext types.Context
|
ParentContext types.Context
|
||||||
// we can't know on the calling end whether a Context is actually a nil value without reflection, so we're storing this flag here to tell us. There has got to be a better way around this
|
|
||||||
hasParent bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.ParentContexter = (*ParentContextMgr)(nil)
|
var _ types.ParentContexter = (*ParentContextMgr)(nil)
|
||||||
|
|
||||||
func (self *ParentContextMgr) SetParentContext(context types.Context) {
|
func (self *ParentContextMgr) SetParentContext(context types.Context) {
|
||||||
self.ParentContext = context
|
self.ParentContext = context
|
||||||
self.hasParent = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ParentContextMgr) GetParentContext() (types.Context, bool) {
|
func (self *ParentContextMgr) GetParentContext() types.Context {
|
||||||
return self.ParentContext, self.hasParent
|
return self.ParentContext
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,13 @@ func NewPatchExplorerContext(
|
|||||||
mutex: &deadlock.Mutex{},
|
mutex: &deadlock.Mutex{},
|
||||||
getIncludedLineIndices: getIncludedLineIndices,
|
getIncludedLineIndices: getIncludedLineIndices,
|
||||||
SimpleContext: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
SimpleContext: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: view,
|
||||||
WindowName: windowName,
|
WindowName: windowName,
|
||||||
Key: key,
|
Key: key,
|
||||||
Kind: types.MAIN_CONTEXT,
|
Kind: types.MAIN_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
HighlightOnFocus: true,
|
HighlightOnFocus: true,
|
||||||
|
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_WIDTH_CHANGES,
|
||||||
})),
|
})),
|
||||||
SearchTrait: NewSearchTrait(c),
|
SearchTrait: NewSearchTrait(c),
|
||||||
}
|
}
|
||||||
@@ -53,10 +54,13 @@ func NewPatchExplorerContext(
|
|||||||
func(selectedLineIdx int) error {
|
func(selectedLineIdx int) error {
|
||||||
ctx.GetMutex().Lock()
|
ctx.GetMutex().Lock()
|
||||||
defer ctx.GetMutex().Unlock()
|
defer ctx.GetMutex().Unlock()
|
||||||
return ctx.NavigateTo(ctx.c.IsCurrentContext(ctx), selectedLineIdx)
|
ctx.NavigateTo(selectedLineIdx)
|
||||||
|
return nil
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.SetHandleRenderFunc(ctx.OnViewWidthChanged)
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,65 +82,58 @@ func (self *PatchExplorerContext) GetIncludedLineIndices() []int {
|
|||||||
return self.getIncludedLineIndices()
|
return self.getIncludedLineIndices()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) RenderAndFocus(isFocused bool) error {
|
func (self *PatchExplorerContext) RenderAndFocus() {
|
||||||
self.setContent(isFocused)
|
self.setContent()
|
||||||
|
|
||||||
self.FocusSelection()
|
self.FocusSelection()
|
||||||
self.c.Render()
|
self.c.Render()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) Render(isFocused bool) error {
|
func (self *PatchExplorerContext) Render() {
|
||||||
self.setContent(isFocused)
|
self.setContent()
|
||||||
|
|
||||||
self.c.Render()
|
self.c.Render()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) Focus() error {
|
func (self *PatchExplorerContext) Focus() {
|
||||||
self.FocusSelection()
|
self.FocusSelection()
|
||||||
self.c.Render()
|
self.c.Render()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) setContent(isFocused bool) {
|
func (self *PatchExplorerContext) setContent() {
|
||||||
self.GetView().SetContent(self.GetContentToRender(isFocused))
|
self.GetView().SetContent(self.GetContentToRender())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) FocusSelection() {
|
func (self *PatchExplorerContext) FocusSelection() {
|
||||||
view := self.GetView()
|
view := self.GetView()
|
||||||
state := self.GetState()
|
state := self.GetState()
|
||||||
_, viewHeight := view.Size()
|
bufferHeight := view.InnerHeight()
|
||||||
bufferHeight := viewHeight - 1
|
|
||||||
_, origin := view.Origin()
|
_, origin := view.Origin()
|
||||||
numLines := view.LinesHeight()
|
numLines := view.ViewLinesHeight()
|
||||||
|
|
||||||
newOriginY := state.CalculateOrigin(origin, bufferHeight, numLines)
|
newOriginY := state.CalculateOrigin(origin, bufferHeight, numLines)
|
||||||
|
|
||||||
_ = view.SetOriginY(newOriginY)
|
view.SetOriginY(newOriginY)
|
||||||
|
|
||||||
startIdx, endIdx := state.SelectedRange()
|
startIdx, endIdx := state.SelectedViewRange()
|
||||||
// As far as the view is concerned, we are always selecting a range
|
// As far as the view is concerned, we are always selecting a range
|
||||||
view.SetRangeSelectStart(startIdx)
|
view.SetRangeSelectStart(startIdx)
|
||||||
view.SetCursorY(endIdx - newOriginY)
|
view.SetCursorY(endIdx - newOriginY)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) GetContentToRender(isFocused bool) string {
|
func (self *PatchExplorerContext) GetContentToRender() string {
|
||||||
if self.GetState() == nil {
|
if self.GetState() == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.GetState().RenderForLineIndices(isFocused, self.GetIncludedLineIndices())
|
return self.GetState().RenderForLineIndices(self.GetIncludedLineIndices())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) NavigateTo(isFocused bool, selectedLineIdx int) error {
|
func (self *PatchExplorerContext) NavigateTo(selectedLineIdx int) {
|
||||||
self.GetState().SetLineSelectMode()
|
self.GetState().SetLineSelectMode()
|
||||||
self.GetState().SelectLine(selectedLineIdx)
|
self.GetState().SelectLine(selectedLineIdx)
|
||||||
|
|
||||||
return self.RenderAndFocus(isFocused)
|
self.RenderAndFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) GetMutex() *deadlock.Mutex {
|
func (self *PatchExplorerContext) GetMutex() *deadlock.Mutex {
|
||||||
@@ -146,3 +143,11 @@ func (self *PatchExplorerContext) GetMutex() *deadlock.Mutex {
|
|||||||
func (self *PatchExplorerContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
func (self *PatchExplorerContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *PatchExplorerContext) OnViewWidthChanged() {
|
||||||
|
if state := self.GetState(); state != nil {
|
||||||
|
state.OnViewWidthChanged(self.GetView())
|
||||||
|
self.setContent()
|
||||||
|
self.RenderAndFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
|||||||
c.Modes().CherryPicking.SelectedHashSet(),
|
c.Modes().CherryPicking.SelectedHashSet(),
|
||||||
c.Modes().Diffing.Ref,
|
c.Modes().Diffing.Ref,
|
||||||
time.Now(),
|
time.Now(),
|
||||||
c.UserConfig.Gui.TimeFormat,
|
c.UserConfig().Gui.TimeFormat,
|
||||||
c.UserConfig.Gui.ShortTimeFormat,
|
c.UserConfig().Gui.ShortTimeFormat,
|
||||||
c.UserConfig.Git.ParseEmoji,
|
c.UserConfig().Git.ParseEmoji,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +71,11 @@ func (self *ReflogCommitsContext) GetSelectedRef() types.Ref {
|
|||||||
return commit
|
return commit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ReflogCommitsContext) GetSelectedRefRangeForDiffFiles() *types.RefRange {
|
||||||
|
// It doesn't make much sense to show a range diff between two reflog entries.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *ReflogCommitsContext) GetCommits() []*models.Commit {
|
func (self *ReflogCommitsContext) GetCommits() []*models.Commit {
|
||||||
return self.getModel()
|
return self.getModel()
|
||||||
}
|
}
|
||||||
@@ -81,6 +86,10 @@ func (self *ReflogCommitsContext) GetDiffTerminals() []string {
|
|||||||
return []string{itemId}
|
return []string{itemId}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ReflogCommitsContext) RefForAdjustingLineNumberInDiff() string {
|
||||||
|
return self.GetSelectedItemId()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *ReflogCommitsContext) ShowBranchHeadsInSubCommits() bool {
|
func (self *ReflogCommitsContext) ShowBranchHeadsInSubCommits() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ func (self *RemoteBranchesContext) GetDiffTerminals() []string {
|
|||||||
return []string{itemId}
|
return []string{itemId}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *RemoteBranchesContext) RefForAdjustingLineNumberInDiff() string {
|
||||||
|
return self.GetSelectedItemId()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *RemoteBranchesContext) ShowBranchHeadsInSubCommits() bool {
|
func (self *RemoteBranchesContext) ShowBranchHeadsInSubCommits() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func NewRemotesContext(c *ContextCommon) *RemotesContext {
|
|||||||
|
|
||||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||||
return presentation.GetRemoteListDisplayStrings(
|
return presentation.GetRemoteListDisplayStrings(
|
||||||
viewModel.GetItems(), c.Modes().Diffing.Ref, c.State().GetItemOperation, c.Tr, c.UserConfig)
|
viewModel.GetItems(), c.Modes().Diffing.Ref, c.State().GetItemOperation, c.Tr, c.UserConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &RemotesContext{
|
return &RemotesContext{
|
||||||
@@ -53,3 +53,7 @@ func (self *RemotesContext) GetDiffTerminals() []string {
|
|||||||
|
|
||||||
return []string{itemId}
|
return []string{itemId}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *RemotesContext) RefForAdjustingLineNumberInDiff() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user