mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 05:26:53 -04:00
Compare commits
289
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4465fec48 | ||
|
|
effda8291b | ||
|
|
7985e31020 | ||
|
|
866e0a618b | ||
|
|
a5ee61c117 | ||
|
|
08aad924d7 | ||
|
|
be02786dad | ||
|
|
949022db8c | ||
|
|
b1a090d4c0 | ||
|
|
39f3f150ed | ||
|
|
7e9f669421 | ||
|
|
6b769fb138 | ||
|
|
cc835a813e | ||
|
|
6103a4d13c | ||
|
|
69575dd4f3 | ||
|
|
bfcff3222c | ||
|
|
5adea789d0 | ||
|
|
78bbdca757 | ||
|
|
5cb82a49f8 | ||
|
|
9617737352 | ||
|
|
a251f6ad6c | ||
|
|
b61ca21a84 | ||
|
|
1ded318666 | ||
|
|
e15a99e626 | ||
|
|
d4eae73a68 | ||
|
|
5ba1eb2785 | ||
|
|
d71f5d951e | ||
|
|
adce8ad398 | ||
|
|
62ab41c310 | ||
|
|
85f7aa9d7b | ||
|
|
30ce7c8085 | ||
|
|
1827380c69 | ||
|
|
ea0baf58e6 | ||
|
|
1d96ade0ba | ||
|
|
82b3803164 | ||
|
|
f4ada537d2 | ||
|
|
956399a1ea | ||
|
|
057742d4af | ||
|
|
9c0a151dfa | ||
|
|
f9414f275d | ||
|
|
cc316ab6de | ||
|
|
8f00bfebce | ||
|
|
2dddd906f8 | ||
|
|
16ed3c2377 | ||
|
|
d44d164a5a | ||
|
|
90613056ce | ||
|
|
c05a1ae711 | ||
|
|
a0154dc525 | ||
|
|
6b9390409e | ||
|
|
8964cedf27 | ||
|
|
14ecc15e71 | ||
|
|
9e79ee5fe3 | ||
|
|
fdee0e1497 | ||
|
|
bf7726d130 | ||
|
|
6282d55919 | ||
|
|
e588355f57 | ||
|
|
c7a3b69eb9 | ||
|
|
b19943af01 | ||
|
|
015a04fac6 | ||
|
|
26ca41a40e | ||
|
|
6c4e7ee972 | ||
|
|
631cf1e873 | ||
|
|
585ea361f6 | ||
|
|
1a36cb9f3f | ||
|
|
5d982e1d70 | ||
|
|
4d734d594a | ||
|
|
b625eb5323 | ||
|
|
db7b472f9a | ||
|
|
8e46b8a275 | ||
|
|
cd989d8ebe | ||
|
|
261f30f49c | ||
|
|
7d7399a89f | ||
|
|
9df634f13f | ||
|
|
3ca1292fb4 | ||
|
|
13c1103815 | ||
|
|
b8bee4de51 | ||
|
|
d67b209e62 | ||
|
|
bf5871cc4f | ||
|
|
13326344f0 | ||
|
|
84870d4503 | ||
|
|
a9e2c8129f | ||
|
|
fd861826bc | ||
|
|
2be4359e87 | ||
|
|
c9a917b830 | ||
|
|
4df353d006 | ||
|
|
9ae7710850 | ||
|
|
87fe30d50d | ||
|
|
cff9850374 | ||
|
|
7cb54f4fee | ||
|
|
eb7e0e7252 | ||
|
|
074396a892 | ||
|
|
62cc036254 | ||
|
|
a8aeadfdb7 | ||
|
|
69df9ba105 | ||
|
|
5133113142 | ||
|
|
8051052ea7 | ||
|
|
ff8bc91a8e | ||
|
|
4e573e4bb1 | ||
|
|
2f6b7b9bc3 | ||
|
|
ee03a0be41 | ||
|
|
51a558040d | ||
|
|
6ecd691223 | ||
|
|
c4a2749a99 | ||
|
|
85f293af1a | ||
|
|
9cbd7fe69e | ||
|
|
4461dc68b7 | ||
|
|
6acabba417 | ||
|
|
7fb86d6e9c | ||
|
|
221433522d | ||
|
|
90084d115e | ||
|
|
a14794bf5c | ||
|
|
bf685cf832 | ||
|
|
8932d17393 | ||
|
|
de69fbd645 | ||
|
|
1998d0724f | ||
|
|
3928d0ebda | ||
|
|
d66ca7751c | ||
|
|
ba160cb5db | ||
|
|
3d76c734aa | ||
|
|
cddf056f4d | ||
|
|
41b00c8313 | ||
|
|
06892cb0ac | ||
|
|
77c5d1761d | ||
|
|
6ab5d7f69b | ||
|
|
c410d26006 | ||
|
|
db1fd85e12 | ||
|
|
1489a31e48 | ||
|
|
1f42c8a387 | ||
|
|
e229e26fbe | ||
|
|
5c55ce6555 | ||
|
|
1f801b91e4 | ||
|
|
0080684c7c | ||
|
|
c92e687d3b | ||
|
|
b6a31369da | ||
|
|
a694c458dd | ||
|
|
ced773ab18 | ||
|
|
a741b81b21 | ||
|
|
cbbb281011 | ||
|
|
e6fc332748 | ||
|
|
042ab2f99a | ||
|
|
a9ae5063c2 | ||
|
|
1932c2366b | ||
|
|
dd34adb36c | ||
|
|
e98935f83e | ||
|
|
75293ff572 | ||
|
|
4ff02bd3b7 | ||
|
|
5df27c61ed | ||
|
|
c9136538b5 | ||
|
|
b250644ea8 | ||
|
|
caab31ff38 | ||
|
|
a4db44bc3d | ||
|
|
4b3f8055d0 | ||
|
|
378c50cf30 | ||
|
|
860fd23b42 | ||
|
|
33e5f8f776 | ||
|
|
16dceb813b | ||
|
|
61f00e6dd4 | ||
|
|
523be47865 | ||
|
|
31a2ea1f19 | ||
|
|
697157f5d5 | ||
|
|
ee4b9d20b1 | ||
|
|
a2bdab2135 | ||
|
|
c70c8e84f8 | ||
|
|
614a30134c | ||
|
|
6754335b26 | ||
|
|
d29b3e372e | ||
|
|
33789d67f0 | ||
|
|
01b3e8e8bb | ||
|
|
cc0edd42bb | ||
|
|
237dde598c | ||
|
|
5d8af7bbd8 | ||
|
|
1de876ed4d | ||
|
|
036a1ea519 | ||
|
|
29c738a88b | ||
|
|
c0d3bd412e | ||
|
|
16fa22a36e | ||
|
|
8e6967c702 | ||
|
|
0e0458f355 | ||
|
|
05bfa96936 | ||
|
|
be6acf2fbe | ||
|
|
bf092f76d6 | ||
|
|
9c384c5267 | ||
|
|
d772c9f1d4 | ||
|
|
e5534d9781 | ||
|
|
34755285a1 | ||
|
|
e0ecc9e835 | ||
|
|
add1de4138 | ||
|
|
ed496deeca | ||
|
|
1f8e838052 | ||
|
|
f212e19efd | ||
|
|
fb0931e1a1 | ||
|
|
ee1597415d | ||
|
|
63dc07fded | ||
|
|
70e473b25d | ||
|
|
1a4cf84b58 | ||
|
|
ad72a1f5a3 | ||
|
|
59379b45da | ||
|
|
2d4ca2b54f | ||
|
|
ec5075104a | ||
|
|
6f535d71c9 | ||
|
|
ec3a28df43 | ||
|
|
5b933762c2 | ||
|
|
3eed997161 | ||
|
|
526d8a8a76 | ||
|
|
e1fc90615d | ||
|
|
460a166e16 | ||
|
|
2e66d87b94 | ||
|
|
820f7b9404 | ||
|
|
3cddd7cfa5 | ||
|
|
2e0d0a92ee | ||
|
|
ed857d1e07 | ||
|
|
b30ec538fb | ||
|
|
ee11046d35 | ||
|
|
25f8b0337e | ||
|
|
63ddc52a6b | ||
|
|
b07c4fc001 | ||
|
|
2304ffc804 | ||
|
|
401610c0ef | ||
|
|
64b2685c2d | ||
|
|
7d4bfb6621 | ||
|
|
a2778f01c6 | ||
|
|
d161afe37f | ||
|
|
3dd96a8010 | ||
|
|
681a9bf20d | ||
|
|
0606b7a43b | ||
|
|
33da56eeb4 | ||
|
|
46b93bba0e | ||
|
|
d2d50aedd0 | ||
|
|
ea5fb6a364 | ||
|
|
a82134f41c | ||
|
|
00b03079d8 | ||
|
|
9592686629 | ||
|
|
114ad52ff6 | ||
|
|
9514284f8e | ||
|
|
ece14844bb | ||
|
|
9910a7c308 | ||
|
|
910a61dc46 | ||
|
|
e5dd4d3110 | ||
|
|
595c7ee73e | ||
|
|
051af6a066 | ||
|
|
e2d7e461a8 | ||
|
|
7c66ca83c1 | ||
|
|
007b406b14 | ||
|
|
2b30085dba | ||
|
|
5c95d23169 | ||
|
|
e156e090cc | ||
|
|
0accb07dcc | ||
|
|
3cac14c76e | ||
|
|
1636931c2b | ||
|
|
6e027f42da | ||
|
|
5dacbb6293 | ||
|
|
8d68ab41b6 | ||
|
|
af97bf484c | ||
|
|
a57310df24 | ||
|
|
9d68b287db | ||
|
|
a5c72d056d | ||
|
|
8a86de85c8 | ||
|
|
dd31f8ecea | ||
|
|
5a7b2ab6d0 | ||
|
|
68a9d7fd77 | ||
|
|
19cbafcdfc | ||
|
|
f2c85c5b19 | ||
|
|
0faa41e6f8 | ||
|
|
2e32e55957 | ||
|
|
037cd99138 | ||
|
|
6388885699 | ||
|
|
d3e9bc2185 | ||
|
|
ea4587a3b8 | ||
|
|
2da300f2fb | ||
|
|
7848958326 | ||
|
|
2cba98e3fe | ||
|
|
f8c9ce33c2 | ||
|
|
71753770ad | ||
|
|
820b1e811d | ||
|
|
4a33fede7b | ||
|
|
db12853bbe | ||
|
|
711674f6cd | ||
|
|
fc91ef6a59 | ||
|
|
43251e7275 | ||
|
|
f081358943 | ||
|
|
0c6ab4b43e | ||
|
|
1b2fb34ffd | ||
|
|
0e5a4c7a36 | ||
|
|
47b91f1ef5 | ||
|
|
e2db6a1732 | ||
|
|
509e3efa70 | ||
|
|
8edad826ca | ||
|
|
e842548fc8 | ||
|
|
33f332e28d |
@@ -0,0 +1,26 @@
|
|||||||
|
changelog:
|
||||||
|
exclude:
|
||||||
|
labels:
|
||||||
|
- ignore-for-release
|
||||||
|
categories:
|
||||||
|
- title: Features ✨
|
||||||
|
labels:
|
||||||
|
- feature
|
||||||
|
- title: Enhancements 🔥
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
- title: Fixes 🔧
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
- title: Maintenance ⚙️
|
||||||
|
labels:
|
||||||
|
- maintenance
|
||||||
|
- title: Docs 📖
|
||||||
|
labels:
|
||||||
|
- docs
|
||||||
|
- title: I18n 🌎
|
||||||
|
labels:
|
||||||
|
- i18n
|
||||||
|
- title: Other Changes
|
||||||
|
labels:
|
||||||
|
- "*"
|
||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Unshallow repo
|
- name: Unshallow repo
|
||||||
run: git fetch --prune --unshallow
|
run: git fetch --prune --unshallow
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
GOFLAGS: -mod=vendor
|
GOFLAGS: -mod=vendor
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
@@ -47,13 +47,47 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
go test ./... -short
|
go test ./... -short
|
||||||
integration-tests:
|
integration-tests:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
git-version:
|
||||||
|
- 2.20.0 # oldest supported version
|
||||||
|
- 2.22.5
|
||||||
|
- 2.23.0
|
||||||
|
- 2.25.1
|
||||||
|
- 2.30.8
|
||||||
|
- latest # We rely on github to have the latest version installed on their VMs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "Integration Tests"
|
name: "Integration Tests - git ${{matrix.git-version}}"
|
||||||
env:
|
env:
|
||||||
GOFLAGS: -mod=vendor
|
GOFLAGS: -mod=vendor
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
- name: Restore Git cache
|
||||||
|
if: matrix.git-version != 'latest'
|
||||||
|
id: cache-git-restore
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: ~/git-${{matrix.git-version}}
|
||||||
|
key: ${{runner.os}}-git-${{matrix.git-version}}
|
||||||
|
- name: Build Git ${{matrix.git-version}}
|
||||||
|
if: steps.cache-git-restore.outputs.cache-hit != 'true' && matrix.git-version != 'latest'
|
||||||
|
run: >
|
||||||
|
sudo apt-get update && sudo apt-get install --no-install-recommends -y build-essential ca-certificates curl gettext libexpat1-dev libssl-dev libz-dev openssl
|
||||||
|
&& curl -sL "https://mirrors.edge.kernel.org/pub/software/scm/git/git-${{matrix.git-version}}.tar.xz" -o - | tar xJ -C "$HOME"
|
||||||
|
&& cd "$HOME/git-${{matrix.git-version}}"
|
||||||
|
&& ./configure
|
||||||
|
&& make -j
|
||||||
|
- name: Install Git ${{matrix.git-version}}
|
||||||
|
if: matrix.git-version != 'latest'
|
||||||
|
run: sudo make -C "$HOME/git-${{matrix.git-version}}" -j install
|
||||||
|
- name: Save Git cache
|
||||||
|
if: steps.cache-git-restore.outputs.cache-hit != 'true' && matrix.git-version != 'latest'
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
with:
|
||||||
|
path: ~/git-${{matrix.git-version}}
|
||||||
|
key: ${{runner.os}}-git-${{matrix.git-version}}
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
@@ -67,10 +101,11 @@ jobs:
|
|||||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{runner.os}}-go-
|
${{runner.os}}-go-
|
||||||
|
- name: Print git version
|
||||||
|
run: git --version
|
||||||
- name: Test code
|
- name: Test code
|
||||||
# LONG_WAIT_BEFORE_FAIL means that for a given test assertion, we'll wait longer before failing
|
|
||||||
run: |
|
run: |
|
||||||
LONG_WAIT_BEFORE_FAIL=true go test pkg/integration/clients/*.go
|
./scripts/run_integration_tests.sh
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
@@ -78,7 +113,7 @@ jobs:
|
|||||||
GOARCH: amd64
|
GOARCH: amd64
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
@@ -114,7 +149,7 @@ jobs:
|
|||||||
GOARCH: amd64
|
GOARCH: amd64
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
@@ -148,7 +183,7 @@ jobs:
|
|||||||
GOFLAGS: -mod=vendor
|
GOFLAGS: -mod=vendor
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
@@ -169,3 +204,14 @@ jobs:
|
|||||||
- name: errors
|
- name: errors
|
||||||
run: golangci-lint run
|
run: golangci-lint run
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
|
label:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# permissions:
|
||||||
|
# issues: write
|
||||||
|
# pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: mheap/github-action-required-labels@v5
|
||||||
|
with:
|
||||||
|
mode: exactly
|
||||||
|
count: 1
|
||||||
|
labels: "ignore-for-release, feature, enhancement, maintenance, docs, i18n"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout 🛎️
|
- name: Checkout 🛎️
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Generate Sponsors 💖
|
- name: Generate Sponsors 💖
|
||||||
uses: JamesIves/github-sponsors-readme-action@v1.0.8
|
uses: JamesIves/github-sponsors-readme-action@v1.0.8
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
name: Build PR Image
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, closed]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-application:
|
|
||||||
name: Build and Push `lazygit`
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
|
||||||
outputs:
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout git repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
- name: Generate UUID image name
|
|
||||||
id: uuid
|
|
||||||
run: echo "UUID_APP_TAG=$(uuidgen)" >> $GITHUB_ENV
|
|
||||||
- name: Docker metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
with:
|
|
||||||
images: registry.uffizzi.com/${{ env.UUID_APP_TAG }}
|
|
||||||
tags: type=raw,value=60d
|
|
||||||
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
context: ./
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
file: ./uffizzi/DockerfileTtyd
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
|
|
||||||
render-compose-file:
|
|
||||||
name: Render Docker Compose File
|
|
||||||
# Pass output of this workflow to another triggered by `workflow_run` event.
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- build-application
|
|
||||||
outputs:
|
|
||||||
compose-file-cache-key: ${{ steps.hash.outputs.hash }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout git repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Render Compose File
|
|
||||||
run: |
|
|
||||||
APP_IMAGE=$(echo ${{ needs.build-application.outputs.tags }})
|
|
||||||
export APP_IMAGE
|
|
||||||
# Render simple template from environment variables.
|
|
||||||
envsubst < ./uffizzi/docker-compose.uffizzi.yml > docker-compose.rendered.yml
|
|
||||||
cat docker-compose.rendered.yml
|
|
||||||
- name: Upload Rendered Compose File as Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: preview-spec
|
|
||||||
path: docker-compose.rendered.yml
|
|
||||||
retention-days: 2
|
|
||||||
- name: Upload PR Event as Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: preview-spec
|
|
||||||
path: ${{ github.event_path }}
|
|
||||||
retention-days: 2
|
|
||||||
|
|
||||||
delete-preview:
|
|
||||||
name: Call for Preview Deletion
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event.action == 'closed' }}
|
|
||||||
steps:
|
|
||||||
# If this PR is closing, we will not render a compose file nor pass it to the next workflow.
|
|
||||||
- name: Upload PR Event as Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: preview-spec
|
|
||||||
path: ${{ github.event_path }}
|
|
||||||
retention-days: 2
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
name: Deploy Uffizzi Preview
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows:
|
|
||||||
- "Build PR Image"
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cache-compose-file:
|
|
||||||
name: Cache Compose File
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
||||||
outputs:
|
|
||||||
compose-file-cache-key: ${{ env.COMPOSE_FILE_HASH }}
|
|
||||||
pr-number: ${{ env.PR_NUMBER }}
|
|
||||||
steps:
|
|
||||||
- name: 'Download artifacts'
|
|
||||||
# Fetch output (zip archive) from the workflow run that triggered this workflow.
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
run_id: context.payload.workflow_run.id,
|
|
||||||
});
|
|
||||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
|
||||||
return artifact.name == "preview-spec"
|
|
||||||
})[0];
|
|
||||||
if (matchArtifact === undefined) {
|
|
||||||
throw TypeError('Build Artifact not found!');
|
|
||||||
}
|
|
||||||
let download = await github.rest.actions.downloadArtifact({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
artifact_id: matchArtifact.id,
|
|
||||||
archive_format: 'zip',
|
|
||||||
});
|
|
||||||
let fs = require('fs');
|
|
||||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data));
|
|
||||||
- name: 'Unzip artifact'
|
|
||||||
run: unzip preview-spec.zip
|
|
||||||
|
|
||||||
- name: Read Event into ENV
|
|
||||||
run: |
|
|
||||||
echo 'EVENT_JSON<<EOF' >> $GITHUB_ENV
|
|
||||||
cat event.json >> $GITHUB_ENV
|
|
||||||
echo -e '\nEOF' >> $GITHUB_ENV
|
|
||||||
- name: Hash Rendered Compose File
|
|
||||||
id: hash
|
|
||||||
# If the previous workflow was triggered by a PR close event, we will not have a compose file artifact.
|
|
||||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
|
||||||
run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Cache Rendered Compose File
|
|
||||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: docker-compose.rendered.yml
|
|
||||||
key: ${{ env.COMPOSE_FILE_HASH }}
|
|
||||||
|
|
||||||
- name: Read PR Number From Event Object
|
|
||||||
id: pr
|
|
||||||
run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: DEBUG - Print Job Outputs
|
|
||||||
if: ${{ runner.debug }}
|
|
||||||
run: |
|
|
||||||
echo "PR number: ${{ env.PR_NUMBER }}"
|
|
||||||
echo "Compose file hash: ${{ env.COMPOSE_FILE_HASH }}"
|
|
||||||
cat event.json
|
|
||||||
deploy-uffizzi-preview:
|
|
||||||
name: Use Remote Workflow to Preview on Uffizzi
|
|
||||||
needs:
|
|
||||||
- cache-compose-file
|
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
||||||
uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2
|
|
||||||
with:
|
|
||||||
# If this workflow was triggered by a PR close event, cache-key will be an empty string
|
|
||||||
# and this reusable workflow will delete the preview deployment.
|
|
||||||
compose-file-cache-key: ${{ needs.cache-compose-file.outputs.compose-file-cache-key }}
|
|
||||||
compose-file-cache-path: docker-compose.rendered.yml
|
|
||||||
server: https://app.uffizzi.com
|
|
||||||
pr-number: ${{ needs.cache-compose-file.outputs.pr-number }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
id-token: write
|
|
||||||
Vendored
+2
-2
@@ -5,8 +5,8 @@
|
|||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "Generate cheatsheet",
|
"label": "Generate cheatsheet",
|
||||||
"type": "process",
|
"type": "shell",
|
||||||
"command": "go run scripts/cheatsheet/main.go ",
|
"command": "go run scripts/cheatsheet/main.go generate",
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-22
@@ -99,36 +99,19 @@ go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
|
|||||||
|
|
||||||
## Internationalisation
|
## Internationalisation
|
||||||
|
|
||||||
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `app.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
|
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `self.c.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
|
||||||
|
|
||||||
|
Note, we use 'Sentence case' for everything (so no 'Title Case' or 'whatever-it's-called-when-there's-no-capital-letters-case')
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
The easiest way to debug lazygit is to have two terminal tabs open at once: one for running lazygit (via `go run main.go -debug` in the project root) and one for viewing lazygit's logs (which can be done via `go run main.go --logs` or just `lazygit --logs`).
|
The easiest way to debug lazygit is to have two terminal tabs open at once: one for running lazygit (via `go run main.go -debug` in the project root) and one for viewing lazygit's logs (which can be done via `go run main.go --logs` or just `lazygit --logs`).
|
||||||
|
|
||||||
From most places in the codebase you have access to a logger e.g. `gui.Log.Warn("blah")`.
|
From most places in the codebase you have access to a logger e.g. `gui.Log.Warn("blah")` or `self.c.Log.Warn("blah")`.
|
||||||
|
|
||||||
If you find that the existing logs are too noisy, you can set the log level with e.g. `LOG_LEVEL=warn go run main.go -debug` and then only use `Warn` logs yourself.
|
If you find that the existing logs are too noisy, you can set the log level with e.g. `LOG_LEVEL=warn go run main.go -debug` and then only use `Warn` logs yourself.
|
||||||
|
|
||||||
If you need to log from code in the vendor directory (e.g. the `gocui` package), you won't have access to the logger, but you can easily add logging support by adding the following:
|
If you need to log from code in the vendor directory (e.g. the `gocui` package), you won't have access to the logger, but you can easily add logging support by setting the `LAZYGIT_LOG_PATH` environment variable and using `logs.Global.Warn("blah")`. This is a global logger that's only intended for development purposes.
|
||||||
|
|
||||||
```go
|
|
||||||
func newLogger() *logrus.Entry {
|
|
||||||
// REPLACE THE BELOW PATH WITH YOUR ACTUAL LOG PATH (YOU'LL SEE THIS PRINTED WHEN YOU RUN `lazygit --logs`
|
|
||||||
logPath := "/Users/jesseduffield/Library/Application Support/jesseduffield/lazygit/development.log"
|
|
||||||
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
panic("unable to log to file")
|
|
||||||
}
|
|
||||||
logger := logrus.New()
|
|
||||||
logger.SetLevel(logrus.WarnLevel)
|
|
||||||
logger.SetOutput(file)
|
|
||||||
return logger.WithFields(logrus.Fields{})
|
|
||||||
}
|
|
||||||
|
|
||||||
var Log = newLogger()
|
|
||||||
...
|
|
||||||
Log.Warn("blah")
|
|
||||||
```
|
|
||||||
|
|
||||||
If you keep having to do some setup steps to reproduce an issue, read the Testing section below to see how to create an integration test by recording a lazygit session. It's pretty easy!
|
If you keep having to do some setup steps to reproduce an issue, read the Testing section below to see how to create an integration test by recording a lazygit session. It's pretty easy!
|
||||||
|
|
||||||
|
|||||||
+22
-9
@@ -27,14 +27,18 @@ gui:
|
|||||||
sidePanelWidth: 0.3333 # number from 0 to 1
|
sidePanelWidth: 0.3333 # number from 0 to 1
|
||||||
expandFocusedSidePanel: false
|
expandFocusedSidePanel: false
|
||||||
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
|
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
|
||||||
language: 'auto' # one of 'auto' | 'en' | 'zh' | 'pl' | 'nl' | 'ja' | 'ko'
|
language: 'auto' # one of 'auto' | 'en' | 'zh' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||||
timeFormat: '02 Jan 06 15:04 MST' # https://pkg.go.dev/time#Time.Format
|
timeFormat: '02 Jan 06' # https://pkg.go.dev/time#Time.Format
|
||||||
|
shortTimeFormat: '3:04PM'
|
||||||
theme:
|
theme:
|
||||||
activeBorderColor:
|
activeBorderColor:
|
||||||
- green
|
- green
|
||||||
- bold
|
- bold
|
||||||
inactiveBorderColor:
|
inactiveBorderColor:
|
||||||
- white
|
- white
|
||||||
|
searchingActiveBorderColor:
|
||||||
|
- cyan
|
||||||
|
- bold
|
||||||
optionsTextColor:
|
optionsTextColor:
|
||||||
- blue
|
- blue
|
||||||
selectedLineBgColor:
|
selectedLineBgColor:
|
||||||
@@ -52,15 +56,17 @@ gui:
|
|||||||
commitLength:
|
commitLength:
|
||||||
show: true
|
show: true
|
||||||
mouseEvents: true
|
mouseEvents: true
|
||||||
skipUnstageLineWarning: false
|
skipDiscardChangeWarning: false
|
||||||
skipStashWarning: false
|
skipStashWarning: false
|
||||||
showFileTree: true # for rendering changes files in a tree format
|
showFileTree: true # for rendering changes files in a tree format
|
||||||
showListFooter: true # for seeing the '5 of 20' message in list panels
|
showListFooter: true # for seeing the '5 of 20' message in list panels
|
||||||
showRandomTip: true
|
showRandomTip: true
|
||||||
|
showBranchCommitHash: false # show commit hashes alongside branch names
|
||||||
experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list
|
experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list
|
||||||
showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
|
showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
|
||||||
showCommandLog: true
|
showCommandLog: true
|
||||||
showIcons: false
|
showIcons: false # deprecated: use nerdFontsVersion instead
|
||||||
|
nerdFontsVersion: "" # nerd fonts version to use ("2" or "3"); empty means don't show nerd font icons
|
||||||
commandLogSize: 8
|
commandLogSize: 8
|
||||||
splitDiff: 'auto' # one of 'auto' | 'always'
|
splitDiff: 'auto' # one of 'auto' | 'always'
|
||||||
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
||||||
@@ -71,7 +77,6 @@ git:
|
|||||||
useConfig: false
|
useConfig: false
|
||||||
commit:
|
commit:
|
||||||
signOff: false
|
signOff: false
|
||||||
verbose: default # one of 'default' | 'always' | 'never'
|
|
||||||
merging:
|
merging:
|
||||||
# only applicable to unix users
|
# only applicable to unix users
|
||||||
manualCommit: false
|
manualCommit: false
|
||||||
@@ -88,8 +93,12 @@ git:
|
|||||||
# displays the whole git graph by default in the commits panel (equivalent to passing the `--all` argument to `git log`)
|
# displays the whole git graph by default in the commits panel (equivalent to passing the `--all` argument to `git log`)
|
||||||
showWholeGraph: false
|
showWholeGraph: false
|
||||||
skipHookPrefix: WIP
|
skipHookPrefix: WIP
|
||||||
|
# The main branches. We colour commits green if they belong to one of these branches,
|
||||||
|
# so that you can easily see which commits are unique to your branch (coloured in yellow)
|
||||||
|
mainBranches: [master, main]
|
||||||
autoFetch: true
|
autoFetch: true
|
||||||
autoRefresh: true
|
autoRefresh: true
|
||||||
|
fetchAll: true # Pass --all flag when running git fetch. Set to false to fetch only origin (or the current branch's upstream remote if there is one)
|
||||||
branchLogCmd: 'git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --'
|
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'
|
||||||
overrideGpg: false # prevents lazygit from spawning a separate process when using GPG
|
overrideGpg: false # prevents lazygit from spawning a separate process when using GPG
|
||||||
@@ -285,9 +294,9 @@ os:
|
|||||||
editPreset: 'vscode'
|
editPreset: 'vscode'
|
||||||
```
|
```
|
||||||
|
|
||||||
Supported presets are `vim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`, and
|
Supported presets are `vim`, `nvim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`,
|
||||||
`xcode`. In many cases lazygit will be able to guess the right preset from your
|
`kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset
|
||||||
$(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||||
|
|
||||||
If for some reason you are not happy with the default commands from a preset, or
|
If for some reason you are not happy with the default commands from a preset, or
|
||||||
there simply is no preset for your editor, you can customize the commands by
|
there simply is no preset for your editor, you can customize the commands by
|
||||||
@@ -344,6 +353,7 @@ The available attributes are:
|
|||||||
- default
|
- default
|
||||||
- reverse # useful for high-contrast
|
- reverse # useful for high-contrast
|
||||||
- underline
|
- underline
|
||||||
|
- strikethrough
|
||||||
|
|
||||||
## Highlighting the selected line
|
## Highlighting the selected line
|
||||||
|
|
||||||
@@ -413,9 +423,12 @@ If you are using [Nerd Fonts](https://www.nerdfonts.com), you can display icons.
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
gui:
|
gui:
|
||||||
showIcons: true
|
nerdFontsVersion: "3"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Supported versions are "2" and "3". The deprecated config `showIcons` sets the
|
||||||
|
version to "2" for backwards compatibility.
|
||||||
|
|
||||||
## Keybindings
|
## Keybindings
|
||||||
|
|
||||||
For all possible keybinding options, check [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md)
|
For all possible keybinding options, check [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md)
|
||||||
|
|||||||
@@ -5,20 +5,22 @@ You can add custom command keybindings in your config.yml (accessible by pressin
|
|||||||
```yml
|
```yml
|
||||||
customCommands:
|
customCommands:
|
||||||
- key: '<c-r>'
|
- key: '<c-r>'
|
||||||
command: 'hub browse -- "commit/{{.SelectedLocalCommit.Sha}}"'
|
|
||||||
context: 'commits'
|
context: 'commits'
|
||||||
|
command: 'hub browse -- "commit/{{.SelectedLocalCommit.Sha}}"'
|
||||||
- key: 'a'
|
- key: 'a'
|
||||||
command: "git {{if .SelectedFile.HasUnstagedChanges}} add {{else}} reset {{end}} {{.SelectedFile.Name | quote}}"
|
|
||||||
context: 'files'
|
context: 'files'
|
||||||
description: 'toggle file staged'
|
command: "git {{if .SelectedFile.HasUnstagedChanges}} add {{else}} reset {{end}} {{.SelectedFile.Name | quote}}"
|
||||||
|
description: 'Toggle file staged'
|
||||||
- key: 'C'
|
- key: 'C'
|
||||||
command: "git commit"
|
|
||||||
context: 'global'
|
context: 'global'
|
||||||
|
command: "git commit"
|
||||||
subprocess: true
|
subprocess: true
|
||||||
- key: 'n'
|
- key: 'n'
|
||||||
|
context: 'localBranches'
|
||||||
prompts:
|
prompts:
|
||||||
- type: 'menu'
|
- type: 'menu'
|
||||||
title: 'What kind of branch is it?'
|
title: 'What kind of branch is it?'
|
||||||
|
key: 'BranchType'
|
||||||
options:
|
options:
|
||||||
- name: 'feature'
|
- name: 'feature'
|
||||||
description: 'a feature branch'
|
description: 'a feature branch'
|
||||||
@@ -31,110 +33,258 @@ customCommands:
|
|||||||
value: 'release'
|
value: 'release'
|
||||||
- type: 'input'
|
- type: 'input'
|
||||||
title: 'What is the new branch name?'
|
title: 'What is the new branch name?'
|
||||||
|
key: 'BranchName'
|
||||||
initialValue: ''
|
initialValue: ''
|
||||||
command: "git flow {{index .PromptResponses 0}} start {{index .PromptResponses 1}}"
|
command: "git flow {{.Form.BranchType}} start {{.Form.BranchName}}"
|
||||||
context: 'localBranches'
|
loadingText: 'Creating branch'
|
||||||
loadingText: 'creating branch'
|
|
||||||
- key : 'r'
|
|
||||||
description: 'Checkout a remote branch as FETCH_HEAD'
|
|
||||||
command: "git fetch {{index .PromptResponses 0}} {{index .PromptResponses 1}} && git checkout FETCH_HEAD"
|
|
||||||
context: 'remotes'
|
|
||||||
prompts:
|
|
||||||
- type: 'input'
|
|
||||||
title: 'Remote:'
|
|
||||||
initialValue: "{{index .SelectedRemote.Name }}"
|
|
||||||
- type: 'menuFromCommand'
|
|
||||||
title: 'Remote branch:'
|
|
||||||
command: 'git branch -r --list {{index .PromptResponses 0}}/*'
|
|
||||||
filter: '.*{{index .PromptResponses 0}}/(?P<branch>.*)'
|
|
||||||
valueFormat: '{{ .branch }}'
|
|
||||||
labelFormat: '{{ .branch | green }}'
|
|
||||||
- key: '<f1>'
|
|
||||||
command: 'git reset --soft {{.CheckedOutBranch.UpstreamRemote}}'
|
|
||||||
context: 'files'
|
|
||||||
prompts:
|
|
||||||
- type: 'confirm'
|
|
||||||
title: "Confirm:"
|
|
||||||
body: "Are you sure you want to reset HEAD to {{.CheckedOutBranch.UpstreamRemote}}?"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Looking at the command assigned to the 'n' key, here's what the result looks like:
|
Looking at the command assigned to the 'n' key, here's what the result looks like:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Custom command keybindings will appear alongside inbuilt keybindings when you view the options menu by pressing 'x':
|
Custom command keybindings will appear alongside inbuilt keybindings when you view the keybindings menu by pressing '?':
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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) | yes |
|
||||||
| command | the command to run | 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) | 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 (necessary if you want to view the output of the command or provide user input) | no |
|
| subprocess | Whether you want the command to run in a subprocess (e.g. if the command requires user input) | no |
|
||||||
| prompts | a list of prompts that will request user input before running the final command | no |
|
| prompts | A list of prompts that will request user input before running the final command | no |
|
||||||
| loadingText | text to display while waiting for command to finish | no |
|
| loadingText | Text to display while waiting for command to finish | no |
|
||||||
| description | text to display in the keybindings menu that appears when you press 'x' | no |
|
| description | Label for the custom command when displayed in the keybindings menu | no |
|
||||||
| stream | whether you want to stream the command's output to the Command Log panel | no |
|
| stream | Whether you want to stream the command's output to the Command Log panel | no |
|
||||||
| showOutput | whether you want to show the command's output in a gui prompt | no |
|
| showOutput | Whether you want to show the command's output in a popup within Lazygit | no |
|
||||||
|
| after | Actions to take after the command has completed | no |
|
||||||
|
|
||||||
### Contexts
|
Here are the options for the `after` key:
|
||||||
|
| _field_ | _description_ | required |
|
||||||
|
|-----------------|----------------------|-|
|
||||||
|
| checkForConflicts | true/false. If true, check for merge conflicts | no |
|
||||||
|
|
||||||
|
## Contexts
|
||||||
|
|
||||||
The permitted contexts are:
|
The permitted contexts are:
|
||||||
|
|
||||||
| _context_ | _description_ |
|
| _context_ | _description_ |
|
||||||
| -------------- | -------------------------------------------------------------------------------------------------------- |
|
| -------------- | -------------------------------------------------------------------------------------------------------- |
|
||||||
| status | the 'Status' tab |
|
| status | The 'Status' tab |
|
||||||
| files | the 'Files' tab |
|
| files | The 'Files' tab |
|
||||||
| localBranches | the 'Local Branches' tab |
|
| localBranches | The 'Local Branches' tab |
|
||||||
| remotes | the 'Remotes' tab |
|
| remotes | The 'Remotes' tab |
|
||||||
| remoteBranches | the context you get when pressing enter on a remote in the remotes tab |
|
| remoteBranches | The context you get when pressing enter on a remote in the remotes tab |
|
||||||
| tags | the 'Tags' tab |
|
| tags | The 'Tags' tab |
|
||||||
| commits | the 'Commits' tab |
|
| commits | The 'Commits' tab |
|
||||||
| reflogCommits | the 'Reflog' tab |
|
| reflogCommits | The 'Reflog' tab |
|
||||||
| subCommits | the context you see when pressing enter on a branch |
|
| subCommits | The context you see when pressing enter on a branch |
|
||||||
| commitFiles | the context you see when pressing enter on a commit or stash entry (warning, might be renamed in future) |
|
| commitFiles | The context you see when pressing enter on a commit or stash entry (warning, might be renamed in future) |
|
||||||
| stash | the 'Stash' tab |
|
| stash | The 'Stash' tab |
|
||||||
| global | this keybinding will take affect everywhere |
|
| global | This keybinding will take affect everywhere |
|
||||||
|
|
||||||
### Prompts
|
## Prompts
|
||||||
|
|
||||||
The permitted prompt fields are:
|
### Common fields
|
||||||
|
|
||||||
|
These fields are applicable to all prompts.
|
||||||
|
|
||||||
| _field_ | _description_ | _required_ |
|
| _field_ | _description_ | _required_ |
|
||||||
| ------------ | -----------------------------------------------------------------------------------------------| ---------- |
|
| ------------ | -----------------------------------------------------------------------------------------------| ---------- |
|
||||||
| type | one of 'input', 'menu', or 'confirm' | yes |
|
| type | One of 'input', 'confirm', 'menu', 'menuFromCommand' | yes |
|
||||||
| title | the title to display in the popup panel | no |
|
| title | The title to display in the popup panel | no |
|
||||||
| initialValue | (only applicable to 'input' prompts) the initial value to appear in the text box | no |
|
| key | Used to reference the entered value from within the custom command. E.g. a prompt with `key: 'Branch'` can be referred to as `{{.Form.Branch}}` in the command | yes |
|
||||||
| body | (only applicable to 'confirm' prompts) the immutable body text to appear in the text box | no |
|
|
||||||
| options | (only applicable to 'menu' prompts) the options to display in the menu | no |
|
### Input
|
||||||
| command | (only applicable to 'menuFromCommand' prompts) the command to run to generate | yes |
|
|
||||||
| | menu options | |
|
| _field_ | _description_ | _required_ |
|
||||||
| filter | (only applicable to 'menuFromCommand' prompts) the regexp to run specifying groups which are going to be kept from the command's output | yes |
|
| ------------ | -----------------------------------------------------------------------------------------------| ---------- |
|
||||||
| valueFormat | (only applicable to 'menuFromCommand' prompts) how to format matched groups from the filter to construct a menu item's value (What gets appended to prompt responses when the item is selected). You can use named groups, or `{{ .group_GROUPID }}`. PS: named groups keep first match only | yes |
|
| initialValue | The initial value to appear in the text box | no |
|
||||||
| labelFormat | (only applicable to 'menuFromCommand' prompts) how to format matched groups from the filter to construct the item's label (What's shown on screen). You can use named groups, or `{{ .group_GROUPID }}`. You can also color each match with `{{ .group_GROUPID \| colorname }}` (Color names from [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md)). If `labelFormat` is not specified, `valueFormat` is shown instead. PS: named groups keep first match only | no |
|
| suggestions | Shows suggestions as the input is entered. See below for details | no |
|
||||||
|
|
||||||
|
The permitted suggestions fields are:
|
||||||
|
| _field_ | _description_ | _required_ |
|
||||||
|
|-----------------|----------------------|-|
|
||||||
|
| preset | Uses built-in logic to obtain the suggestions. One of 'authors', 'branches', 'files', 'refs', 'remotes', 'remoteBranches', 'tags' | no |
|
||||||
|
| command | Command to run such that each line in the output becomes a suggestion. Mutually exclusive with 'preset' field. | no |
|
||||||
|
|
||||||
|
Here's an example of passing a preset:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
customCommands:
|
||||||
|
- key: 'a'
|
||||||
|
command: 'echo {{.Form.Branch | quote}}'
|
||||||
|
context: 'commits'
|
||||||
|
prompts:
|
||||||
|
- type: 'input'
|
||||||
|
title: 'Which branch?'
|
||||||
|
key: 'Branch'
|
||||||
|
suggestions:
|
||||||
|
preset: 'branches' # use built-in logic for obtaining branches
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's an example of passing a command directly:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
customCommands:
|
||||||
|
- key: 'a'
|
||||||
|
command: 'echo {{.Form.Branch | quote}}'
|
||||||
|
context: 'commits'
|
||||||
|
prompts:
|
||||||
|
- type: 'input'
|
||||||
|
title: 'Which branch?'
|
||||||
|
key: 'Branch'
|
||||||
|
suggestions:
|
||||||
|
command: "git branch --format='%(refname:short)'"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Here's an example of passing an initial value for the input:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
customCommands:
|
||||||
|
- key: 'a'
|
||||||
|
command: 'echo {{.Form.Remote | quote}}'
|
||||||
|
context: 'commits'
|
||||||
|
prompts:
|
||||||
|
- type: 'input'
|
||||||
|
title: 'Remote:'
|
||||||
|
key: 'Remote'
|
||||||
|
initialValue: "{{.SelectedRemote.Name}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Confirm
|
||||||
|
|
||||||
|
| _field_ | _description_ | _required_ |
|
||||||
|
| ------------ | -----------------------------------------------------------------------------------------------| ---------- |
|
||||||
|
| body | The immutable body text to appear in the text box | no |
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
customCommands:
|
||||||
|
- key: 'a'
|
||||||
|
command: 'echo "pushing to remote"'
|
||||||
|
context: 'commits'
|
||||||
|
prompts:
|
||||||
|
- type: 'confirm'
|
||||||
|
title: 'Push to remote'
|
||||||
|
body: 'Are you sure you want to push to the remote?'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Menu
|
||||||
|
|
||||||
|
| _field_ | _description_ | _required_ |
|
||||||
|
| ------------ | -----------------------------------------------------------------------------------------------| ---------- |
|
||||||
|
| options | The options to display in the menu | yes |
|
||||||
|
|
||||||
The permitted option fields are:
|
The permitted option fields are:
|
||||||
| _field_ | _description_ | _required_ |
|
| _field_ | _description_ | _required_ |
|
||||||
|-----------------|----------------------|-|
|
|-----------------|----------------------|-|
|
||||||
| name | the string which will appear first on the line | no |
|
| name | The first part of the label | no |
|
||||||
| description | the string which will appear second on the line | no |
|
| description | The second part of the label | no |
|
||||||
| value | the value that will be stored in `.PromptResponses` if the option is selected | yes |
|
| value | the value that will be used in the command | yes |
|
||||||
|
|
||||||
If an option has no name the value will be displayed to the user in place of the name, so you're allowed to only include the value like so:
|
If an option has no name the value will be displayed to the user in place of the name, so you're allowed to only include the value like so:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
customCommands:
|
||||||
|
- key: 'a'
|
||||||
|
command: 'echo {{.Form.BranchType | quote}}'
|
||||||
|
context: 'commits'
|
||||||
prompts:
|
prompts:
|
||||||
- type: 'menu'
|
- type: 'menu'
|
||||||
title: 'What kind of branch is it?'
|
title: 'What kind of branch is it?'
|
||||||
|
key: 'BranchType'
|
||||||
options:
|
options:
|
||||||
- value: 'feature'
|
- value: 'feature'
|
||||||
- value: 'hotfix'
|
- value: 'hotfix'
|
||||||
- value: 'release'
|
- value: 'release'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Placeholder values
|
Here's an example of supplying more detail for each option:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
customCommands:
|
||||||
|
- key: 'a'
|
||||||
|
command: 'echo {{.Form.BranchType | quote}}'
|
||||||
|
context: 'commits'
|
||||||
|
prompts:
|
||||||
|
- type: 'menu'
|
||||||
|
title: 'What kind of branch is it?'
|
||||||
|
key: 'BranchType'
|
||||||
|
options:
|
||||||
|
- value: 'feature'
|
||||||
|
name: 'feature branch'
|
||||||
|
description: 'branch based off develop'
|
||||||
|
- value: 'hotfix'
|
||||||
|
name: 'hotfix branch'
|
||||||
|
description: 'branch based off main for fast bug fixes'
|
||||||
|
- value: 'release'
|
||||||
|
name: 'release branch'
|
||||||
|
description: 'branch for a release'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Menu-from-command
|
||||||
|
|
||||||
|
| _field_ | _description_ | _required_ |
|
||||||
|
| ------------ | -----------------------------------------------------------------------------------------------| ---------- |
|
||||||
|
| command | The command to run to generate menu options | yes |
|
||||||
|
| filter | The regexp to run specifying groups which are going to be kept from the command's output | no |
|
||||||
|
| valueFormat | How to format matched groups from the filter to construct a menu item's value | no |
|
||||||
|
| labelFormat | Like valueFormat but for the labels. If `labelFormat` is not specified, `valueFormat` is shown instead. | no |
|
||||||
|
|
||||||
|
Here's an example using named groups in the regex. Notice how we can pipe the label to a colour function for coloured output (available colours [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md))
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- key : 'a'
|
||||||
|
description: 'Checkout a remote branch as FETCH_HEAD'
|
||||||
|
command: "git fetch {{.Form.Remote}} {{.Form.Branch}} && git checkout FETCH_HEAD"
|
||||||
|
context: 'remotes'
|
||||||
|
prompts:
|
||||||
|
- type: 'menuFromCommand'
|
||||||
|
title: 'Remote branch:'
|
||||||
|
key: 'Branch'
|
||||||
|
command: 'git branch -r --list {{.SelectedRemote.Name }}/*'
|
||||||
|
filter: '.*{{.SelectedRemote.Name }}/(?P<branch>.*)'
|
||||||
|
valueFormat: '{{ .branch }}'
|
||||||
|
labelFormat: '{{ .branch | green }}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's an example using unnamed groups:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- key : 'a'
|
||||||
|
description: 'Checkout a remote branch as FETCH_HEAD'
|
||||||
|
command: "git fetch {{.Form.Remote}} {{.Form.Branch}} && git checkout FETCH_HEAD"
|
||||||
|
context: 'remotes'
|
||||||
|
prompts:
|
||||||
|
- type: 'menuFromCommand'
|
||||||
|
title: 'Remote branch:'
|
||||||
|
key: 'Branch'
|
||||||
|
command: 'git branch -r --list {{.SelectedRemote.Name }}/*'
|
||||||
|
filter: '.*{{.SelectedRemote.Name }}/(.*)'
|
||||||
|
valueFormat: '{{ .group_1 }}'
|
||||||
|
labelFormat: '{{ .group_1 | green }}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's an example using a command but not specifying anything else: so each line from the command becomes the value and label of the menu items
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- key : 'a'
|
||||||
|
description: 'Checkout a remote branch as FETCH_HEAD'
|
||||||
|
command: "open {{.Form.File | quote}}"
|
||||||
|
context: 'global'
|
||||||
|
prompts:
|
||||||
|
- type: 'menuFromCommand'
|
||||||
|
title: 'File:'
|
||||||
|
key: 'File'
|
||||||
|
command: 'ls'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Placeholder values
|
||||||
|
|
||||||
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:
|
||||||
|
|
||||||
@@ -153,16 +303,16 @@ SelectedCommitFile
|
|||||||
CheckedOutBranch
|
CheckedOutBranch
|
||||||
```
|
```
|
||||||
|
|
||||||
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/commands/models/file.go) (all the modelling lives in the same directory). Note that the custom commands feature does not guarantee backwards compatibility (until we hit lazygit version 1.0 of course) which means a field you're accessing on an object may no longer be available from one release to the next. Typically however, all you'll need is `{{.SelectedFile.Name}}`, `{{.SelectedLocalCommit.Sha}}` and `{{.SelectedLocalBranch.Name}}`. In the future we will likely introduce a tighter interface that exposes a limited set of fields for each model.
|
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/commands/models/file.go) (all the modelling lives in the same directory). Note that the custom commands feature does not guarantee backwards compatibility (until we hit Lazygit version 1.0 of course) which means a field you're accessing on an object may no longer be available from one release to the next. Typically however, all you'll need is `{{.SelectedFile.Name}}`, `{{.SelectedLocalCommit.Sha}}` and `{{.SelectedLocalBranch.Name}}`. In the future we will likely introduce a tighter interface that exposes a limited set of fields for each model.
|
||||||
|
|
||||||
### Keybinding collisions
|
## Keybinding collisions
|
||||||
|
|
||||||
If your custom keybinding collides with an inbuilt keybinding that is defined for the same context, only the custom keybinding will be executed. This also applies to the global context. However, one caveat is that if you have a custom keybinding defined on the global context for some key, and there is an in-built keybinding defined for the same key and for a specific context (say the 'files' context), then the in-built keybinding will take precedence. See how to change in-built keybindings [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#keybindings)
|
If your custom keybinding collides with an inbuilt keybinding that is defined for the same context, only the custom keybinding will be executed. This also applies to the global context. However, one caveat is that if you have a custom keybinding defined on the global context for some key, and there is an in-built keybinding defined for the same key and for a specific context (say the 'files' context), then the in-built keybinding will take precedence. See how to change in-built keybindings [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#keybindings)
|
||||||
|
|
||||||
### Debugging
|
## Debugging
|
||||||
|
|
||||||
If you want to verify that your command actually does what you expect, you can wrap it in an 'echo' call and set `showOutput: true` so that it doesn't actually execute the command but you can see how the placeholders were resolved. Alternatively you can run lazygit in debug mode with `lazygit --debug` and in another terminal window run `lazygit --logs` to see which commands are actually run
|
If you want to verify that your command actually does what you expect, you can wrap it in an 'echo' call and set `showOutput: true` so that it doesn't actually execute the command but you can see how the placeholders were resolved.
|
||||||
|
|
||||||
### More Examples
|
## More Examples
|
||||||
|
|
||||||
See the [wiki](https://github.com/jesseduffield/lazygit/wiki/Custom-Commands-Compendium) page for more examples, and feel free to add your own custom commands to this page so others can benefit!
|
See the [wiki](https://github.com/jesseduffield/lazygit/wiki/Custom-Commands-Compendium) page for more examples, and feel free to add your own custom commands to this page so others can benefit!
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
see new docs [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
|
|
||||||
@@ -5,3 +5,5 @@
|
|||||||
* [Custom Pagers](./Custom_Pagers.md)
|
* [Custom Pagers](./Custom_Pagers.md)
|
||||||
* [Keybindings](./keybindings)
|
* [Keybindings](./keybindings)
|
||||||
* [Undo/Redo](./Undoing.md)
|
* [Undo/Redo](./Undoing.md)
|
||||||
|
* [Searching/Filtering](./Searching.md)
|
||||||
|
* [Dev docs](./dev)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Searching/Filtering
|
||||||
|
|
||||||
|
## View searching/filtering
|
||||||
|
|
||||||
|
Depending on the currently focused view, hitting '/' will bring up a filter or search prompt. When filtering, the contents of the view will be filtered down to only those lines which match the query string. When searching, the contents of the view are not filtered, but matching lines are highlighted and you can iterate through matches with `n`/`N`.
|
||||||
|
|
||||||
|
We intend to support filtering for the files view soon, but at the moment it uses searching. We intend to continue using search for the commits view because you typically care about the commits that come before/after a matching commit.
|
||||||
|
|
||||||
|
If you would like both filtering and searching to be enabled on a given view, please raise an issue for this.
|
||||||
|
|
||||||
|
## Filtering files by status
|
||||||
|
|
||||||
|
You can filter the files view to only show staged/unstaged files by pressing `<c-b>` in the files view.
|
||||||
|
|
||||||
|
## Filtering commits by file path
|
||||||
|
|
||||||
|
You can filter the commits view to only show commits which contain changes to a given file path.
|
||||||
|
|
||||||
|
You can do this in a couple of ways:
|
||||||
|
1) Start lazygit with the -f flag e.g. `lazygit -f my/path`
|
||||||
|
2) From within lazygit, press `<c-s>` and then enter the path of the file you want to filter by
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Knowing when Lazygit is busy/idle
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
1) press a key
|
||||||
|
2) wait until Lazygit is idle
|
||||||
|
3) run assertion / press another key
|
||||||
|
4) repeat
|
||||||
|
|
||||||
|
In the past the process was:
|
||||||
|
1) press a key
|
||||||
|
2) run assertion
|
||||||
|
3) if assertion fails, wait a bit and retry
|
||||||
|
4) repeat
|
||||||
|
|
||||||
|
The old process was problematic because an assertion may give a false positive due to the contents of some view not yet having changed since the last key was pressed.
|
||||||
|
|
||||||
|
## The solution
|
||||||
|
|
||||||
|
First, it's important to distinguish three different types of goroutines:
|
||||||
|
* The UI goroutine, of which there is only one, which infinitely processes a queue of events
|
||||||
|
* Worker goroutines, which do some work and then typically enqueue an event in the UI goroutine to display the results
|
||||||
|
* Background goroutines, which periodically spawn worker goroutines (e.g. doing a git fetch every minute)
|
||||||
|
|
||||||
|
The point of distinguishing worker goroutines from background goroutines is that when any worker goroutine is running, we consider Lazygit to be 'busy', whereas this is not the case with background goroutines. It would be pointless to have background goroutines be considered 'busy' because then Lazygit would be considered busy for the entire duration of the program!
|
||||||
|
|
||||||
|
In gocui, the underlying package we use for managing the UI and events, we keep track of how many busy goroutines there are using the `Task` type. A task represents some work being done by lazygit. The gocui Gui struct holds a map of tasks and allows creating a new task (which adds it to the map), pausing/continuing a task, and marking a task as done (which removes it from the map). Lazygit is considered to be busy so long as there is at least one busy task in the map; otherwise it's considered idle. When Lazygit goes from busy to idle, it notifies the integration test.
|
||||||
|
|
||||||
|
It's important that we play by the rules below to ensure that after the user does anything, all the processing that follows happens in a contiguous block of busy-ness with no gaps.
|
||||||
|
|
||||||
|
### Spawning a worker goroutine
|
||||||
|
|
||||||
|
Here's the basic implementation of `OnWorker` (using the same flow as `WaitGroup`s):
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (g *Gui) OnWorker(f func(*Task)) {
|
||||||
|
task := g.NewTask()
|
||||||
|
go func() {
|
||||||
|
f(task)
|
||||||
|
task.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The crucial thing here is that we create the task _before_ spawning the goroutine, because it means that we'll have at least one busy task in the map until the completion of the goroutine. If we created the task within the goroutine, the current function could exit and Lazygit would be considered idle before the goroutine starts, leading to our integration test prematurely progressing.
|
||||||
|
|
||||||
|
You typically invoke this with `self.c.OnWorker(f)`. Note that the callback function receives the task. This allows the callback to pause/continue the task (see below).
|
||||||
|
|
||||||
|
### Spawning a background goroutine
|
||||||
|
|
||||||
|
Spawning a background goroutine is as simple as:
|
||||||
|
|
||||||
|
```go
|
||||||
|
go utils.Safe(f)
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `utils.Safe` is a helper function that ensures we clean up the gui if the goroutine panics.
|
||||||
|
|
||||||
|
### Programmatically enqueing a UI event
|
||||||
|
|
||||||
|
This is invoked with `self.c.OnUIThread(f)`. Internally, it creates a task before enqueuing the function as an event (including the task in the event struct) and once that event is processed by the event queue (and any other pending events are processed) the task is removed from the map by calling `task.Done()`.
|
||||||
|
|
||||||
|
### Pressing a key
|
||||||
|
|
||||||
|
If the user presses a key, an event will be enqueued automatically and a task will be created before (and `Done`'d after) the event is processed.
|
||||||
|
|
||||||
|
## Special cases
|
||||||
|
|
||||||
|
There are a couple of special cases where we manually pause/continue the task directly in the client code. These are subject to change but for the sake of completeness:
|
||||||
|
|
||||||
|
### Writing to the main view(s)
|
||||||
|
|
||||||
|
If the user focuses a file in the files panel, we run a `git diff` command for that file and write the output to the main view. But we only read enough of the command's output to fill the view's viewport: further loading only happens if the user scrolls. Given that we have a background goroutine for running the command and writing more output upon scrolling, we create our own task and call `Done` on it as soon as the viewport is filled.
|
||||||
|
|
||||||
|
### Requesting credentials from a git command
|
||||||
|
|
||||||
|
Some git commands (e.g. git push) may request credentials. This is the same deal as above; we use a worker goroutine and manually pause continue its task as we go from waiting on the git command to waiting on user input. This requires passing the task through to the `Push` method so that it can be paused/continued.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
see new docs [here](../../pkg/integration/README.md)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Dev Documentation Overview
|
||||||
|
|
||||||
|
* [Busy/Idle tracking](./Busy.md).
|
||||||
|
* [Integration Tests](../../pkg/integration/README.md)
|
||||||
+247
-211
@@ -2,296 +2,332 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
|
|
||||||
# Lazygit Keybindings
|
# Lazygit Keybindings
|
||||||
|
|
||||||
## Global Keybindings
|
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||||
|
|
||||||
|
## Global keybindings
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+r</kbd>: switch to a recent repo
|
<kbd><c-r></kbd>: Switch to a recent repo
|
||||||
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
<kbd><pgup></kbd>: Scroll up main panel (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
<kbd><pgdown></kbd>: Scroll down main panel (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: view merge/rebase options
|
<kbd>@</kbd>: Open command log menu
|
||||||
<kbd>ctrl+p</kbd>: view custom patch options
|
|
||||||
<kbd>R</kbd>: refresh
|
|
||||||
<kbd>?</kbd>: open menu
|
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
|
||||||
<kbd>_</kbd>: prev screen mode
|
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
|
||||||
<kbd>W</kbd>: open diff menu
|
|
||||||
<kbd>ctrl+e</kbd>: open diff menu
|
|
||||||
<kbd>@</kbd>: open command log menu
|
|
||||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: execute custom command
|
<kbd>:</kbd>: Execute custom command
|
||||||
<kbd>z</kbd>: undo (via reflog) (experimental)
|
<kbd><c-p></kbd>: View custom patch options
|
||||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
<kbd>m</kbd>: View merge/rebase options
|
||||||
<kbd>P</kbd>: push
|
<kbd>R</kbd>: Refresh
|
||||||
<kbd>p</kbd>: pull
|
<kbd>+</kbd>: Next screen mode (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: Prev screen mode
|
||||||
|
<kbd>?</kbd>: Open menu
|
||||||
|
<kbd><c-s></kbd>: View filter-by-path options
|
||||||
|
<kbd>W</kbd>: Open diff menu
|
||||||
|
<kbd><c-e></kbd>: Open diff menu
|
||||||
|
<kbd><c-w></kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||||
|
<kbd>z</kbd>: Undo
|
||||||
|
<kbd><c-z></kbd>: Redo
|
||||||
|
<kbd>P</kbd>: Push
|
||||||
|
<kbd>p</kbd>: Pull
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## List Panel Navigation
|
## List panel navigation
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>,</kbd>: previous page
|
<kbd>,</kbd>: Previous page
|
||||||
<kbd>.</kbd>: next page
|
<kbd>.</kbd>: Next page
|
||||||
<kbd><</kbd>: scroll to top
|
<kbd><</kbd>: Scroll to top
|
||||||
<kbd>/</kbd>: start search
|
<kbd>></kbd>: Scroll to bottom
|
||||||
<kbd>></kbd>: scroll to bottom
|
<kbd>/</kbd>: Search the current view by text
|
||||||
<kbd>H</kbd>: scroll left
|
<kbd>H</kbd>: Scroll left
|
||||||
<kbd>L</kbd>: scroll right
|
<kbd>L</kbd>: Scroll right
|
||||||
<kbd>]</kbd>: next tab
|
<kbd>]</kbd>: Next tab
|
||||||
<kbd>[</kbd>: previous tab
|
<kbd>[</kbd>: Previous tab
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Commit Files
|
## Commit files
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy the committed file name to the clipboard
|
<kbd><c-o></kbd>: Copy the committed file name to the clipboard
|
||||||
<kbd>c</kbd>: checkout file
|
<kbd>c</kbd>: Checkout file
|
||||||
<kbd>d</kbd>: discard this commit's changes to this file
|
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||||
<kbd>o</kbd>: open file
|
<kbd>o</kbd>: Open file
|
||||||
<kbd>e</kbd>: edit file
|
<kbd>e</kbd>: Edit file
|
||||||
<kbd>space</kbd>: toggle file included in patch
|
<kbd><space></kbd>: Toggle file included in patch
|
||||||
<kbd>a</kbd>: toggle all files included in patch
|
<kbd>a</kbd>: Toggle all files included in patch
|
||||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||||
<kbd>`</kbd>: toggle file tree view
|
<kbd>`</kbd>: Toggle file tree view
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Commit summary
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Confirm
|
||||||
|
<kbd><esc></kbd>: Close
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Commits
|
## Commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>b</kbd>: view bisect options
|
<kbd>b</kbd>: View bisect options
|
||||||
<kbd>s</kbd>: squash down
|
<kbd>s</kbd>: Squash down
|
||||||
<kbd>f</kbd>: fixup commit
|
<kbd>f</kbd>: Fixup commit
|
||||||
<kbd>r</kbd>: reword commit
|
<kbd>r</kbd>: Reword commit
|
||||||
<kbd>R</kbd>: reword commit with editor
|
<kbd>R</kbd>: Reword commit with editor
|
||||||
<kbd>d</kbd>: delete commit
|
<kbd>d</kbd>: Delete commit
|
||||||
<kbd>e</kbd>: edit commit
|
<kbd>e</kbd>: Edit commit
|
||||||
<kbd>p</kbd>: pick commit (when mid-rebase)
|
<kbd>p</kbd>: Pick commit (when mid-rebase)
|
||||||
<kbd>F</kbd>: create fixup commit for this commit
|
<kbd>F</kbd>: Create fixup commit for this commit
|
||||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
|
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
|
||||||
<kbd>ctrl+j</kbd>: move commit down one
|
<kbd><c-j></kbd>: Move commit down one
|
||||||
<kbd>ctrl+k</kbd>: move commit up one
|
<kbd><c-k></kbd>: Move commit up one
|
||||||
<kbd>v</kbd>: paste commits (cherry-pick)
|
<kbd>v</kbd>: Paste commits (cherry-pick)
|
||||||
<kbd>A</kbd>: amend commit with staged changes
|
<kbd>A</kbd>: Amend commit with staged changes
|
||||||
<kbd>a</kbd>: reset commit author
|
<kbd>a</kbd>: Set/Reset commit author
|
||||||
<kbd>t</kbd>: revert commit
|
<kbd>t</kbd>: Revert commit
|
||||||
<kbd>T</kbd>: tag commit
|
<kbd>T</kbd>: Tag commit
|
||||||
<kbd>ctrl+l</kbd>: open log menu
|
<kbd><c-l></kbd>: Open log menu
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: create new branch off of commit
|
<kbd>n</kbd>: Create new branch off of commit
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: copy commit (cherry-pick)
|
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||||
<kbd>C</kbd>: copy commit range (cherry-pick)
|
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Confirmation panel
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Confirm
|
||||||
|
<kbd><esc></kbd>: Close/Cancel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy the file name to the clipboard
|
<kbd><c-o></kbd>: Copy the file name to the clipboard
|
||||||
<kbd>d</kbd>: view 'discard changes' options
|
<kbd>d</kbd>: View 'discard changes' options
|
||||||
<kbd>space</kbd>: toggle staged
|
<kbd><space></kbd>: Toggle staged
|
||||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
<kbd><c-b></kbd>: Filter files by status
|
||||||
<kbd>c</kbd>: commit changes
|
<kbd>c</kbd>: Commit changes
|
||||||
<kbd>w</kbd>: commit changes without pre-commit hook
|
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||||
<kbd>A</kbd>: amend last commit
|
<kbd>A</kbd>: Amend last commit
|
||||||
<kbd>C</kbd>: commit changes using git editor
|
<kbd>C</kbd>: Commit changes using git editor
|
||||||
<kbd>e</kbd>: edit file
|
<kbd>e</kbd>: Edit file
|
||||||
<kbd>o</kbd>: open file
|
<kbd>o</kbd>: Open file
|
||||||
<kbd>i</kbd>: ignore or exclude file
|
<kbd>i</kbd>: Ignore or exclude file
|
||||||
<kbd>r</kbd>: refresh files
|
<kbd>r</kbd>: Refresh files
|
||||||
<kbd>s</kbd>: stash all changes
|
<kbd>s</kbd>: Stash all changes
|
||||||
<kbd>S</kbd>: view stash options
|
<kbd>S</kbd>: View stash options
|
||||||
<kbd>a</kbd>: stage/unstage all
|
<kbd>a</kbd>: Stage/unstage all
|
||||||
<kbd>enter</kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
<kbd><enter></kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
|
||||||
<kbd>g</kbd>: view upstream reset options
|
<kbd>g</kbd>: View upstream reset options
|
||||||
<kbd>D</kbd>: view reset options
|
<kbd>D</kbd>: View reset options
|
||||||
<kbd>`</kbd>: toggle file tree view
|
<kbd>`</kbd>: Toggle file tree view
|
||||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||||
<kbd>f</kbd>: fetch
|
<kbd>f</kbd>: Fetch
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Local Branches
|
## Local branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
<kbd><c-o></kbd>: Copy branch name to clipboard
|
||||||
<kbd>i</kbd>: show git-flow options
|
<kbd>i</kbd>: Show git-flow options
|
||||||
<kbd>space</kbd>: checkout
|
<kbd><space></kbd>: Checkout
|
||||||
<kbd>n</kbd>: new branch
|
<kbd>n</kbd>: New branch
|
||||||
<kbd>o</kbd>: create pull request
|
<kbd>o</kbd>: Create pull request
|
||||||
<kbd>O</kbd>: create pull request options
|
<kbd>O</kbd>: Create pull request options
|
||||||
<kbd>ctrl+y</kbd>: copy pull request URL to clipboard
|
<kbd><c-y></kbd>: Copy pull request URL to clipboard
|
||||||
<kbd>c</kbd>: checkout by name
|
<kbd>c</kbd>: Checkout by name
|
||||||
<kbd>F</kbd>: force checkout
|
<kbd>F</kbd>: Force checkout
|
||||||
<kbd>d</kbd>: delete branch
|
<kbd>d</kbd>: Delete branch
|
||||||
<kbd>r</kbd>: rebase checked-out branch onto this branch
|
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||||
<kbd>M</kbd>: merge into currently checked out branch
|
<kbd>M</kbd>: Merge into currently checked out branch
|
||||||
<kbd>f</kbd>: fast-forward this branch from its upstream
|
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||||
<kbd>T</kbd>: create tag
|
<kbd>T</kbd>: Create tag
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>R</kbd>: rename branch
|
<kbd>R</kbd>: Rename branch
|
||||||
<kbd>u</kbd>: set/unset upstream
|
<kbd>u</kbd>: Set/Unset upstream
|
||||||
<kbd>enter</kbd>: view commits
|
<kbd><enter></kbd>: View commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Main Panel (Merging)
|
## Main panel (merging)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: edit file
|
<kbd>e</kbd>: Edit file
|
||||||
<kbd>o</kbd>: open file
|
<kbd>o</kbd>: Open file
|
||||||
<kbd>◀</kbd>: select previous conflict
|
<kbd><left></kbd>: Select previous conflict
|
||||||
<kbd>▶</kbd>: select next conflict
|
<kbd><right></kbd>: Select next conflict
|
||||||
<kbd>▲</kbd>: select previous hunk
|
<kbd><up></kbd>: Select previous hunk
|
||||||
<kbd>▼</kbd>: select next hunk
|
<kbd><down></kbd>: Select next hunk
|
||||||
<kbd>z</kbd>: undo
|
<kbd>z</kbd>: Undo
|
||||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||||
<kbd>space</kbd>: pick hunk
|
<kbd><space></kbd>: Pick hunk
|
||||||
<kbd>b</kbd>: pick all hunks
|
<kbd>b</kbd>: Pick all hunks
|
||||||
<kbd>esc</kbd>: return to files panel
|
<kbd><esc></kbd>: Return to files panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Main Panel (Normal)
|
## Main panel (normal)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>mouse wheel ▼</kbd>: scroll down (fn+up)
|
<kbd>mouse wheel down</kbd>: Scroll down (fn+up)
|
||||||
<kbd>mouse wheel ▲</kbd>: scroll up (fn+down)
|
<kbd>mouse wheel up</kbd>: Scroll up (fn+down)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Main Panel (Patch Building)
|
## Main panel (patch building)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: select previous hunk
|
<kbd><left></kbd>: Select previous hunk
|
||||||
<kbd>▶</kbd>: select next hunk
|
<kbd><right></kbd>: Select next hunk
|
||||||
<kbd>v</kbd>: toggle drag select
|
<kbd>v</kbd>: Toggle drag select
|
||||||
<kbd>V</kbd>: toggle drag select
|
<kbd>V</kbd>: Toggle drag select
|
||||||
<kbd>a</kbd>: toggle select hunk
|
<kbd>a</kbd>: Toggle select hunk
|
||||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||||
<kbd>o</kbd>: open file
|
<kbd>o</kbd>: Open file
|
||||||
<kbd>e</kbd>: edit file
|
<kbd>e</kbd>: Edit file
|
||||||
<kbd>space</kbd>: add/remove line(s) to patch
|
<kbd><space></kbd>: Add/Remove line(s) to patch
|
||||||
<kbd>esc</kbd>: exit custom patch builder
|
<kbd><esc></kbd>: Exit custom patch builder
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Main Panel (Staging)
|
## Main panel (staging)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: select previous hunk
|
<kbd><left></kbd>: Select previous hunk
|
||||||
<kbd>▶</kbd>: select next hunk
|
<kbd><right></kbd>: Select next hunk
|
||||||
<kbd>v</kbd>: toggle drag select
|
<kbd>v</kbd>: Toggle drag select
|
||||||
<kbd>V</kbd>: toggle drag select
|
<kbd>V</kbd>: Toggle drag select
|
||||||
<kbd>a</kbd>: toggle select hunk
|
<kbd>a</kbd>: Toggle select hunk
|
||||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||||
<kbd>o</kbd>: open file
|
<kbd>o</kbd>: Open file
|
||||||
<kbd>e</kbd>: edit file
|
<kbd>e</kbd>: Edit file
|
||||||
<kbd>esc</kbd>: return to files panel
|
<kbd><esc></kbd>: Return to files panel
|
||||||
<kbd>tab</kbd>: switch to other panel (staged/unstaged changes)
|
<kbd><tab></kbd>: Switch to other panel (staged/unstaged changes)
|
||||||
<kbd>space</kbd>: toggle line staged / unstaged
|
<kbd><space></kbd>: Toggle line staged / unstaged
|
||||||
<kbd>d</kbd>: delete change (git reset)
|
<kbd>d</kbd>: Discard change (git reset)
|
||||||
<kbd>E</kbd>: edit hunk
|
<kbd>E</kbd>: Edit hunk
|
||||||
<kbd>c</kbd>: commit changes
|
<kbd>c</kbd>: Commit changes
|
||||||
<kbd>w</kbd>: commit changes without pre-commit hook
|
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||||
<kbd>C</kbd>: commit changes using git editor
|
<kbd>C</kbd>: Commit changes using git editor
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Execute
|
||||||
|
<kbd><esc></kbd>: Close
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Reflog
|
## Reflog
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: create new branch off of commit
|
<kbd>n</kbd>: Create new branch off of commit
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: copy commit (cherry-pick)
|
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||||
<kbd>C</kbd>: copy commit range (cherry-pick)
|
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: view commits
|
<kbd><enter></kbd>: View commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Remote Branches
|
## Remote branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
<kbd><c-o></kbd>: Copy branch name to clipboard
|
||||||
<kbd>space</kbd>: checkout
|
<kbd><space></kbd>: Checkout
|
||||||
<kbd>n</kbd>: new branch
|
<kbd>n</kbd>: New branch
|
||||||
<kbd>M</kbd>: merge into currently checked out branch
|
<kbd>M</kbd>: Merge into currently checked out branch
|
||||||
<kbd>r</kbd>: rebase checked-out branch onto this branch
|
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||||
<kbd>d</kbd>: delete branch
|
<kbd>d</kbd>: Delete branch
|
||||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||||
<kbd>esc</kbd>: Return to remotes list
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd><enter></kbd>: View commits
|
||||||
<kbd>enter</kbd>: view commits
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Remotes
|
## Remotes
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>f</kbd>: fetch remote
|
<kbd>f</kbd>: Fetch remote
|
||||||
<kbd>n</kbd>: add new remote
|
<kbd>n</kbd>: Add new remote
|
||||||
<kbd>d</kbd>: remove remote
|
<kbd>d</kbd>: Remove remote
|
||||||
<kbd>e</kbd>: edit remote
|
<kbd>e</kbd>: Edit remote
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Stash
|
## Stash
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: apply
|
<kbd><space></kbd>: Apply
|
||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: Pop
|
||||||
<kbd>d</kbd>: drop
|
<kbd>d</kbd>: Drop
|
||||||
<kbd>n</kbd>: new branch
|
<kbd>n</kbd>: New branch
|
||||||
<kbd>r</kbd>: rename stash
|
<kbd>r</kbd>: Rename stash
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: edit config file
|
<kbd>o</kbd>: Open config file
|
||||||
<kbd>o</kbd>: open config file
|
<kbd>e</kbd>: Edit config file
|
||||||
<kbd>u</kbd>: check for update
|
<kbd>u</kbd>: Check for update
|
||||||
<kbd>enter</kbd>: switch to a recent repo
|
<kbd><enter></kbd>: Switch to a recent repo
|
||||||
<kbd>a</kbd>: show all branch logs
|
<kbd>a</kbd>: Show all branch logs
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Sub-commits
|
## Sub-commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: create new branch off of commit
|
<kbd>n</kbd>: Create new branch off of commit
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: copy commit (cherry-pick)
|
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||||
<kbd>C</kbd>: copy commit range (cherry-pick)
|
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Submodules
|
## Submodules
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy submodule name to clipboard
|
<kbd><c-o></kbd>: Copy submodule name to clipboard
|
||||||
<kbd>enter</kbd>: enter submodule
|
<kbd><enter></kbd>: Enter submodule
|
||||||
<kbd>d</kbd>: remove submodule
|
<kbd>d</kbd>: Remove submodule
|
||||||
<kbd>u</kbd>: update submodule
|
<kbd>u</kbd>: Update submodule
|
||||||
<kbd>n</kbd>: add new submodule
|
<kbd>n</kbd>: Add new submodule
|
||||||
<kbd>e</kbd>: update submodule URL
|
<kbd>e</kbd>: Update submodule URL
|
||||||
<kbd>i</kbd>: initialize submodule
|
<kbd>i</kbd>: Initialize submodule
|
||||||
<kbd>b</kbd>: view bulk submodule options
|
<kbd>b</kbd>: View bulk submodule options
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Tags
|
## Tags
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: checkout
|
<kbd><space></kbd>: Checkout
|
||||||
<kbd>d</kbd>: delete tag
|
<kbd>d</kbd>: Delete tag
|
||||||
<kbd>P</kbd>: push tag
|
<kbd>P</kbd>: Push tag
|
||||||
<kbd>n</kbd>: create tag
|
<kbd>n</kbd>: Create tag
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>enter</kbd>: view commits
|
<kbd><enter></kbd>: View commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
+149
-113
@@ -2,30 +2,32 @@ _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_
|
||||||
|
|
||||||
## グローバルキーバインド
|
## グローバルキーバインド
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+r</kbd>: 最近使用したリポジトリに切り替え
|
<kbd><c-r></kbd>: 最近使用したリポジトリに切り替え
|
||||||
<kbd>pgup</kbd>: メインパネルを上にスクロール (fn+up/shift+k)
|
<kbd><pgup></kbd>: メインパネルを上にスクロール (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: メインパネルを下にスクロール (fn+down/shift+j)
|
<kbd><pgdown></kbd>: メインパネルを下にスクロール (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: view merge/rebase options
|
|
||||||
<kbd>ctrl+p</kbd>: view custom patch options
|
|
||||||
<kbd>R</kbd>: リフレッシュ
|
|
||||||
<kbd>?</kbd>: メニューを開く
|
|
||||||
<kbd>+</kbd>: 次のスクリーンモード (normal/half/fullscreen)
|
|
||||||
<kbd>_</kbd>: 前のスクリーンモード
|
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
|
||||||
<kbd>W</kbd>: 差分メニューを開く
|
|
||||||
<kbd>ctrl+e</kbd>: 差分メニューを開く
|
|
||||||
<kbd>@</kbd>: コマンドログメニューを開く
|
<kbd>@</kbd>: コマンドログメニューを開く
|
||||||
<kbd>ctrl+w</kbd>: 空白文字の差分の表示有無を切り替え
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: カスタムコマンドを実行
|
<kbd>:</kbd>: カスタムコマンドを実行
|
||||||
|
<kbd><c-p></kbd>: View custom patch options
|
||||||
|
<kbd>m</kbd>: View merge/rebase options
|
||||||
|
<kbd>R</kbd>: リフレッシュ
|
||||||
|
<kbd>+</kbd>: 次のスクリーンモード (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: 前のスクリーンモード
|
||||||
|
<kbd>?</kbd>: メニューを開く
|
||||||
|
<kbd><c-s></kbd>: View filter-by-path options
|
||||||
|
<kbd>W</kbd>: 差分メニューを開く
|
||||||
|
<kbd><c-e></kbd>: 差分メニューを開く
|
||||||
|
<kbd><c-w></kbd>: 空白文字の差分の表示有無を切り替え
|
||||||
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
|
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
|
||||||
<kbd>ctrl+z</kbd>: リドゥ (via reflog) (experimental)
|
<kbd><c-z></kbd>: リドゥ (via reflog) (experimental)
|
||||||
<kbd>P</kbd>: push
|
<kbd>P</kbd>: Push
|
||||||
<kbd>p</kbd>: pull
|
<kbd>p</kbd>: Pull
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 一覧パネルの操作
|
## 一覧パネルの操作
|
||||||
@@ -33,9 +35,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<pre>
|
<pre>
|
||||||
<kbd>,</kbd>: 前のページ
|
<kbd>,</kbd>: 前のページ
|
||||||
<kbd>.</kbd>: 次のページ
|
<kbd>.</kbd>: 次のページ
|
||||||
<kbd><</kbd>: 最上部までスクロール
|
<kbd><</kbd>: 最上部までスクロール
|
||||||
|
<kbd>></kbd>: 最下部までスクロール
|
||||||
<kbd>/</kbd>: 検索を開始
|
<kbd>/</kbd>: 検索を開始
|
||||||
<kbd>></kbd>: 最下部までスクロール
|
|
||||||
<kbd>H</kbd>: 左スクロール
|
<kbd>H</kbd>: 左スクロール
|
||||||
<kbd>L</kbd>: 右スクロール
|
<kbd>L</kbd>: 右スクロール
|
||||||
<kbd>]</kbd>: 次のタブ
|
<kbd>]</kbd>: 次のタブ
|
||||||
@@ -45,117 +47,130 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## Stash
|
## Stash
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: 適用
|
<kbd><space></kbd>: 適用
|
||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: Pop
|
||||||
<kbd>d</kbd>: drop
|
<kbd>d</kbd>: Drop
|
||||||
<kbd>n</kbd>: 新しいブランチを作成
|
<kbd>n</kbd>: 新しいブランチを作成
|
||||||
<kbd>r</kbd>: Stashを変更
|
<kbd>r</kbd>: Stashを変更
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Sub-commits
|
## Sub-commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: コミットのSHAをクリップボードにコピー
|
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||||
<kbd>space</kbd>: コミットをチェックアウト
|
<kbd><space></kbd>: コミットをチェックアウト
|
||||||
<kbd>y</kbd>: コミットの情報をコピー
|
<kbd>y</kbd>: コミットの情報をコピー
|
||||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||||
<kbd>n</kbd>: コミットにブランチを作成
|
<kbd>n</kbd>: コミットにブランチを作成
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: 検索を開始
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## コミット
|
## コミット
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: コミットのSHAをクリップボードにコピー
|
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>b</kbd>: view bisect options
|
<kbd>b</kbd>: View bisect options
|
||||||
<kbd>s</kbd>: squash down
|
<kbd>s</kbd>: Squash down
|
||||||
<kbd>f</kbd>: fixup commit
|
<kbd>f</kbd>: Fixup commit
|
||||||
<kbd>r</kbd>: コミットメッセージを変更
|
<kbd>r</kbd>: コミットメッセージを変更
|
||||||
<kbd>R</kbd>: エディタでコミットメッセージを編集
|
<kbd>R</kbd>: エディタでコミットメッセージを編集
|
||||||
<kbd>d</kbd>: コミットを削除
|
<kbd>d</kbd>: コミットを削除
|
||||||
<kbd>e</kbd>: コミットを編集
|
<kbd>e</kbd>: コミットを編集
|
||||||
<kbd>p</kbd>: pick commit (when mid-rebase)
|
<kbd>p</kbd>: Pick commit (when mid-rebase)
|
||||||
<kbd>F</kbd>: このコミットに対するfixupコミットを作成
|
<kbd>F</kbd>: このコミットに対するfixupコミットを作成
|
||||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
|
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
|
||||||
<kbd>ctrl+j</kbd>: コミットを1つ下に移動
|
<kbd><c-j></kbd>: コミットを1つ下に移動
|
||||||
<kbd>ctrl+k</kbd>: コミットを1つ上に移動
|
<kbd><c-k></kbd>: コミットを1つ上に移動
|
||||||
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
|
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
|
||||||
<kbd>A</kbd>: ステージされた変更でamendコミット
|
<kbd>A</kbd>: ステージされた変更でamendコミット
|
||||||
<kbd>a</kbd>: reset commit author
|
<kbd>a</kbd>: Set/Reset commit author
|
||||||
<kbd>t</kbd>: コミットをrevert
|
<kbd>t</kbd>: コミットをrevert
|
||||||
<kbd>T</kbd>: タグを作成
|
<kbd>T</kbd>: タグを作成
|
||||||
<kbd>ctrl+l</kbd>: ログメニューを開く
|
<kbd><c-l></kbd>: ログメニューを開く
|
||||||
<kbd>space</kbd>: コミットをチェックアウト
|
<kbd><space></kbd>: コミットをチェックアウト
|
||||||
<kbd>y</kbd>: コミットの情報をコピー
|
<kbd>y</kbd>: コミットの情報をコピー
|
||||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||||
<kbd>n</kbd>: コミットにブランチを作成
|
<kbd>n</kbd>: コミットにブランチを作成
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: 検索を開始
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## コミットファイル
|
## コミットファイル
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: コミットされたファイル名をクリップボードにコピー
|
<kbd><c-o></kbd>: コミットされたファイル名をクリップボードにコピー
|
||||||
<kbd>c</kbd>: checkout file
|
<kbd>c</kbd>: Checkout file
|
||||||
<kbd>d</kbd>: discard this commit's changes to this file
|
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||||
<kbd>o</kbd>: ファイルを開く
|
<kbd>o</kbd>: ファイルを開く
|
||||||
<kbd>e</kbd>: ファイルを編集
|
<kbd>e</kbd>: ファイルを編集
|
||||||
<kbd>space</kbd>: toggle file included in patch
|
<kbd><space></kbd>: Toggle file included in patch
|
||||||
<kbd>a</kbd>: toggle all files included in patch
|
<kbd>a</kbd>: Toggle all files included in patch
|
||||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||||
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
||||||
|
<kbd>/</kbd>: 検索を開始
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## コミットメッセージ
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 確認
|
||||||
|
<kbd><esc></kbd>: 閉じる
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## サブモジュール
|
## サブモジュール
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: サブモジュール名をクリップボードにコピー
|
<kbd><c-o></kbd>: サブモジュール名をクリップボードにコピー
|
||||||
<kbd>enter</kbd>: サブモジュールを開く
|
<kbd><enter></kbd>: サブモジュールを開く
|
||||||
<kbd>d</kbd>: サブモジュールを削除
|
<kbd>d</kbd>: サブモジュールを削除
|
||||||
<kbd>u</kbd>: サブモジュールを更新
|
<kbd>u</kbd>: サブモジュールを更新
|
||||||
<kbd>n</kbd>: サブモジュールを新規追加
|
<kbd>n</kbd>: サブモジュールを新規追加
|
||||||
<kbd>e</kbd>: サブモジュールのURLを更新
|
<kbd>e</kbd>: サブモジュールのURLを更新
|
||||||
<kbd>i</kbd>: サブモジュールを初期化
|
<kbd>i</kbd>: サブモジュールを初期化
|
||||||
<kbd>b</kbd>: view bulk submodule options
|
<kbd>b</kbd>: View bulk submodule options
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## ステータス
|
## ステータス
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 設定ファイルを編集
|
|
||||||
<kbd>o</kbd>: 設定ファイルを開く
|
<kbd>o</kbd>: 設定ファイルを開く
|
||||||
|
<kbd>e</kbd>: 設定ファイルを編集
|
||||||
<kbd>u</kbd>: 更新を確認
|
<kbd>u</kbd>: 更新を確認
|
||||||
<kbd>enter</kbd>: 最近使用したリポジトリに切り替え
|
<kbd><enter></kbd>: 最近使用したリポジトリに切り替え
|
||||||
<kbd>a</kbd>: すべてのブランチログを表示
|
<kbd>a</kbd>: すべてのブランチログを表示
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## タグ
|
## タグ
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: チェックアウト
|
<kbd><space></kbd>: チェックアウト
|
||||||
<kbd>d</kbd>: タグを削除
|
<kbd>d</kbd>: タグを削除
|
||||||
<kbd>P</kbd>: タグをpush
|
<kbd>P</kbd>: タグをpush
|
||||||
<kbd>n</kbd>: タグを作成
|
<kbd>n</kbd>: タグを作成
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>enter</kbd>: コミットを閲覧
|
<kbd><enter></kbd>: コミットを閲覧
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## ファイル
|
## ファイル
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: ファイル名をクリップボードにコピー
|
<kbd><c-o></kbd>: ファイル名をクリップボードにコピー
|
||||||
<kbd>d</kbd>: view 'discard changes' options
|
<kbd>d</kbd>: View 'discard changes' options
|
||||||
<kbd>space</kbd>: ステージ/アンステージ
|
<kbd><space></kbd>: ステージ/アンステージ
|
||||||
<kbd>ctrl+b</kbd>: ファイルをフィルタ (ステージ/アンステージ)
|
<kbd><c-b></kbd>: ファイルをフィルタ (ステージ/アンステージ)
|
||||||
<kbd>c</kbd>: 変更をコミット
|
<kbd>c</kbd>: 変更をコミット
|
||||||
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
||||||
<kbd>A</kbd>: 最新のコミットにamend
|
<kbd>A</kbd>: 最新のコミットにamend
|
||||||
@@ -165,37 +180,39 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>i</kbd>: ファイルをignore
|
<kbd>i</kbd>: ファイルをignore
|
||||||
<kbd>r</kbd>: ファイルをリフレッシュ
|
<kbd>r</kbd>: ファイルをリフレッシュ
|
||||||
<kbd>s</kbd>: 変更をstash
|
<kbd>s</kbd>: 変更をstash
|
||||||
<kbd>S</kbd>: view stash options
|
<kbd>S</kbd>: View stash options
|
||||||
<kbd>a</kbd>: すべての変更をステージ/アンステージ
|
<kbd>a</kbd>: すべての変更をステージ/アンステージ
|
||||||
<kbd>enter</kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
<kbd><enter></kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
|
||||||
<kbd>g</kbd>: view upstream reset options
|
<kbd>g</kbd>: View upstream reset options
|
||||||
<kbd>D</kbd>: view reset options
|
<kbd>D</kbd>: View reset options
|
||||||
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
||||||
<kbd>M</kbd>: git mergetoolを開く
|
<kbd>M</kbd>: Git mergetoolを開く
|
||||||
<kbd>f</kbd>: fetch
|
<kbd>f</kbd>: Fetch
|
||||||
|
<kbd>/</kbd>: 検索を開始
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## ブランチ
|
## ブランチ
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: ブランチ名をクリップボードにコピー
|
<kbd><c-o></kbd>: ブランチ名をクリップボードにコピー
|
||||||
<kbd>i</kbd>: show git-flow options
|
<kbd>i</kbd>: Show git-flow options
|
||||||
<kbd>space</kbd>: チェックアウト
|
<kbd><space></kbd>: チェックアウト
|
||||||
<kbd>n</kbd>: 新しいブランチを作成
|
<kbd>n</kbd>: 新しいブランチを作成
|
||||||
<kbd>o</kbd>: Pull Requestを作成
|
<kbd>o</kbd>: Pull Requestを作成
|
||||||
<kbd>O</kbd>: create pull request options
|
<kbd>O</kbd>: Create pull request options
|
||||||
<kbd>ctrl+y</kbd>: Pull RequestのURLをクリップボードにコピー
|
<kbd><c-y></kbd>: Pull RequestのURLをクリップボードにコピー
|
||||||
<kbd>c</kbd>: checkout by name
|
<kbd>c</kbd>: Checkout by name
|
||||||
<kbd>F</kbd>: force checkout
|
<kbd>F</kbd>: Force checkout
|
||||||
<kbd>d</kbd>: ブランチを削除
|
<kbd>d</kbd>: ブランチを削除
|
||||||
<kbd>r</kbd>: rebase checked-out branch onto this branch
|
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||||
<kbd>M</kbd>: 現在のブランチにマージ
|
<kbd>M</kbd>: 現在のブランチにマージ
|
||||||
<kbd>f</kbd>: fast-forward this branch from its upstream
|
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||||
<kbd>T</kbd>: タグを作成
|
<kbd>T</kbd>: タグを作成
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>R</kbd>: ブランチ名を変更
|
<kbd>R</kbd>: ブランチ名を変更
|
||||||
<kbd>u</kbd>: set/unset upstream
|
<kbd>u</kbd>: Set/Unset upstream
|
||||||
<kbd>enter</kbd>: コミットを閲覧
|
<kbd><enter></kbd>: コミットを閲覧
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## メインパネル (Merging)
|
## メインパネル (Merging)
|
||||||
@@ -203,58 +220,68 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: ファイルを編集
|
<kbd>e</kbd>: ファイルを編集
|
||||||
<kbd>o</kbd>: ファイルを開く
|
<kbd>o</kbd>: ファイルを開く
|
||||||
<kbd>◀</kbd>: 前のコンフリクトを選択
|
<kbd><left></kbd>: 前のコンフリクトを選択
|
||||||
<kbd>▶</kbd>: 次のコンフリクトを選択
|
<kbd><right></kbd>: 次のコンフリクトを選択
|
||||||
<kbd>▲</kbd>: 前のhunkを選択
|
<kbd><up></kbd>: 前のhunkを選択
|
||||||
<kbd>▼</kbd>: 次のhunkを選択
|
<kbd><down></kbd>: 次のhunkを選択
|
||||||
<kbd>z</kbd>: アンドゥ
|
<kbd>z</kbd>: アンドゥ
|
||||||
<kbd>M</kbd>: git mergetoolを開く
|
<kbd>M</kbd>: Git mergetoolを開く
|
||||||
<kbd>space</kbd>: pick hunk
|
<kbd><space></kbd>: Pick hunk
|
||||||
<kbd>b</kbd>: pick all hunks
|
<kbd>b</kbd>: Pick all hunks
|
||||||
<kbd>esc</kbd>: ファイル一覧に戻る
|
<kbd><esc></kbd>: ファイル一覧に戻る
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## メインパネル (Normal)
|
## メインパネル (Normal)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>mouse wheel ▼</kbd>: 下にスクロール (fn+up)
|
<kbd>mouse wheel down</kbd>: 下にスクロール (fn+up)
|
||||||
<kbd>mouse wheel ▲</kbd>: 上にスクロール (fn+down)
|
<kbd>mouse wheel up</kbd>: 上にスクロール (fn+down)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## メインパネル (Patch Building)
|
## メインパネル (Patch Building)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: 前のhunkを選択
|
<kbd><left></kbd>: 前のhunkを選択
|
||||||
<kbd>▶</kbd>: 次のhunkを選択
|
<kbd><right></kbd>: 次のhunkを選択
|
||||||
<kbd>v</kbd>: 範囲選択を切り替え
|
<kbd>v</kbd>: 範囲選択を切り替え
|
||||||
<kbd>V</kbd>: 範囲選択を切り替え
|
<kbd>V</kbd>: 範囲選択を切り替え
|
||||||
<kbd>a</kbd>: hunk選択を切り替え
|
<kbd>a</kbd>: Hunk選択を切り替え
|
||||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||||
<kbd>o</kbd>: ファイルを開く
|
<kbd>o</kbd>: ファイルを開く
|
||||||
<kbd>e</kbd>: ファイルを編集
|
<kbd>e</kbd>: ファイルを編集
|
||||||
<kbd>space</kbd>: 行をパッチに追加/削除
|
<kbd><space></kbd>: 行をパッチに追加/削除
|
||||||
<kbd>esc</kbd>: exit custom patch builder
|
<kbd><esc></kbd>: Exit custom patch builder
|
||||||
|
<kbd>/</kbd>: 検索を開始
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## メインパネル (Staging)
|
## メインパネル (Staging)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: 前のhunkを選択
|
<kbd><left></kbd>: 前のhunkを選択
|
||||||
<kbd>▶</kbd>: 次のhunkを選択
|
<kbd><right></kbd>: 次のhunkを選択
|
||||||
<kbd>v</kbd>: 範囲選択を切り替え
|
<kbd>v</kbd>: 範囲選択を切り替え
|
||||||
<kbd>V</kbd>: 範囲選択を切り替え
|
<kbd>V</kbd>: 範囲選択を切り替え
|
||||||
<kbd>a</kbd>: hunk選択を切り替え
|
<kbd>a</kbd>: Hunk選択を切り替え
|
||||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||||
<kbd>o</kbd>: ファイルを開く
|
<kbd>o</kbd>: ファイルを開く
|
||||||
<kbd>e</kbd>: ファイルを編集
|
<kbd>e</kbd>: ファイルを編集
|
||||||
<kbd>esc</kbd>: ファイル一覧に戻る
|
<kbd><esc></kbd>: ファイル一覧に戻る
|
||||||
<kbd>tab</kbd>: パネルを切り替え
|
<kbd><tab></kbd>: パネルを切り替え
|
||||||
<kbd>space</kbd>: 選択行をステージ/アンステージ
|
<kbd><space></kbd>: 選択行をステージ/アンステージ
|
||||||
<kbd>d</kbd>: 変更を削除 (git reset)
|
<kbd>d</kbd>: 変更を削除 (git reset)
|
||||||
<kbd>E</kbd>: edit hunk
|
<kbd>E</kbd>: Edit hunk
|
||||||
<kbd>c</kbd>: 変更をコミット
|
<kbd>c</kbd>: 変更をコミット
|
||||||
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
||||||
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
||||||
|
<kbd>/</kbd>: 検索を開始
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## メニュー
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 実行
|
||||||
|
<kbd><esc></kbd>: 閉じる
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## リモート
|
## リモート
|
||||||
@@ -264,34 +291,43 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>n</kbd>: リモートを新規追加
|
<kbd>n</kbd>: リモートを新規追加
|
||||||
<kbd>d</kbd>: リモートを削除
|
<kbd>d</kbd>: リモートを削除
|
||||||
<kbd>e</kbd>: リモートを編集
|
<kbd>e</kbd>: リモートを編集
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## リモートブランチ
|
## リモートブランチ
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: ブランチ名をクリップボードにコピー
|
<kbd><c-o></kbd>: ブランチ名をクリップボードにコピー
|
||||||
<kbd>space</kbd>: チェックアウト
|
<kbd><space></kbd>: チェックアウト
|
||||||
<kbd>n</kbd>: 新しいブランチを作成
|
<kbd>n</kbd>: 新しいブランチを作成
|
||||||
<kbd>M</kbd>: 現在のブランチにマージ
|
<kbd>M</kbd>: 現在のブランチにマージ
|
||||||
<kbd>r</kbd>: rebase checked-out branch onto this branch
|
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||||
<kbd>d</kbd>: ブランチを削除
|
<kbd>d</kbd>: ブランチを削除
|
||||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||||
<kbd>esc</kbd>: リモート一覧に戻る
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd><enter></kbd>: コミットを閲覧
|
||||||
<kbd>enter</kbd>: コミットを閲覧
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 参照ログ
|
## 参照ログ
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: コミットのSHAをクリップボードにコピー
|
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||||
<kbd>space</kbd>: コミットをチェックアウト
|
<kbd><space></kbd>: コミットをチェックアウト
|
||||||
<kbd>y</kbd>: コミットの情報をコピー
|
<kbd>y</kbd>: コミットの情報をコピー
|
||||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||||
<kbd>n</kbd>: コミットにブランチを作成
|
<kbd>n</kbd>: コミットにブランチを作成
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: コミットを閲覧
|
<kbd><enter></kbd>: コミットを閲覧
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 確認パネル
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 確認
|
||||||
|
<kbd><esc></kbd>: 閉じる/キャンセル
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
+149
-113
@@ -2,40 +2,42 @@ _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_
|
||||||
|
|
||||||
## 글로벌 키 바인딩
|
## 글로벌 키 바인딩
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+r</kbd>: 최근에 사용한 저장소로 전환
|
<kbd><c-r></kbd>: 최근에 사용한 저장소로 전환
|
||||||
<kbd>pgup</kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
|
<kbd><pgup></kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
|
<kbd><pgdown></kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: view merge/rebase options
|
<kbd>@</kbd>: 명령어 로그 메뉴 열기
|
||||||
<kbd>ctrl+p</kbd>: 커스텀 Patch 옵션 보기
|
<kbd>}</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
|
||||||
|
<kbd>{</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
|
||||||
|
<kbd>:</kbd>: Execute custom command
|
||||||
|
<kbd><c-p></kbd>: 커스텀 Patch 옵션 보기
|
||||||
|
<kbd>m</kbd>: View merge/rebase options
|
||||||
<kbd>R</kbd>: 새로고침
|
<kbd>R</kbd>: 새로고침
|
||||||
<kbd>?</kbd>: 매뉴 열기
|
|
||||||
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
|
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
|
||||||
<kbd>_</kbd>: 이전 스크린 모드
|
<kbd>_</kbd>: 이전 스크린 모드
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
<kbd>?</kbd>: 매뉴 열기
|
||||||
|
<kbd><c-s></kbd>: View filter-by-path options
|
||||||
<kbd>W</kbd>: Diff 메뉴 열기
|
<kbd>W</kbd>: Diff 메뉴 열기
|
||||||
<kbd>ctrl+e</kbd>: Diff 메뉴 열기
|
<kbd><c-e></kbd>: Diff 메뉴 열기
|
||||||
<kbd>@</kbd>: 명령어 로그 메뉴 열기
|
<kbd><c-w></kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
|
||||||
<kbd>ctrl+w</kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
|
|
||||||
<kbd>}</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
|
|
||||||
<kbd>{</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
|
|
||||||
<kbd>:</kbd>: execute custom command
|
|
||||||
<kbd>z</kbd>: 되돌리기 (reflog) (실험적)
|
<kbd>z</kbd>: 되돌리기 (reflog) (실험적)
|
||||||
<kbd>ctrl+z</kbd>: 다시 실행 (reflog) (실험적)
|
<kbd><c-z></kbd>: 다시 실행 (reflog) (실험적)
|
||||||
<kbd>P</kbd>: 푸시
|
<kbd>P</kbd>: 푸시
|
||||||
<kbd>p</kbd>: 업데이트
|
<kbd>p</kbd>: 업데이트
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## List Panel Navigation
|
## List panel navigation
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>,</kbd>: 이전 페이지
|
<kbd>,</kbd>: 이전 페이지
|
||||||
<kbd>.</kbd>: 다음 페이지
|
<kbd>.</kbd>: 다음 페이지
|
||||||
<kbd><</kbd>: 맨 위로 스크롤
|
<kbd><</kbd>: 맨 위로 스크롤
|
||||||
|
<kbd>></kbd>: 맨 아래로 스크롤
|
||||||
<kbd>/</kbd>: 검색 시작
|
<kbd>/</kbd>: 검색 시작
|
||||||
<kbd>></kbd>: 맨 아래로 스크롤
|
|
||||||
<kbd>H</kbd>: 우 스크롤
|
<kbd>H</kbd>: 우 스크롤
|
||||||
<kbd>L</kbd>: 좌 스크롤
|
<kbd>L</kbd>: 좌 스크롤
|
||||||
<kbd>]</kbd>: 이전 탭
|
<kbd>]</kbd>: 이전 탭
|
||||||
@@ -45,42 +47,53 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## Reflog
|
## Reflog
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 커밋 SHA를 클립보드에 복사
|
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||||
<kbd>space</kbd>: 커밋을 체크아웃
|
<kbd><space></kbd>: 커밋을 체크아웃
|
||||||
<kbd>y</kbd>: 커밋 attribute 복사
|
<kbd>y</kbd>: 커밋 attribute 복사
|
||||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: 커밋 보기
|
<kbd><enter></kbd>: 커밋 보기
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Stash
|
## Stash
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: 적용
|
<kbd><space></kbd>: 적용
|
||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: Pop
|
||||||
<kbd>d</kbd>: drop
|
<kbd>d</kbd>: Drop
|
||||||
<kbd>n</kbd>: 새 브랜치 생성
|
<kbd>n</kbd>: 새 브랜치 생성
|
||||||
<kbd>r</kbd>: rename stash
|
<kbd>r</kbd>: Rename stash
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Sub-commits
|
## Sub-commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 커밋 SHA를 클립보드에 복사
|
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||||
<kbd>space</kbd>: 커밋을 체크아웃
|
<kbd><space></kbd>: 커밋을 체크아웃
|
||||||
<kbd>y</kbd>: 커밋 attribute 복사
|
<kbd>y</kbd>: 커밋 attribute 복사
|
||||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: 검색 시작
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 메뉴
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 실행
|
||||||
|
<kbd><esc></kbd>: 닫기
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 메인 패널 (Merging)
|
## 메인 패널 (Merging)
|
||||||
@@ -88,104 +101,108 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 파일 편집
|
<kbd>e</kbd>: 파일 편집
|
||||||
<kbd>o</kbd>: 파일 닫기
|
<kbd>o</kbd>: 파일 닫기
|
||||||
<kbd>◀</kbd>: 이전 충돌을 선택
|
<kbd><left></kbd>: 이전 충돌을 선택
|
||||||
<kbd>▶</kbd>: 다음 충돌을 선택
|
<kbd><right></kbd>: 다음 충돌을 선택
|
||||||
<kbd>▲</kbd>: 이전 hunk를 선택
|
<kbd><up></kbd>: 이전 hunk를 선택
|
||||||
<kbd>▼</kbd>: 다음 hunk를 선택
|
<kbd><down></kbd>: 다음 hunk를 선택
|
||||||
<kbd>z</kbd>: 되돌리기
|
<kbd>z</kbd>: 되돌리기
|
||||||
<kbd>M</kbd>: git mergetool를 열기
|
<kbd>M</kbd>: Git mergetool를 열기
|
||||||
<kbd>space</kbd>: pick hunk
|
<kbd><space></kbd>: Pick hunk
|
||||||
<kbd>b</kbd>: pick all hunks
|
<kbd>b</kbd>: Pick all hunks
|
||||||
<kbd>esc</kbd>: 파일 목록으로 돌아가기
|
<kbd><esc></kbd>: 파일 목록으로 돌아가기
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 메인 패널 (Normal)
|
## 메인 패널 (Normal)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>mouse wheel ▼</kbd>: 아래로 스크롤 (fn+up)
|
<kbd>mouse wheel down</kbd>: 아래로 스크롤 (fn+up)
|
||||||
<kbd>mouse wheel ▲</kbd>: 위로 스크롤 (fn+down)
|
<kbd>mouse wheel up</kbd>: 위로 스크롤 (fn+down)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 메인 패널 (Patch Building)
|
## 메인 패널 (Patch Building)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: 이전 hunk를 선택
|
<kbd><left></kbd>: 이전 hunk를 선택
|
||||||
<kbd>▶</kbd>: 다음 hunk를 선택
|
<kbd><right></kbd>: 다음 hunk를 선택
|
||||||
<kbd>v</kbd>: 드래그 선택 전환
|
<kbd>v</kbd>: 드래그 선택 전환
|
||||||
<kbd>V</kbd>: 드래그 선택 전환
|
<kbd>V</kbd>: 드래그 선택 전환
|
||||||
<kbd>a</kbd>: toggle select hunk
|
<kbd>a</kbd>: Toggle select hunk
|
||||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||||
<kbd>o</kbd>: 파일 닫기
|
<kbd>o</kbd>: 파일 닫기
|
||||||
<kbd>e</kbd>: 파일 편집
|
<kbd>e</kbd>: 파일 편집
|
||||||
<kbd>space</kbd>: line(s)을 패치에 추가/삭제
|
<kbd><space></kbd>: Line(s)을 패치에 추가/삭제
|
||||||
<kbd>esc</kbd>: exit custom patch builder
|
<kbd><esc></kbd>: Exit custom patch builder
|
||||||
|
<kbd>/</kbd>: 검색 시작
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 메인 패널 (Staging)
|
## 메인 패널 (Staging)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: 이전 hunk를 선택
|
<kbd><left></kbd>: 이전 hunk를 선택
|
||||||
<kbd>▶</kbd>: 다음 hunk를 선택
|
<kbd><right></kbd>: 다음 hunk를 선택
|
||||||
<kbd>v</kbd>: 드래그 선택 전환
|
<kbd>v</kbd>: 드래그 선택 전환
|
||||||
<kbd>V</kbd>: 드래그 선택 전환
|
<kbd>V</kbd>: 드래그 선택 전환
|
||||||
<kbd>a</kbd>: toggle select hunk
|
<kbd>a</kbd>: Toggle select hunk
|
||||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||||
<kbd>o</kbd>: 파일 닫기
|
<kbd>o</kbd>: 파일 닫기
|
||||||
<kbd>e</kbd>: 파일 편집
|
<kbd>e</kbd>: 파일 편집
|
||||||
<kbd>esc</kbd>: 파일 목록으로 돌아가기
|
<kbd><esc></kbd>: 파일 목록으로 돌아가기
|
||||||
<kbd>tab</kbd>: 패널 전환
|
<kbd><tab></kbd>: 패널 전환
|
||||||
<kbd>space</kbd>: 선택한 행을 staged / unstaged
|
<kbd><space></kbd>: 선택한 행을 staged / unstaged
|
||||||
<kbd>d</kbd>: 변경을 삭제 (git reset)
|
<kbd>d</kbd>: 변경을 삭제 (git reset)
|
||||||
<kbd>E</kbd>: edit hunk
|
<kbd>E</kbd>: Edit hunk
|
||||||
<kbd>c</kbd>: 커밋 변경내용
|
<kbd>c</kbd>: 커밋 변경내용
|
||||||
<kbd>w</kbd>: commit changes without pre-commit hook
|
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||||
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
|
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
|
||||||
|
<kbd>/</kbd>: 검색 시작
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 브랜치
|
## 브랜치
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 브랜치명을 클립보드에 복사
|
<kbd><c-o></kbd>: 브랜치명을 클립보드에 복사
|
||||||
<kbd>i</kbd>: git-flow 옵션 보기
|
<kbd>i</kbd>: Git-flow 옵션 보기
|
||||||
<kbd>space</kbd>: 체크아웃
|
<kbd><space></kbd>: 체크아웃
|
||||||
<kbd>n</kbd>: 새 브랜치 생성
|
<kbd>n</kbd>: 새 브랜치 생성
|
||||||
<kbd>o</kbd>: 풀 리퀘스트 생성
|
<kbd>o</kbd>: 풀 리퀘스트 생성
|
||||||
<kbd>O</kbd>: 풀 리퀘스트 생성 옵션
|
<kbd>O</kbd>: 풀 리퀘스트 생성 옵션
|
||||||
<kbd>ctrl+y</kbd>: 풀 리퀘스트 URL을 클립보드에 복사
|
<kbd><c-y></kbd>: 풀 리퀘스트 URL을 클립보드에 복사
|
||||||
<kbd>c</kbd>: 이름으로 체크아웃
|
<kbd>c</kbd>: 이름으로 체크아웃
|
||||||
<kbd>F</kbd>: 강제 체크아웃
|
<kbd>F</kbd>: 강제 체크아웃
|
||||||
<kbd>d</kbd>: 브랜치 삭제
|
<kbd>d</kbd>: 브랜치 삭제
|
||||||
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
||||||
<kbd>M</kbd>: 현재 브랜치에 병합
|
<kbd>M</kbd>: 현재 브랜치에 병합
|
||||||
<kbd>f</kbd>: fast-forward this branch from its upstream
|
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||||
<kbd>T</kbd>: 태그를 생성
|
<kbd>T</kbd>: 태그를 생성
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>R</kbd>: 브랜치 이름 변경
|
<kbd>R</kbd>: 브랜치 이름 변경
|
||||||
<kbd>u</kbd>: set/unset upstream
|
<kbd>u</kbd>: Set/Unset upstream
|
||||||
<kbd>enter</kbd>: 커밋 보기
|
<kbd><enter></kbd>: 커밋 보기
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 상태
|
## 상태
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 설정 파일 수정
|
|
||||||
<kbd>o</kbd>: 설정 파일 열기
|
<kbd>o</kbd>: 설정 파일 열기
|
||||||
|
<kbd>e</kbd>: 설정 파일 수정
|
||||||
<kbd>u</kbd>: 업데이트 확인
|
<kbd>u</kbd>: 업데이트 확인
|
||||||
<kbd>enter</kbd>: 최근에 사용한 저장소로 전환
|
<kbd><enter></kbd>: 최근에 사용한 저장소로 전환
|
||||||
<kbd>a</kbd>: 모든 브랜치 로그 표시
|
<kbd>a</kbd>: 모든 브랜치 로그 표시
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 서브모듈
|
## 서브모듈
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 서브모듈 이름을 클립보드에 복사
|
<kbd><c-o></kbd>: 서브모듈 이름을 클립보드에 복사
|
||||||
<kbd>enter</kbd>: 서브모듈 열기
|
<kbd><enter></kbd>: 서브모듈 열기
|
||||||
<kbd>d</kbd>: 서브모듈 삭제
|
<kbd>d</kbd>: 서브모듈 삭제
|
||||||
<kbd>u</kbd>: 서브모듈 업데이트
|
<kbd>u</kbd>: 서브모듈 업데이트
|
||||||
<kbd>n</kbd>: 새로운 서브모듈 추가
|
<kbd>n</kbd>: 새로운 서브모듈 추가
|
||||||
<kbd>e</kbd>: 서브모듈의 URL을 수정
|
<kbd>e</kbd>: 서브모듈의 URL을 수정
|
||||||
<kbd>i</kbd>: 서브모듈 초기화
|
<kbd>i</kbd>: 서브모듈 초기화
|
||||||
<kbd>b</kbd>: view bulk submodule options
|
<kbd>b</kbd>: View bulk submodule options
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 원격
|
## 원격
|
||||||
@@ -195,103 +212,122 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>n</kbd>: 새로운 Remote 추가
|
<kbd>n</kbd>: 새로운 Remote 추가
|
||||||
<kbd>d</kbd>: Remote를 삭제
|
<kbd>d</kbd>: Remote를 삭제
|
||||||
<kbd>e</kbd>: Remote를 수정
|
<kbd>e</kbd>: Remote를 수정
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 원격 브랜치
|
## 원격 브랜치
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 브랜치명을 클립보드에 복사
|
<kbd><c-o></kbd>: 브랜치명을 클립보드에 복사
|
||||||
<kbd>space</kbd>: 체크아웃
|
<kbd><space></kbd>: 체크아웃
|
||||||
<kbd>n</kbd>: 새 브랜치 생성
|
<kbd>n</kbd>: 새 브랜치 생성
|
||||||
<kbd>M</kbd>: 현재 브랜치에 병합
|
<kbd>M</kbd>: 현재 브랜치에 병합
|
||||||
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
||||||
<kbd>d</kbd>: 브랜치 삭제
|
<kbd>d</kbd>: 브랜치 삭제
|
||||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||||
<kbd>esc</kbd>: 원격목록으로 돌아가기
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd><enter></kbd>: 커밋 보기
|
||||||
<kbd>enter</kbd>: 커밋 보기
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 커밋
|
## 커밋
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 커밋 SHA를 클립보드에 복사
|
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>b</kbd>: bisect 옵션 보기
|
<kbd>b</kbd>: Bisect 옵션 보기
|
||||||
<kbd>s</kbd>: squash down
|
<kbd>s</kbd>: Squash down
|
||||||
<kbd>f</kbd>: fixup commit
|
<kbd>f</kbd>: Fixup commit
|
||||||
<kbd>r</kbd>: 커밋메시지 변경
|
<kbd>r</kbd>: 커밋메시지 변경
|
||||||
<kbd>R</kbd>: 에디터에서 커밋메시지 수정
|
<kbd>R</kbd>: 에디터에서 커밋메시지 수정
|
||||||
<kbd>d</kbd>: 커밋 삭제
|
<kbd>d</kbd>: 커밋 삭제
|
||||||
<kbd>e</kbd>: 커밋을 편집
|
<kbd>e</kbd>: 커밋을 편집
|
||||||
<kbd>p</kbd>: pick commit (when mid-rebase)
|
<kbd>p</kbd>: Pick commit (when mid-rebase)
|
||||||
<kbd>F</kbd>: create fixup commit for this commit
|
<kbd>F</kbd>: Create fixup commit for this commit
|
||||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
|
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
|
||||||
<kbd>ctrl+j</kbd>: 커밋을 1개 아래로 이동
|
<kbd><c-j></kbd>: 커밋을 1개 아래로 이동
|
||||||
<kbd>ctrl+k</kbd>: 커밋을 1개 위로 이동
|
<kbd><c-k></kbd>: 커밋을 1개 위로 이동
|
||||||
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
|
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
|
||||||
<kbd>A</kbd>: amend commit with staged changes
|
<kbd>A</kbd>: Amend commit with staged changes
|
||||||
<kbd>a</kbd>: reset commit author
|
<kbd>a</kbd>: Set/Reset commit author
|
||||||
<kbd>t</kbd>: 커밋 되돌리기
|
<kbd>t</kbd>: 커밋 되돌리기
|
||||||
<kbd>T</kbd>: tag commit
|
<kbd>T</kbd>: Tag commit
|
||||||
<kbd>ctrl+l</kbd>: 로그 메뉴 열기
|
<kbd><c-l></kbd>: 로그 메뉴 열기
|
||||||
<kbd>space</kbd>: 커밋을 체크아웃
|
<kbd><space></kbd>: 커밋을 체크아웃
|
||||||
<kbd>y</kbd>: 커밋 attribute 복사
|
<kbd>y</kbd>: 커밋 attribute 복사
|
||||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd><enter></kbd>: View selected item's files
|
||||||
|
<kbd>/</kbd>: 검색 시작
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 커밋 파일
|
## 커밋 파일
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 커밋한 파일명을 클립보드에 복사
|
<kbd><c-o></kbd>: 커밋한 파일명을 클립보드에 복사
|
||||||
<kbd>c</kbd>: checkout file
|
<kbd>c</kbd>: Checkout file
|
||||||
<kbd>d</kbd>: discard this commit's changes to this file
|
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||||
<kbd>o</kbd>: 파일 닫기
|
<kbd>o</kbd>: 파일 닫기
|
||||||
<kbd>e</kbd>: 파일 편집
|
<kbd>e</kbd>: 파일 편집
|
||||||
<kbd>space</kbd>: toggle file included in patch
|
<kbd><space></kbd>: Toggle file included in patch
|
||||||
<kbd>a</kbd>: toggle all files included in patch
|
<kbd>a</kbd>: Toggle all files included in patch
|
||||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||||
<kbd>`</kbd>: 파일 트리뷰로 전환
|
<kbd>`</kbd>: 파일 트리뷰로 전환
|
||||||
|
<kbd>/</kbd>: 검색 시작
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 커밋메시지
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 확인
|
||||||
|
<kbd><esc></kbd>: 닫기
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 태그
|
## 태그
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: 체크아웃
|
<kbd><space></kbd>: 체크아웃
|
||||||
<kbd>d</kbd>: 태그 삭제
|
<kbd>d</kbd>: 태그 삭제
|
||||||
<kbd>P</kbd>: 태그를 push
|
<kbd>P</kbd>: 태그를 push
|
||||||
<kbd>n</kbd>: 태그를 생성
|
<kbd>n</kbd>: 태그를 생성
|
||||||
<kbd>g</kbd>: view reset options
|
<kbd>g</kbd>: View reset options
|
||||||
<kbd>enter</kbd>: 커밋 보기
|
<kbd><enter></kbd>: 커밋 보기
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 파일
|
## 파일
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 파일명을 클립보드에 복사
|
<kbd><c-o></kbd>: 파일명을 클립보드에 복사
|
||||||
<kbd>d</kbd>: view 'discard changes' options
|
<kbd>d</kbd>: View 'discard changes' options
|
||||||
<kbd>space</kbd>: Staged 전환
|
<kbd><space></kbd>: Staged 전환
|
||||||
<kbd>ctrl+b</kbd>: 파일을 필터하기 (Staged/unstaged)
|
<kbd><c-b></kbd>: 파일을 필터하기 (Staged/unstaged)
|
||||||
<kbd>c</kbd>: 커밋 변경내용
|
<kbd>c</kbd>: 커밋 변경내용
|
||||||
<kbd>w</kbd>: commit changes without pre-commit hook
|
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||||
<kbd>A</kbd>: 마지맛 커밋 수정
|
<kbd>A</kbd>: 마지맛 커밋 수정
|
||||||
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
|
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
|
||||||
<kbd>e</kbd>: 파일 편집
|
<kbd>e</kbd>: 파일 편집
|
||||||
<kbd>o</kbd>: 파일 닫기
|
<kbd>o</kbd>: 파일 닫기
|
||||||
<kbd>i</kbd>: ignore file
|
<kbd>i</kbd>: Ignore file
|
||||||
<kbd>r</kbd>: 파일 새로고침
|
<kbd>r</kbd>: 파일 새로고침
|
||||||
<kbd>s</kbd>: 변경사항을 Stash
|
<kbd>s</kbd>: 변경사항을 Stash
|
||||||
<kbd>S</kbd>: Stash 옵션 보기
|
<kbd>S</kbd>: Stash 옵션 보기
|
||||||
<kbd>a</kbd>: 모든 변경을 Staged/unstaged으로 전환
|
<kbd>a</kbd>: 모든 변경을 Staged/unstaged으로 전환
|
||||||
<kbd>enter</kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
<kbd><enter></kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
|
||||||
<kbd>g</kbd>: view upstream reset options
|
<kbd>g</kbd>: View upstream reset options
|
||||||
<kbd>D</kbd>: view reset options
|
<kbd>D</kbd>: View reset options
|
||||||
<kbd>`</kbd>: 파일 트리뷰로 전환
|
<kbd>`</kbd>: 파일 트리뷰로 전환
|
||||||
<kbd>M</kbd>: git mergetool를 열기
|
<kbd>M</kbd>: Git mergetool를 열기
|
||||||
<kbd>f</kbd>: fetch
|
<kbd>f</kbd>: Fetch
|
||||||
|
<kbd>/</kbd>: 검색 시작
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 확인 패널
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 확인
|
||||||
|
<kbd><esc></kbd>: 닫기/취소
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
+241
-205
@@ -2,296 +2,332 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
|
|
||||||
# Lazygit Sneltoetsen
|
# Lazygit Sneltoetsen
|
||||||
|
|
||||||
## Globale Sneltoetsen
|
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||||
|
|
||||||
|
## Globale sneltoetsen
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+r</kbd>: wissel naar een recente repo
|
<kbd><c-r></kbd>: Wissel naar een recente repo
|
||||||
<kbd>pgup</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
|
<kbd><pgup></kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
|
<kbd><pgdown></kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: bekijk merge/rebase opties
|
<kbd>@</kbd>: Open command log menu
|
||||||
<kbd>ctrl+p</kbd>: bekijk aangepaste patch opties
|
|
||||||
<kbd>R</kbd>: verversen
|
|
||||||
<kbd>?</kbd>: open menu
|
|
||||||
<kbd>+</kbd>: volgende scherm modus (normaal/half/groot)
|
|
||||||
<kbd>_</kbd>: vorige scherm modus
|
|
||||||
<kbd>ctrl+s</kbd>: bekijk scoping opties
|
|
||||||
<kbd>W</kbd>: open diff menu
|
|
||||||
<kbd>ctrl+e</kbd>: open diff menu
|
|
||||||
<kbd>@</kbd>: open command log menu
|
|
||||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: voer aangepaste commando uit
|
<kbd>:</kbd>: Voer aangepaste commando uit
|
||||||
<kbd>z</kbd>: ongedaan maken (via reflog) (experimenteel)
|
<kbd><c-p></kbd>: Bekijk aangepaste patch opties
|
||||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimenteel)
|
<kbd>m</kbd>: Bekijk merge/rebase opties
|
||||||
<kbd>P</kbd>: push
|
<kbd>R</kbd>: Verversen
|
||||||
<kbd>p</kbd>: pull
|
<kbd>+</kbd>: Volgende scherm modus (normaal/half/groot)
|
||||||
|
<kbd>_</kbd>: Vorige scherm modus
|
||||||
|
<kbd>?</kbd>: Open menu
|
||||||
|
<kbd><c-s></kbd>: Bekijk scoping opties
|
||||||
|
<kbd>W</kbd>: Open diff menu
|
||||||
|
<kbd><c-e></kbd>: Open diff menu
|
||||||
|
<kbd><c-w></kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||||
|
<kbd>z</kbd>: Ongedaan maken (via reflog) (experimenteel)
|
||||||
|
<kbd><c-z></kbd>: Redo (via reflog) (experimenteel)
|
||||||
|
<kbd>P</kbd>: Push
|
||||||
|
<kbd>p</kbd>: Pull
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Lijstpaneel Navigatie
|
## Lijstpaneel navigatie
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>,</kbd>: vorige pagina
|
<kbd>,</kbd>: Vorige pagina
|
||||||
<kbd>.</kbd>: volgende pagina
|
<kbd>.</kbd>: Volgende pagina
|
||||||
<kbd><</kbd>: scroll naar boven
|
<kbd><</kbd>: Scroll naar boven
|
||||||
<kbd>/</kbd>: start met zoeken
|
<kbd>></kbd>: Scroll naar beneden
|
||||||
<kbd>></kbd>: scroll naar beneden
|
<kbd>/</kbd>: Start met zoeken
|
||||||
<kbd>H</kbd>: scroll left
|
<kbd>H</kbd>: Scroll left
|
||||||
<kbd>L</kbd>: scroll right
|
<kbd>L</kbd>: Scroll right
|
||||||
<kbd>]</kbd>: volgende tabblad
|
<kbd>]</kbd>: Volgende tabblad
|
||||||
<kbd>[</kbd>: vorige tabblad
|
<kbd>[</kbd>: Vorige tabblad
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Bestanden
|
## Bestanden
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer de bestandsnaam naar het klembord
|
<kbd><c-o></kbd>: Kopieer de bestandsnaam naar het klembord
|
||||||
<kbd>d</kbd>: bekijk 'veranderingen ongedaan maken' opties
|
<kbd>d</kbd>: Bekijk 'veranderingen ongedaan maken' opties
|
||||||
<kbd>space</kbd>: toggle staged
|
<kbd><space></kbd>: Toggle staged
|
||||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
<kbd><c-b></kbd>: Filter files by status
|
||||||
<kbd>c</kbd>: commit veranderingen
|
<kbd>c</kbd>: Commit veranderingen
|
||||||
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
|
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
|
||||||
<kbd>A</kbd>: wijzig laatste commit
|
<kbd>A</kbd>: Wijzig laatste commit
|
||||||
<kbd>C</kbd>: commit veranderingen met de git editor
|
<kbd>C</kbd>: Commit veranderingen met de git editor
|
||||||
<kbd>e</kbd>: verander bestand
|
<kbd>e</kbd>: Verander bestand
|
||||||
<kbd>o</kbd>: open bestand
|
<kbd>o</kbd>: Open bestand
|
||||||
<kbd>i</kbd>: ignore or exclude file
|
<kbd>i</kbd>: Ignore or exclude file
|
||||||
<kbd>r</kbd>: refresh bestanden
|
<kbd>r</kbd>: Refresh bestanden
|
||||||
<kbd>s</kbd>: stash-bestanden
|
<kbd>s</kbd>: Stash-bestanden
|
||||||
<kbd>S</kbd>: bekijk stash opties
|
<kbd>S</kbd>: Bekijk stash opties
|
||||||
<kbd>a</kbd>: toggle staged alle
|
<kbd>a</kbd>: Toggle staged alle
|
||||||
<kbd>enter</kbd>: stage individuele hunks/lijnen
|
<kbd><enter></kbd>: Stage individuele hunks/lijnen
|
||||||
<kbd>g</kbd>: bekijk upstream reset opties
|
<kbd>g</kbd>: Bekijk upstream reset opties
|
||||||
<kbd>D</kbd>: bekijk reset opties
|
<kbd>D</kbd>: Bekijk reset opties
|
||||||
<kbd>`</kbd>: toggle bestandsboom weergave
|
<kbd>`</kbd>: Toggle bestandsboom weergave
|
||||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||||
<kbd>f</kbd>: fetch
|
<kbd>f</kbd>: Fetch
|
||||||
|
<kbd>/</kbd>: Start met zoeken
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Bevestigingspaneel
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Bevestig
|
||||||
|
<kbd><esc></kbd>: Sluiten
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Branches
|
## Branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer branch name naar klembord
|
<kbd><c-o></kbd>: Kopieer branch name naar klembord
|
||||||
<kbd>i</kbd>: laat git-flow opties zien
|
<kbd>i</kbd>: Laat git-flow opties zien
|
||||||
<kbd>space</kbd>: uitchecken
|
<kbd><space></kbd>: Uitchecken
|
||||||
<kbd>n</kbd>: nieuwe branch
|
<kbd>n</kbd>: Nieuwe branch
|
||||||
<kbd>o</kbd>: maak een pull-request
|
<kbd>o</kbd>: Maak een pull-request
|
||||||
<kbd>O</kbd>: bekijk opties voor pull-aanvraag
|
<kbd>O</kbd>: Bekijk opties voor pull-aanvraag
|
||||||
<kbd>ctrl+y</kbd>: kopieer de URL van het pull-verzoek naar het klembord
|
<kbd><c-y></kbd>: Kopieer de URL van het pull-verzoek naar het klembord
|
||||||
<kbd>c</kbd>: uitchecken bij naam
|
<kbd>c</kbd>: Uitchecken bij naam
|
||||||
<kbd>F</kbd>: forceer checkout
|
<kbd>F</kbd>: Forceer checkout
|
||||||
<kbd>d</kbd>: verwijder branch
|
<kbd>d</kbd>: Verwijder branch
|
||||||
<kbd>r</kbd>: rebase branch
|
<kbd>r</kbd>: Rebase branch
|
||||||
<kbd>M</kbd>: merge in met huidige checked out branch
|
<kbd>M</kbd>: Merge in met huidige checked out branch
|
||||||
<kbd>f</kbd>: fast-forward deze branch vanaf zijn upstream
|
<kbd>f</kbd>: Fast-forward deze branch vanaf zijn upstream
|
||||||
<kbd>T</kbd>: creëer tag
|
<kbd>T</kbd>: Creëer tag
|
||||||
<kbd>g</kbd>: bekijk reset opties
|
<kbd>g</kbd>: Bekijk reset opties
|
||||||
<kbd>R</kbd>: hernoem branch
|
<kbd>R</kbd>: Hernoem branch
|
||||||
<kbd>u</kbd>: set/unset upstream
|
<kbd>u</kbd>: Set/Unset upstream
|
||||||
<kbd>enter</kbd>: bekijk commits
|
<kbd><enter></kbd>: Bekijk commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Commit bericht
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Bevestig
|
||||||
|
<kbd><esc></kbd>: Sluiten
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Commit bestanden
|
## Commit bestanden
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer de vastgelegde bestandsnaam naar het klembord
|
<kbd><c-o></kbd>: Kopieer de vastgelegde bestandsnaam naar het klembord
|
||||||
<kbd>c</kbd>: bestand uitchecken
|
<kbd>c</kbd>: Bestand uitchecken
|
||||||
<kbd>d</kbd>: uitsluit deze commit zijn veranderingen aan dit bestand
|
<kbd>d</kbd>: Uitsluit deze commit zijn veranderingen aan dit bestand
|
||||||
<kbd>o</kbd>: open bestand
|
<kbd>o</kbd>: Open bestand
|
||||||
<kbd>e</kbd>: verander bestand
|
<kbd>e</kbd>: Verander bestand
|
||||||
<kbd>space</kbd>: toggle bestand inbegrepen in patch
|
<kbd><space></kbd>: Toggle bestand inbegrepen in patch
|
||||||
<kbd>a</kbd>: toggle all files included in patch
|
<kbd>a</kbd>: Toggle all files included in patch
|
||||||
<kbd>enter</kbd>: enter bestand om geselecteerde regels toe te voegen aan de patch
|
<kbd><enter></kbd>: Enter bestand om geselecteerde regels toe te voegen aan de patch
|
||||||
<kbd>`</kbd>: toggle bestandsboom weergave
|
<kbd>`</kbd>: Toggle bestandsboom weergave
|
||||||
|
<kbd>/</kbd>: Start met zoeken
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Commits
|
## Commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
<kbd><c-o></kbd>: Kopieer commit SHA naar klembord
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
<kbd><c-r></kbd>: Reset cherry-picked (gekopieerde) commits selectie
|
||||||
<kbd>b</kbd>: view bisect options
|
<kbd>b</kbd>: View bisect options
|
||||||
<kbd>s</kbd>: squash beneden
|
<kbd>s</kbd>: Squash beneden
|
||||||
<kbd>f</kbd>: Fixup commit
|
<kbd>f</kbd>: Fixup commit
|
||||||
<kbd>r</kbd>: hernoem commit
|
<kbd>r</kbd>: Hernoem commit
|
||||||
<kbd>R</kbd>: hernoem commit met editor
|
<kbd>R</kbd>: Hernoem commit met editor
|
||||||
<kbd>d</kbd>: verwijder commit
|
<kbd>d</kbd>: Verwijder commit
|
||||||
<kbd>e</kbd>: wijzig commit
|
<kbd>e</kbd>: Wijzig commit
|
||||||
<kbd>p</kbd>: kies commit (wanneer midden in rebase)
|
<kbd>p</kbd>: Kies commit (wanneer midden in rebase)
|
||||||
<kbd>F</kbd>: creëer fixup commit voor deze commit
|
<kbd>F</kbd>: Creëer fixup commit
|
||||||
<kbd>S</kbd>: squash bovenstaande commits
|
<kbd>S</kbd>: Squash bovenstaande commits
|
||||||
<kbd>ctrl+j</kbd>: verplaats commit 1 naar beneden
|
<kbd><c-j></kbd>: Verplaats commit 1 naar beneden
|
||||||
<kbd>ctrl+k</kbd>: verplaats commit 1 naar boven
|
<kbd><c-k></kbd>: Verplaats commit 1 naar boven
|
||||||
<kbd>v</kbd>: plak commits (cherry-pick)
|
<kbd>v</kbd>: Plak commits (cherry-pick)
|
||||||
<kbd>A</kbd>: wijzig commit met staged veranderingen
|
<kbd>A</kbd>: Wijzig commit met staged veranderingen
|
||||||
<kbd>a</kbd>: reset commit author
|
<kbd>a</kbd>: Set/Reset commit author
|
||||||
<kbd>t</kbd>: commit ongedaan maken
|
<kbd>t</kbd>: Commit ongedaan maken
|
||||||
<kbd>T</kbd>: tag commit
|
<kbd>T</kbd>: Tag commit
|
||||||
<kbd>ctrl+l</kbd>: open log menu
|
<kbd><c-l></kbd>: Open log menu
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: creëer nieuwe branch van commit
|
<kbd>n</kbd>: Creëer nieuwe branch van commit
|
||||||
<kbd>g</kbd>: bekijk reset opties
|
<kbd>g</kbd>: Bekijk reset opties
|
||||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||||
|
<kbd>/</kbd>: Start met zoeken
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Uitvoeren
|
||||||
|
<kbd><esc></kbd>: Sluiten
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Mergen
|
## Mergen
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: verander bestand
|
<kbd>e</kbd>: Verander bestand
|
||||||
<kbd>o</kbd>: open bestand
|
<kbd>o</kbd>: Open bestand
|
||||||
<kbd>◀</kbd>: selecteer voorgaand conflict
|
<kbd><left></kbd>: Selecteer voorgaand conflict
|
||||||
<kbd>▶</kbd>: selecteer volgende conflict
|
<kbd><right></kbd>: Selecteer volgende conflict
|
||||||
<kbd>▲</kbd>: selecteer bovenste hunk
|
<kbd><up></kbd>: Selecteer bovenste hunk
|
||||||
<kbd>▼</kbd>: selecteer onderste hunk
|
<kbd><down></kbd>: Selecteer onderste hunk
|
||||||
<kbd>z</kbd>: ongedaan maken
|
<kbd>z</kbd>: Ongedaan maken
|
||||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||||
<kbd>space</kbd>: kies hunk
|
<kbd><space></kbd>: Kies stuk
|
||||||
<kbd>b</kbd>: kies bijde hunks
|
<kbd>b</kbd>: Kies beide stukken
|
||||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
<kbd><esc></kbd>: Ga terug naar het bestanden paneel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Normaal
|
## Normaal
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>mouse wheel ▼</kbd>: scroll omlaag (fn+up)
|
<kbd>mouse wheel down</kbd>: Scroll omlaag (fn+up)
|
||||||
<kbd>mouse wheel ▲</kbd>: scroll omhoog (fn+down)
|
<kbd>mouse wheel up</kbd>: Scroll omhoog (fn+down)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Patch Bouwen
|
## Patch bouwen
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: selecteer de vorige hunk
|
<kbd><left></kbd>: Selecteer de vorige hunk
|
||||||
<kbd>▶</kbd>: selecteer de volgende hunk
|
<kbd><right></kbd>: Selecteer de volgende hunk
|
||||||
<kbd>v</kbd>: toggle drag selecteer
|
<kbd>v</kbd>: Toggle drag selecteer
|
||||||
<kbd>V</kbd>: toggle drag selecteer
|
<kbd>V</kbd>: Toggle drag selecteer
|
||||||
<kbd>a</kbd>: toggle selecteer hunk
|
<kbd>a</kbd>: Toggle selecteer hunk
|
||||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||||
<kbd>o</kbd>: open bestand
|
<kbd>o</kbd>: Open bestand
|
||||||
<kbd>e</kbd>: verander bestand
|
<kbd>e</kbd>: Verander bestand
|
||||||
<kbd>space</kbd>: voeg toe/verwijder lijn(en) in patch
|
<kbd><space></kbd>: Voeg toe/verwijder lijn(en) in patch
|
||||||
<kbd>esc</kbd>: sluit lijn-bij-lijn modus
|
<kbd><esc></kbd>: Sluit lijn-bij-lijn modus
|
||||||
|
<kbd>/</kbd>: Start met zoeken
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Reflog
|
## Reflog
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
<kbd><c-o></kbd>: Kopieer commit SHA naar klembord
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: creëer nieuwe branch van commit
|
<kbd>n</kbd>: Creëer nieuwe branch van commit
|
||||||
<kbd>g</kbd>: bekijk reset opties
|
<kbd>g</kbd>: Bekijk reset opties
|
||||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
<kbd><c-r></kbd>: Reset cherry-picked (gekopieerde) commits selectie
|
||||||
<kbd>enter</kbd>: bekijk commits
|
<kbd><enter></kbd>: Bekijk commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Remote Branches
|
## Remote branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer branch name naar klembord
|
<kbd><c-o></kbd>: Kopieer branch name naar klembord
|
||||||
<kbd>space</kbd>: uitchecken
|
<kbd><space></kbd>: Uitchecken
|
||||||
<kbd>n</kbd>: nieuwe branch
|
<kbd>n</kbd>: Nieuwe branch
|
||||||
<kbd>M</kbd>: merge in met huidige checked out branch
|
<kbd>M</kbd>: Merge in met huidige checked out branch
|
||||||
<kbd>r</kbd>: rebase branch
|
<kbd>r</kbd>: Rebase branch
|
||||||
<kbd>d</kbd>: verwijder branch
|
<kbd>d</kbd>: Verwijder branch
|
||||||
<kbd>u</kbd>: stel in als upstream van uitgecheckte branch
|
<kbd>u</kbd>: Stel in als upstream van uitgecheckte branch
|
||||||
<kbd>esc</kbd>: ga terug naar remotes lijst
|
<kbd>g</kbd>: Bekijk reset opties
|
||||||
<kbd>g</kbd>: bekijk reset opties
|
<kbd><enter></kbd>: Bekijk commits
|
||||||
<kbd>enter</kbd>: bekijk commits
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Remotes
|
## Remotes
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>f</kbd>: fetch remote
|
<kbd>f</kbd>: Fetch remote
|
||||||
<kbd>n</kbd>: voeg een nieuwe remote toe
|
<kbd>n</kbd>: Voeg een nieuwe remote toe
|
||||||
<kbd>d</kbd>: verwijder remote
|
<kbd>d</kbd>: Verwijder remote
|
||||||
<kbd>e</kbd>: wijzig remote
|
<kbd>e</kbd>: Wijzig remote
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Staging
|
## Staging
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: selecteer de vorige hunk
|
<kbd><left></kbd>: Selecteer de vorige hunk
|
||||||
<kbd>▶</kbd>: selecteer de volgende hunk
|
<kbd><right></kbd>: Selecteer de volgende hunk
|
||||||
<kbd>v</kbd>: toggle drag selecteer
|
<kbd>v</kbd>: Toggle drag selecteer
|
||||||
<kbd>V</kbd>: toggle drag selecteer
|
<kbd>V</kbd>: Toggle drag selecteer
|
||||||
<kbd>a</kbd>: toggle selecteer hunk
|
<kbd>a</kbd>: Toggle selecteer hunk
|
||||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||||
<kbd>o</kbd>: open bestand
|
<kbd>o</kbd>: Open bestand
|
||||||
<kbd>e</kbd>: verander bestand
|
<kbd>e</kbd>: Verander bestand
|
||||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
<kbd><esc></kbd>: Ga terug naar het bestanden paneel
|
||||||
<kbd>tab</kbd>: ga naar een ander paneel
|
<kbd><tab></kbd>: Ga naar een ander paneel
|
||||||
<kbd>space</kbd>: toggle lijnen staged / unstaged
|
<kbd><space></kbd>: Toggle lijnen staged / unstaged
|
||||||
<kbd>d</kbd>: verwijdert change (git reset)
|
<kbd>d</kbd>: Verwijdert change (git reset)
|
||||||
<kbd>E</kbd>: edit hunk
|
<kbd>E</kbd>: Edit hunk
|
||||||
<kbd>c</kbd>: commit veranderingen
|
<kbd>c</kbd>: Commit veranderingen
|
||||||
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
|
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
|
||||||
<kbd>C</kbd>: commit veranderingen met de git editor
|
<kbd>C</kbd>: Commit veranderingen met de git editor
|
||||||
|
<kbd>/</kbd>: Start met zoeken
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Stash
|
## Stash
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: toepassen
|
<kbd><space></kbd>: Toepassen
|
||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: Pop
|
||||||
<kbd>d</kbd>: laten vallen
|
<kbd>d</kbd>: Laten vallen
|
||||||
<kbd>n</kbd>: nieuwe branch
|
<kbd>n</kbd>: Nieuwe branch
|
||||||
<kbd>r</kbd>: rename stash
|
<kbd>r</kbd>: Rename stash
|
||||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: verander config bestand
|
<kbd>o</kbd>: Open config bestand
|
||||||
<kbd>o</kbd>: open config bestand
|
<kbd>e</kbd>: Verander config bestand
|
||||||
<kbd>u</kbd>: check voor updates
|
<kbd>u</kbd>: Check voor updates
|
||||||
<kbd>enter</kbd>: wissel naar een recente repo
|
<kbd><enter></kbd>: Wissel naar een recente repo
|
||||||
<kbd>a</kbd>: alle logs van de branch laten zien
|
<kbd>a</kbd>: Alle logs van de branch laten zien
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Sub-commits
|
## Sub-commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
<kbd><c-o></kbd>: Kopieer commit SHA naar klembord
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: creëer nieuwe branch van commit
|
<kbd>n</kbd>: Creëer nieuwe branch van commit
|
||||||
<kbd>g</kbd>: bekijk reset opties
|
<kbd>g</kbd>: Bekijk reset opties
|
||||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
<kbd><c-r></kbd>: Reset cherry-picked (gekopieerde) commits selectie
|
||||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||||
|
<kbd>/</kbd>: Start met zoeken
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Submodules
|
## Submodules
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: kopieer submodule naam naar klembord
|
<kbd><c-o></kbd>: Kopieer submodule naam naar klembord
|
||||||
<kbd>enter</kbd>: enter submodule
|
<kbd><enter></kbd>: Enter submodule
|
||||||
<kbd>d</kbd>: remove submodule
|
<kbd>d</kbd>: Remove submodule
|
||||||
<kbd>u</kbd>: update submodule
|
<kbd>u</kbd>: Update submodule
|
||||||
<kbd>n</kbd>: voeg nieuwe submodule toe
|
<kbd>n</kbd>: Voeg nieuwe submodule toe
|
||||||
<kbd>e</kbd>: update submodule URL
|
<kbd>e</kbd>: Update submodule URL
|
||||||
<kbd>i</kbd>: initialiseer submodule
|
<kbd>i</kbd>: Initialiseer submodule
|
||||||
<kbd>b</kbd>: bekijk bulk submodule opties
|
<kbd>b</kbd>: Bekijk bulk submodule opties
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Tags
|
## Tags
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: uitchecken
|
<kbd><space></kbd>: Uitchecken
|
||||||
<kbd>d</kbd>: verwijder tag
|
<kbd>d</kbd>: Verwijder tag
|
||||||
<kbd>P</kbd>: push tag
|
<kbd>P</kbd>: Push tag
|
||||||
<kbd>n</kbd>: creëer tag
|
<kbd>n</kbd>: Creëer tag
|
||||||
<kbd>g</kbd>: bekijk reset opties
|
<kbd>g</kbd>: Bekijk reset opties
|
||||||
<kbd>enter</kbd>: bekijk commits
|
<kbd><enter></kbd>: Bekijk commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
+237
-201
@@ -2,296 +2,332 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
|
|
||||||
# Lazygit Keybindings
|
# Lazygit Keybindings
|
||||||
|
|
||||||
|
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||||
|
|
||||||
## Globalne
|
## Globalne
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+r</kbd>: switch to a recent repo
|
<kbd><c-r></kbd>: Switch to a recent repo
|
||||||
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
<kbd><pgup></kbd>: Scroll up main panel (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
<kbd><pgdown></kbd>: Scroll down main panel (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: widok scalenia/opcje zmiany bazy
|
<kbd>@</kbd>: Open command log menu
|
||||||
<kbd>ctrl+p</kbd>: view custom patch options
|
|
||||||
<kbd>R</kbd>: odśwież
|
|
||||||
<kbd>?</kbd>: open menu
|
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
|
||||||
<kbd>_</kbd>: prev screen mode
|
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
|
||||||
<kbd>W</kbd>: open diff menu
|
|
||||||
<kbd>ctrl+e</kbd>: open diff menu
|
|
||||||
<kbd>@</kbd>: open command log menu
|
|
||||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: wykonaj własną komendę
|
<kbd>:</kbd>: Wykonaj własną komendę
|
||||||
<kbd>z</kbd>: undo (via reflog) (experimental)
|
<kbd><c-p></kbd>: View custom patch options
|
||||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
<kbd>m</kbd>: Widok scalenia/opcje zmiany bazy
|
||||||
<kbd>P</kbd>: push
|
<kbd>R</kbd>: Odśwież
|
||||||
<kbd>p</kbd>: pull
|
<kbd>+</kbd>: Next screen mode (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: Prev screen mode
|
||||||
|
<kbd>?</kbd>: Open menu
|
||||||
|
<kbd><c-s></kbd>: View filter-by-path options
|
||||||
|
<kbd>W</kbd>: Open diff menu
|
||||||
|
<kbd><c-e></kbd>: Open diff menu
|
||||||
|
<kbd><c-w></kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||||
|
<kbd>z</kbd>: Undo
|
||||||
|
<kbd><c-z></kbd>: Redo
|
||||||
|
<kbd>P</kbd>: Push
|
||||||
|
<kbd>p</kbd>: Pull
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## List Panel Navigation
|
## List panel navigation
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>,</kbd>: previous page
|
<kbd>,</kbd>: Previous page
|
||||||
<kbd>.</kbd>: next page
|
<kbd>.</kbd>: Next page
|
||||||
<kbd><</kbd>: scroll to top
|
<kbd><</kbd>: Scroll to top
|
||||||
<kbd>/</kbd>: start search
|
<kbd>></kbd>: Scroll to bottom
|
||||||
<kbd>></kbd>: scroll to bottom
|
<kbd>/</kbd>: Search the current view by text
|
||||||
<kbd>H</kbd>: scroll left
|
<kbd>H</kbd>: Scroll left
|
||||||
<kbd>L</kbd>: scroll right
|
<kbd>L</kbd>: Scroll right
|
||||||
<kbd>]</kbd>: next tab
|
<kbd>]</kbd>: Next tab
|
||||||
<kbd>[</kbd>: previous tab
|
<kbd>[</kbd>: Previous tab
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Commit summary
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Potwierdź
|
||||||
|
<kbd><esc></kbd>: Zamknij
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Commity
|
## Commity
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>b</kbd>: view bisect options
|
<kbd>b</kbd>: View bisect options
|
||||||
<kbd>s</kbd>: ściśnij
|
<kbd>s</kbd>: Ściśnij
|
||||||
<kbd>f</kbd>: napraw commit
|
<kbd>f</kbd>: Napraw commit
|
||||||
<kbd>r</kbd>: zmień nazwę commita
|
<kbd>r</kbd>: Zmień nazwę commita
|
||||||
<kbd>R</kbd>: zmień nazwę commita w edytorze
|
<kbd>R</kbd>: Zmień nazwę commita w edytorze
|
||||||
<kbd>d</kbd>: usuń commit
|
<kbd>d</kbd>: Usuń commit
|
||||||
<kbd>e</kbd>: edytuj commit
|
<kbd>e</kbd>: Edytuj commit
|
||||||
<kbd>p</kbd>: wybierz commit (podczas zmiany bazy)
|
<kbd>p</kbd>: Wybierz commit (podczas zmiany bazy)
|
||||||
<kbd>F</kbd>: utwórz commit naprawczy dla tego commita
|
<kbd>F</kbd>: Utwórz commit naprawczy dla tego commita
|
||||||
<kbd>S</kbd>: spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash)
|
<kbd>S</kbd>: Spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash)
|
||||||
<kbd>ctrl+j</kbd>: przenieś commit 1 w dół
|
<kbd><c-j></kbd>: Przenieś commit 1 w dół
|
||||||
<kbd>ctrl+k</kbd>: przenieś commit 1 w górę
|
<kbd><c-k></kbd>: Przenieś commit 1 w górę
|
||||||
<kbd>v</kbd>: wklej commity (przebieranie)
|
<kbd>v</kbd>: Wklej commity (przebieranie)
|
||||||
<kbd>A</kbd>: popraw commit zmianami z poczekalni
|
<kbd>A</kbd>: Popraw commit zmianami z poczekalni
|
||||||
<kbd>a</kbd>: reset commit author
|
<kbd>a</kbd>: Set/Reset commit author
|
||||||
<kbd>t</kbd>: odwróć commit
|
<kbd>t</kbd>: Odwróć commit
|
||||||
<kbd>T</kbd>: tag commit
|
<kbd>T</kbd>: Tag commit
|
||||||
<kbd>ctrl+l</kbd>: open log menu
|
<kbd><c-l></kbd>: Open log menu
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: create new branch off of commit
|
<kbd>n</kbd>: Create new branch off of commit
|
||||||
<kbd>g</kbd>: wyświetl opcje resetu
|
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||||
<kbd>c</kbd>: kopiuj commit (przebieranie)
|
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||||
<kbd>C</kbd>: kopiuj zakres commitów (przebieranie)
|
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Local Branches
|
## Confirmation panel
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
<kbd><enter></kbd>: Potwierdź
|
||||||
<kbd>i</kbd>: show git-flow options
|
<kbd><esc></kbd>: Zamknij
|
||||||
<kbd>space</kbd>: przełącz
|
|
||||||
<kbd>n</kbd>: nowa gałąź
|
|
||||||
<kbd>o</kbd>: utwórz żądanie pobrania
|
|
||||||
<kbd>O</kbd>: utwórz opcje żądania ściągnięcia
|
|
||||||
<kbd>ctrl+y</kbd>: skopiuj adres URL żądania pobrania do schowka
|
|
||||||
<kbd>c</kbd>: przełącz używając nazwy
|
|
||||||
<kbd>F</kbd>: wymuś przełączenie
|
|
||||||
<kbd>d</kbd>: usuń gałąź
|
|
||||||
<kbd>r</kbd>: zmiana bazy gałęzi
|
|
||||||
<kbd>M</kbd>: scal do obecnej gałęzi
|
|
||||||
<kbd>f</kbd>: fast-forward this branch from its upstream
|
|
||||||
<kbd>T</kbd>: create tag
|
|
||||||
<kbd>g</kbd>: wyświetl opcje resetu
|
|
||||||
<kbd>R</kbd>: rename branch
|
|
||||||
<kbd>u</kbd>: set/unset upstream
|
|
||||||
<kbd>enter</kbd>: view commits
|
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Main Panel (Patch Building)
|
## Local branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: poprzedni kawałek
|
<kbd><c-o></kbd>: Copy branch name to clipboard
|
||||||
<kbd>▶</kbd>: następny kawałek
|
<kbd>i</kbd>: Show git-flow options
|
||||||
<kbd>v</kbd>: toggle drag select
|
<kbd><space></kbd>: Przełącz
|
||||||
<kbd>V</kbd>: toggle drag select
|
<kbd>n</kbd>: Nowa gałąź
|
||||||
<kbd>a</kbd>: toggle select hunk
|
<kbd>o</kbd>: Utwórz żądanie pobrania
|
||||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
<kbd>O</kbd>: Utwórz opcje żądania ściągnięcia
|
||||||
<kbd>o</kbd>: otwórz plik
|
<kbd><c-y></kbd>: Skopiuj adres URL żądania pobrania do schowka
|
||||||
<kbd>e</kbd>: edytuj plik
|
<kbd>c</kbd>: Przełącz używając nazwy
|
||||||
<kbd>space</kbd>: add/remove line(s) to patch
|
<kbd>F</kbd>: Wymuś przełączenie
|
||||||
<kbd>esc</kbd>: wyście z trybu "linia po linii"
|
<kbd>d</kbd>: Usuń gałąź
|
||||||
|
<kbd>r</kbd>: Zmiana bazy gałęzi
|
||||||
|
<kbd>M</kbd>: Scal do obecnej gałęzi
|
||||||
|
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||||
|
<kbd>T</kbd>: Create tag
|
||||||
|
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||||
|
<kbd>R</kbd>: Rename branch
|
||||||
|
<kbd>u</kbd>: Set/Unset upstream
|
||||||
|
<kbd><enter></kbd>: View commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Main panel (patch building)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><left></kbd>: Poprzedni kawałek
|
||||||
|
<kbd><right></kbd>: Następny kawałek
|
||||||
|
<kbd>v</kbd>: Toggle drag select
|
||||||
|
<kbd>V</kbd>: Toggle drag select
|
||||||
|
<kbd>a</kbd>: Toggle select hunk
|
||||||
|
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||||
|
<kbd>o</kbd>: Otwórz plik
|
||||||
|
<kbd>e</kbd>: Edytuj plik
|
||||||
|
<kbd><space></kbd>: Add/Remove line(s) to patch
|
||||||
|
<kbd><esc></kbd>: Wyście z trybu "linia po linii"
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Wykonaj
|
||||||
|
<kbd><esc></kbd>: Zamknij
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Pliki
|
## Pliki
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy the file name to the clipboard
|
<kbd><c-o></kbd>: Copy the file name to the clipboard
|
||||||
<kbd>d</kbd>: pokaż opcje porzucania zmian
|
<kbd>d</kbd>: Pokaż opcje porzucania zmian
|
||||||
<kbd>space</kbd>: przełącz stan poczekalni
|
<kbd><space></kbd>: Przełącz stan poczekalni
|
||||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
<kbd><c-b></kbd>: Filter files by status
|
||||||
<kbd>c</kbd>: Zatwierdź zmiany
|
<kbd>c</kbd>: Zatwierdź zmiany
|
||||||
<kbd>w</kbd>: zatwierdź zmiany bez skryptu pre-commit
|
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
|
||||||
<kbd>A</kbd>: Zmień ostatni commit
|
<kbd>A</kbd>: Zmień ostatni commit
|
||||||
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
|
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
|
||||||
<kbd>e</kbd>: edytuj plik
|
<kbd>e</kbd>: Edytuj plik
|
||||||
<kbd>o</kbd>: otwórz plik
|
<kbd>o</kbd>: Otwórz plik
|
||||||
<kbd>i</kbd>: ignore or exclude file
|
<kbd>i</kbd>: Ignore or exclude file
|
||||||
<kbd>r</kbd>: odśwież pliki
|
<kbd>r</kbd>: Odśwież pliki
|
||||||
<kbd>s</kbd>: przechowaj zmiany
|
<kbd>s</kbd>: Przechowaj zmiany
|
||||||
<kbd>S</kbd>: wyświetl opcje schowka
|
<kbd>S</kbd>: Wyświetl opcje schowka
|
||||||
<kbd>a</kbd>: przełącz stan poczekalni wszystkich
|
<kbd>a</kbd>: Przełącz stan poczekalni wszystkich
|
||||||
<kbd>enter</kbd>: zatwierdź pojedyncze linie
|
<kbd><enter></kbd>: Zatwierdź pojedyncze linie
|
||||||
<kbd>g</kbd>: view upstream reset options
|
<kbd>g</kbd>: View upstream reset options
|
||||||
<kbd>D</kbd>: wyświetl opcje resetu
|
<kbd>D</kbd>: Wyświetl opcje resetu
|
||||||
<kbd>`</kbd>: toggle file tree view
|
<kbd>`</kbd>: Toggle file tree view
|
||||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||||
<kbd>f</kbd>: pobierz
|
<kbd>f</kbd>: Pobierz
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Pliki commita
|
## Pliki commita
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy the committed file name to the clipboard
|
<kbd><c-o></kbd>: Copy the committed file name to the clipboard
|
||||||
<kbd>c</kbd>: plik wybierania
|
<kbd>c</kbd>: Plik wybierania
|
||||||
<kbd>d</kbd>: porzuć zmiany commita dla tego pliku
|
<kbd>d</kbd>: Porzuć zmiany commita dla tego pliku
|
||||||
<kbd>o</kbd>: otwórz plik
|
<kbd>o</kbd>: Otwórz plik
|
||||||
<kbd>e</kbd>: edytuj plik
|
<kbd>e</kbd>: Edytuj plik
|
||||||
<kbd>space</kbd>: toggle file included in patch
|
<kbd><space></kbd>: Toggle file included in patch
|
||||||
<kbd>a</kbd>: toggle all files included in patch
|
<kbd>a</kbd>: Toggle all files included in patch
|
||||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||||
<kbd>`</kbd>: toggle file tree view
|
<kbd>`</kbd>: Toggle file tree view
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Poczekalnia
|
## Poczekalnia
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: poprzedni kawałek
|
<kbd><left></kbd>: Poprzedni kawałek
|
||||||
<kbd>▶</kbd>: następny kawałek
|
<kbd><right></kbd>: Następny kawałek
|
||||||
<kbd>v</kbd>: toggle drag select
|
<kbd>v</kbd>: Toggle drag select
|
||||||
<kbd>V</kbd>: toggle drag select
|
<kbd>V</kbd>: Toggle drag select
|
||||||
<kbd>a</kbd>: toggle select hunk
|
<kbd>a</kbd>: Toggle select hunk
|
||||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||||
<kbd>o</kbd>: otwórz plik
|
<kbd>o</kbd>: Otwórz plik
|
||||||
<kbd>e</kbd>: edytuj plik
|
<kbd>e</kbd>: Edytuj plik
|
||||||
<kbd>esc</kbd>: wróć do panelu plików
|
<kbd><esc></kbd>: Wróć do panelu plików
|
||||||
<kbd>tab</kbd>: switch to other panel (staged/unstaged changes)
|
<kbd><tab></kbd>: Switch to other panel (staged/unstaged changes)
|
||||||
<kbd>space</kbd>: toggle line staged / unstaged
|
<kbd><space></kbd>: Toggle line staged / unstaged
|
||||||
<kbd>d</kbd>: delete change (git reset)
|
<kbd>d</kbd>: Discard change (git reset)
|
||||||
<kbd>E</kbd>: edit hunk
|
<kbd>E</kbd>: Edit hunk
|
||||||
<kbd>c</kbd>: Zatwierdź zmiany
|
<kbd>c</kbd>: Zatwierdź zmiany
|
||||||
<kbd>w</kbd>: zatwierdź zmiany bez skryptu pre-commit
|
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
|
||||||
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
|
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Reflog
|
## Reflog
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: create new branch off of commit
|
<kbd>n</kbd>: Create new branch off of commit
|
||||||
<kbd>g</kbd>: wyświetl opcje resetu
|
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||||
<kbd>c</kbd>: kopiuj commit (przebieranie)
|
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||||
<kbd>C</kbd>: kopiuj zakres commitów (przebieranie)
|
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: view commits
|
<kbd><enter></kbd>: View commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Remote Branches
|
## Remote branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
<kbd><c-o></kbd>: Copy branch name to clipboard
|
||||||
<kbd>space</kbd>: przełącz
|
<kbd><space></kbd>: Przełącz
|
||||||
<kbd>n</kbd>: nowa gałąź
|
<kbd>n</kbd>: Nowa gałąź
|
||||||
<kbd>M</kbd>: scal do obecnej gałęzi
|
<kbd>M</kbd>: Scal do obecnej gałęzi
|
||||||
<kbd>r</kbd>: zmiana bazy gałęzi
|
<kbd>r</kbd>: Zmiana bazy gałęzi
|
||||||
<kbd>d</kbd>: usuń gałąź
|
<kbd>d</kbd>: Usuń gałąź
|
||||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||||
<kbd>esc</kbd>: wróć do listy repozytoriów zdalnych
|
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||||
<kbd>g</kbd>: wyświetl opcje resetu
|
<kbd><enter></kbd>: View commits
|
||||||
<kbd>enter</kbd>: view commits
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Remotes
|
## Remotes
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>f</kbd>: fetch remote
|
<kbd>f</kbd>: Fetch remote
|
||||||
<kbd>n</kbd>: add new remote
|
<kbd>n</kbd>: Add new remote
|
||||||
<kbd>d</kbd>: remove remote
|
<kbd>d</kbd>: Remove remote
|
||||||
<kbd>e</kbd>: edit remote
|
<kbd>e</kbd>: Edit remote
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Scalanie
|
## Scalanie
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: edytuj plik
|
<kbd>e</kbd>: Edytuj plik
|
||||||
<kbd>o</kbd>: otwórz plik
|
<kbd>o</kbd>: Otwórz plik
|
||||||
<kbd>◀</kbd>: poprzedni konflikt
|
<kbd><left></kbd>: Poprzedni konflikt
|
||||||
<kbd>▶</kbd>: następny konflikt
|
<kbd><right></kbd>: Następny konflikt
|
||||||
<kbd>▲</kbd>: wybierz poprzedni kawałek
|
<kbd><up></kbd>: Wybierz poprzedni kawałek
|
||||||
<kbd>▼</kbd>: wybierz następny kawałek
|
<kbd><down></kbd>: Wybierz następny kawałek
|
||||||
<kbd>z</kbd>: cofnij
|
<kbd>z</kbd>: Cofnij
|
||||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||||
<kbd>space</kbd>: wybierz kawałek
|
<kbd><space></kbd>: Wybierz kawałek
|
||||||
<kbd>b</kbd>: wybierz wszystkie kawałki
|
<kbd>b</kbd>: Wybierz oba kawałki
|
||||||
<kbd>esc</kbd>: wróć do panelu plików
|
<kbd><esc></kbd>: Wróć do panelu plików
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Schowek
|
## Schowek
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: zastosuj
|
<kbd><space></kbd>: Zastosuj
|
||||||
<kbd>g</kbd>: wyciągnij
|
<kbd>g</kbd>: Wyciągnij
|
||||||
<kbd>d</kbd>: porzuć
|
<kbd>d</kbd>: Porzuć
|
||||||
<kbd>n</kbd>: nowa gałąź
|
<kbd>n</kbd>: Nowa gałąź
|
||||||
<kbd>r</kbd>: rename stash
|
<kbd>r</kbd>: Rename stash
|
||||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: edytuj konfigurację
|
<kbd>o</kbd>: Otwórz konfigurację
|
||||||
<kbd>o</kbd>: otwórz konfigurację
|
<kbd>e</kbd>: Edytuj konfigurację
|
||||||
<kbd>u</kbd>: sprawdź aktualizacje
|
<kbd>u</kbd>: Sprawdź aktualizacje
|
||||||
<kbd>enter</kbd>: switch to a recent repo
|
<kbd><enter></kbd>: Switch to a recent repo
|
||||||
<kbd>a</kbd>: pokaż wszystkie logi gałęzi
|
<kbd>a</kbd>: Pokaż wszystkie logi gałęzi
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Sub-commits
|
## Sub-commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||||
<kbd>space</kbd>: checkout commit
|
<kbd><space></kbd>: Checkout commit
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: open commit in browser
|
<kbd>o</kbd>: Open commit in browser
|
||||||
<kbd>n</kbd>: create new branch off of commit
|
<kbd>n</kbd>: Create new branch off of commit
|
||||||
<kbd>g</kbd>: wyświetl opcje resetu
|
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||||
<kbd>c</kbd>: kopiuj commit (przebieranie)
|
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||||
<kbd>C</kbd>: kopiuj zakres commitów (przebieranie)
|
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||||
|
<kbd>/</kbd>: Search the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Submodules
|
## Submodules
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: copy submodule name to clipboard
|
<kbd><c-o></kbd>: Copy submodule name to clipboard
|
||||||
<kbd>enter</kbd>: enter submodule
|
<kbd><enter></kbd>: Enter submodule
|
||||||
<kbd>d</kbd>: remove submodule
|
<kbd>d</kbd>: Remove submodule
|
||||||
<kbd>u</kbd>: update submodule
|
<kbd>u</kbd>: Update submodule
|
||||||
<kbd>n</kbd>: add new submodule
|
<kbd>n</kbd>: Add new submodule
|
||||||
<kbd>e</kbd>: update submodule URL
|
<kbd>e</kbd>: Update submodule URL
|
||||||
<kbd>i</kbd>: initialize submodule
|
<kbd>i</kbd>: Initialize submodule
|
||||||
<kbd>b</kbd>: view bulk submodule options
|
<kbd>b</kbd>: View bulk submodule options
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Tags
|
## Tags
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: przełącz
|
<kbd><space></kbd>: Przełącz
|
||||||
<kbd>d</kbd>: delete tag
|
<kbd>d</kbd>: Delete tag
|
||||||
<kbd>P</kbd>: push tag
|
<kbd>P</kbd>: Push tag
|
||||||
<kbd>n</kbd>: create tag
|
<kbd>n</kbd>: Create tag
|
||||||
<kbd>g</kbd>: wyświetl opcje resetu
|
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||||
<kbd>enter</kbd>: view commits
|
<kbd><enter></kbd>: View commits
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Zwykłe
|
## Zwykłe
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>mouse wheel ▼</kbd>: przewiń w dół (fn+up)
|
<kbd>mouse wheel down</kbd>: Przewiń w dół (fn+up)
|
||||||
<kbd>mouse wheel ▲</kbd>: przewiń w górę (fn+down)
|
<kbd>mouse wheel up</kbd>: Przewiń w górę (fn+down)
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
@@ -0,0 +1,333 @@
|
|||||||
|
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||||
|
|
||||||
|
# Lazygit Связки клавиш
|
||||||
|
|
||||||
|
_Связки клавиш_
|
||||||
|
|
||||||
|
## Глобальные сочетания клавиш
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-r></kbd>: Переключиться на последний репозиторий
|
||||||
|
<kbd><pgup></kbd>: Прокрутить вверх главную панель (fn+up/shift+k)
|
||||||
|
<kbd><pgdown></kbd>: Прокрутить вниз главную панель (fn+down/shift+j)
|
||||||
|
<kbd>@</kbd>: Открыть меню журнала команд
|
||||||
|
<kbd>}</kbd>: Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении
|
||||||
|
<kbd>{</kbd>: Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении
|
||||||
|
<kbd>:</kbd>: Выполнить пользовательскую команду
|
||||||
|
<kbd><c-p></kbd>: Просмотреть пользовательские параметры патча
|
||||||
|
<kbd>m</kbd>: Просмотреть параметры слияния/перебазирования
|
||||||
|
<kbd>R</kbd>: Обновить
|
||||||
|
<kbd>+</kbd>: Следующий режим экрана (нормальный/полуэкранный/полноэкранный)
|
||||||
|
<kbd>_</kbd>: Предыдущий режим экрана
|
||||||
|
<kbd>?</kbd>: Открыть меню
|
||||||
|
<kbd><c-s></kbd>: Просмотреть параметры фильтрации по пути
|
||||||
|
<kbd>W</kbd>: Открыть меню сравнении
|
||||||
|
<kbd><c-e></kbd>: Открыть меню сравнении
|
||||||
|
<kbd><c-w></kbd>: Переключить отображение изменении пробелов в просмотрщике сравнении
|
||||||
|
<kbd>z</kbd>: Отменить (через reflog) (экспериментальный)
|
||||||
|
<kbd><c-z></kbd>: Повторить (через reflog) (экспериментальный)
|
||||||
|
<kbd>P</kbd>: Отправить изменения
|
||||||
|
<kbd>p</kbd>: Получить и слить изменения
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Навигация по панели списка
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>,</kbd>: Предыдущая страница
|
||||||
|
<kbd>.</kbd>: Следующая страница
|
||||||
|
<kbd><</kbd>: Пролистать наверх
|
||||||
|
<kbd>></kbd>: Прокрутить вниз
|
||||||
|
<kbd>/</kbd>: Найти
|
||||||
|
<kbd>H</kbd>: Прокрутить влево
|
||||||
|
<kbd>L</kbd>: Прокрутить вправо
|
||||||
|
<kbd>]</kbd>: Следующая вкладка
|
||||||
|
<kbd>[</kbd>: Предыдущая вкладка
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Главная панель (Индексирование)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><left></kbd>: Выбрать предыдущую часть
|
||||||
|
<kbd><right></kbd>: Выбрать следующую часть
|
||||||
|
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||||
|
<kbd>V</kbd>: Переключить выборку перетаскивания
|
||||||
|
<kbd>a</kbd>: Переключить выборку частей
|
||||||
|
<kbd><c-o></kbd>: Скопировать выделенный текст в буфер обмена
|
||||||
|
<kbd>o</kbd>: Открыть файл
|
||||||
|
<kbd>e</kbd>: Редактировать файл
|
||||||
|
<kbd><esc></kbd>: Вернуться к панели файлов
|
||||||
|
<kbd><tab></kbd>: Переключиться на другую панель (проиндексированные/непроиндексированные изменения)
|
||||||
|
<kbd><space></kbd>: Переключить строку в проиндексированные / непроиндексированные
|
||||||
|
<kbd>d</kbd>: Отменить изменение (git reset)
|
||||||
|
<kbd>E</kbd>: Изменить эту часть
|
||||||
|
<kbd>c</kbd>: Сохранить изменения
|
||||||
|
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
|
||||||
|
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
|
||||||
|
<kbd>/</kbd>: Найти
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Главная панель (Обычный)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>mouse wheel down</kbd>: Прокрутить вниз (fn+up)
|
||||||
|
<kbd>mouse wheel up</kbd>: Прокрутить вверх (fn+down)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Главная панель (Слияние)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>e</kbd>: Редактировать файл
|
||||||
|
<kbd>o</kbd>: Открыть файл
|
||||||
|
<kbd><left></kbd>: Выбрать предыдущий конфликт
|
||||||
|
<kbd><right></kbd>: Выбрать следующий конфликт
|
||||||
|
<kbd><up></kbd>: Выбрать предыдущую часть
|
||||||
|
<kbd><down></kbd>: Выбрать следующую часть
|
||||||
|
<kbd>z</kbd>: Отменить
|
||||||
|
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
|
||||||
|
<kbd><space></kbd>: Выбрать эту часть
|
||||||
|
<kbd>b</kbd>: Выбрать все части
|
||||||
|
<kbd><esc></kbd>: Вернуться к панели файлов
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Главная панель (сборка патчей)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><left></kbd>: Выбрать предыдущую часть
|
||||||
|
<kbd><right></kbd>: Выбрать следующую часть
|
||||||
|
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||||
|
<kbd>V</kbd>: Переключить выборку перетаскивания
|
||||||
|
<kbd>a</kbd>: Переключить выборку частей
|
||||||
|
<kbd><c-o></kbd>: Скопировать выделенный текст в буфер обмена
|
||||||
|
<kbd>o</kbd>: Открыть файл
|
||||||
|
<kbd>e</kbd>: Редактировать файл
|
||||||
|
<kbd><space></kbd>: Добавить/удалить строку(и) для патча
|
||||||
|
<kbd><esc></kbd>: Выйти из сборщика пользовательских патчей
|
||||||
|
<kbd>/</kbd>: Найти
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Журнал ссылок (Reflog)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать SHA коммита в буфер обмена
|
||||||
|
<kbd><space></kbd>: Переключить коммит
|
||||||
|
<kbd>y</kbd>: Скопировать атрибут коммита
|
||||||
|
<kbd>o</kbd>: Открыть коммит в браузере
|
||||||
|
<kbd>n</kbd>: Создать новую ветку с этого коммита
|
||||||
|
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||||
|
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||||
|
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||||
|
<kbd><c-r></kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
|
||||||
|
<kbd><enter></kbd>: Просмотреть коммиты
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Коммиты
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать SHA коммита в буфер обмена
|
||||||
|
<kbd><c-r></kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
|
||||||
|
<kbd>b</kbd>: Просмотреть параметры бинарного поиска
|
||||||
|
<kbd>s</kbd>: Объединить несколько коммитов в один нижний
|
||||||
|
<kbd>f</kbd>: Объединить несколько коммитов в один отбросив сообщение коммита
|
||||||
|
<kbd>r</kbd>: Перефразировать коммит
|
||||||
|
<kbd>R</kbd>: Переписать коммит с помощью редактора
|
||||||
|
<kbd>d</kbd>: Удалить коммит
|
||||||
|
<kbd>e</kbd>: Изменить коммит
|
||||||
|
<kbd>p</kbd>: Выбрать коммит (в середине перебазирования)
|
||||||
|
<kbd>F</kbd>: Создать fixup коммит для этого коммита
|
||||||
|
<kbd>S</kbd>: Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение)
|
||||||
|
<kbd><c-j></kbd>: Переместить коммит вниз на один
|
||||||
|
<kbd><c-k></kbd>: Переместить коммит вверх на один
|
||||||
|
<kbd>v</kbd>: Вставить отобранные коммиты (cherry-pick)
|
||||||
|
<kbd>A</kbd>: Править последний коммит с проиндексированными изменениями
|
||||||
|
<kbd>a</kbd>: Установить/убрать автора коммита
|
||||||
|
<kbd>t</kbd>: Отменить коммит
|
||||||
|
<kbd>T</kbd>: Пометить коммит тегом
|
||||||
|
<kbd><c-l></kbd>: Открыть меню журнала
|
||||||
|
<kbd><space></kbd>: Переключить коммит
|
||||||
|
<kbd>y</kbd>: Скопировать атрибут коммита
|
||||||
|
<kbd>o</kbd>: Открыть коммит в браузере
|
||||||
|
<kbd>n</kbd>: Создать новую ветку с этого коммита
|
||||||
|
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||||
|
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||||
|
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||||
|
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||||
|
<kbd>/</kbd>: Найти
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Локальные Ветки
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать название ветки в буфер обмена
|
||||||
|
<kbd>i</kbd>: Показать параметры git-flow
|
||||||
|
<kbd><space></kbd>: Переключить
|
||||||
|
<kbd>n</kbd>: Новая ветка
|
||||||
|
<kbd>o</kbd>: Создать запрос на принятие изменений
|
||||||
|
<kbd>O</kbd>: Создать параметры запроса принятие изменений
|
||||||
|
<kbd><c-y></kbd>: Скопировать URL запроса на принятие изменений в буфер обмена
|
||||||
|
<kbd>c</kbd>: Переключить по названию
|
||||||
|
<kbd>F</kbd>: Принудительное переключение
|
||||||
|
<kbd>d</kbd>: Удалить ветку
|
||||||
|
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
|
||||||
|
<kbd>M</kbd>: Слияние с текущей переключённой веткой
|
||||||
|
<kbd>f</kbd>: Перемотать эту ветку вперёд из её upstream-ветки
|
||||||
|
<kbd>T</kbd>: Создать тег
|
||||||
|
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||||
|
<kbd>R</kbd>: Переименовать ветку
|
||||||
|
<kbd>u</kbd>: Установить/убрать upstream-ветку
|
||||||
|
<kbd><enter></kbd>: Просмотреть коммиты
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Меню
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Выполнить
|
||||||
|
<kbd><esc></kbd>: Закрыть
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Панель Подтверждения
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Подтвердить
|
||||||
|
<kbd><esc></kbd>: Закрыть/отменить
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Подкоммиты
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать SHA коммита в буфер обмена
|
||||||
|
<kbd><space></kbd>: Переключить коммит
|
||||||
|
<kbd>y</kbd>: Скопировать атрибут коммита
|
||||||
|
<kbd>o</kbd>: Открыть коммит в браузере
|
||||||
|
<kbd>n</kbd>: Создать новую ветку с этого коммита
|
||||||
|
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||||
|
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||||
|
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||||
|
<kbd><c-r></kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
|
||||||
|
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||||
|
<kbd>/</kbd>: Найти
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Подмодули
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать название подмодуля в буфер обмена
|
||||||
|
<kbd><enter></kbd>: Ввести подмодуль
|
||||||
|
<kbd>d</kbd>: Удалить подмодуль
|
||||||
|
<kbd>u</kbd>: Обновить подмодуль
|
||||||
|
<kbd>n</kbd>: Добавить новый подмодуль
|
||||||
|
<kbd>e</kbd>: Обновить URL подмодуля
|
||||||
|
<kbd>i</kbd>: Инициализировать подмодуль
|
||||||
|
<kbd>b</kbd>: Просмотреть параметры массового подмодуля
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Сводка коммита
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: Подтвердить
|
||||||
|
<kbd><esc></kbd>: Закрыть
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Сохранить Изменения Файлов
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать закомиченное имя файла в буфер обмена
|
||||||
|
<kbd>c</kbd>: Переключить файл
|
||||||
|
<kbd>d</kbd>: Отменить изменения коммита в этом файле
|
||||||
|
<kbd>o</kbd>: Открыть файл
|
||||||
|
<kbd>e</kbd>: Редактировать файл
|
||||||
|
<kbd><space></kbd>: Переключить файлы включённые в патч
|
||||||
|
<kbd>a</kbd>: Переключить все файлы, включённые в патч
|
||||||
|
<kbd><enter></kbd>: Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения)
|
||||||
|
<kbd>`</kbd>: Переключить вид дерева файлов
|
||||||
|
<kbd>/</kbd>: Найти
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Статус
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>o</kbd>: Открыть файл конфигурации
|
||||||
|
<kbd>e</kbd>: Редактировать файл конфигурации
|
||||||
|
<kbd>u</kbd>: Проверить обновления
|
||||||
|
<kbd><enter></kbd>: Переключиться на последний репозиторий
|
||||||
|
<kbd>a</kbd>: Показать все логи ветки
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Теги
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><space></kbd>: Переключить
|
||||||
|
<kbd>d</kbd>: Удалить тег
|
||||||
|
<kbd>P</kbd>: Отправить тег
|
||||||
|
<kbd>n</kbd>: Создать тег
|
||||||
|
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||||
|
<kbd><enter></kbd>: Просмотреть коммиты
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Удалённые ветки
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать название ветки в буфер обмена
|
||||||
|
<kbd><space></kbd>: Переключить
|
||||||
|
<kbd>n</kbd>: Новая ветка
|
||||||
|
<kbd>M</kbd>: Слияние с текущей переключённой веткой
|
||||||
|
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
|
||||||
|
<kbd>d</kbd>: Удалить ветку
|
||||||
|
<kbd>u</kbd>: Установить как upstream-ветку переключённую ветку
|
||||||
|
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||||
|
<kbd><enter></kbd>: Просмотреть коммиты
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Удалённые репозитории
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>f</kbd>: Получение изменения из удалённого репозитория
|
||||||
|
<kbd>n</kbd>: Добавить новую удалённую ветку
|
||||||
|
<kbd>d</kbd>: Удалить удалённую ветку
|
||||||
|
<kbd>e</kbd>: Редактировать удалённый репозитории
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Файлы
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: Скопировать название файла в буфер обмена
|
||||||
|
<kbd>d</kbd>: Просмотреть параметры «отмены изменении»
|
||||||
|
<kbd><space></kbd>: Переключить индекс
|
||||||
|
<kbd><c-b></kbd>: Фильтровать файлы (проиндексированные/непроиндексированные)
|
||||||
|
<kbd>c</kbd>: Сохранить изменения
|
||||||
|
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
|
||||||
|
<kbd>A</kbd>: Правка последнего коммита
|
||||||
|
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
|
||||||
|
<kbd>e</kbd>: Редактировать файл
|
||||||
|
<kbd>o</kbd>: Открыть файл
|
||||||
|
<kbd>i</kbd>: Игнорировать или исключить файл
|
||||||
|
<kbd>r</kbd>: Обновить файлы
|
||||||
|
<kbd>s</kbd>: Припрятать все изменения
|
||||||
|
<kbd>S</kbd>: Просмотреть параметры хранилища
|
||||||
|
<kbd>a</kbd>: Все проиндексированные/непроиндексированные
|
||||||
|
<kbd><enter></kbd>: Проиндексировать отдельные части/строки для файла или свернуть/развернуть для каталога
|
||||||
|
<kbd>g</kbd>: Просмотреть параметры сброса upstream-ветки
|
||||||
|
<kbd>D</kbd>: Просмотреть параметры сброса
|
||||||
|
<kbd>`</kbd>: Переключить вид дерева файлов
|
||||||
|
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
|
||||||
|
<kbd>f</kbd>: Получить изменения
|
||||||
|
<kbd>/</kbd>: Найти
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Хранилище
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><space></kbd>: Применить припрятанные изменения
|
||||||
|
<kbd>g</kbd>: Применить припрятанные изменения и тут же удалить их из хранилища
|
||||||
|
<kbd>d</kbd>: Удалить припрятанные изменения из хранилища
|
||||||
|
<kbd>n</kbd>: Новая ветка
|
||||||
|
<kbd>r</kbd>: Переименовать хранилище
|
||||||
|
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
@@ -2,28 +2,30 @@ _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_
|
||||||
|
|
||||||
## 全局键绑定
|
## 全局键绑定
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+r</kbd>: 切换到最近的仓库
|
<kbd><c-r></kbd>: 切换到最近的仓库
|
||||||
<kbd>pgup</kbd>: 向上滚动主面板 (fn+up/shift+k)
|
<kbd><pgup></kbd>: 向上滚动主面板 (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: 向下滚动主面板 (fn+down/shift+j)
|
<kbd><pgdown></kbd>: 向下滚动主面板 (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: 查看 合并/变基 选项
|
|
||||||
<kbd>ctrl+p</kbd>: 查看自定义补丁选项
|
|
||||||
<kbd>R</kbd>: 刷新
|
|
||||||
<kbd>?</kbd>: 打开菜单
|
|
||||||
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
|
|
||||||
<kbd>_</kbd>: 上一屏模式
|
|
||||||
<kbd>ctrl+s</kbd>: 查看按路径过滤选项
|
|
||||||
<kbd>W</kbd>: 打开 diff 菜单
|
|
||||||
<kbd>ctrl+e</kbd>: 打开 diff 菜单
|
|
||||||
<kbd>@</kbd>: 打开命令日志菜单
|
<kbd>@</kbd>: 打开命令日志菜单
|
||||||
<kbd>ctrl+w</kbd>: 切换是否在差异视图中显示空白字符差异
|
|
||||||
<kbd>}</kbd>: 扩大差异视图中显示的上下文范围
|
<kbd>}</kbd>: 扩大差异视图中显示的上下文范围
|
||||||
<kbd>{</kbd>: 缩小差异视图中显示的上下文范围
|
<kbd>{</kbd>: 缩小差异视图中显示的上下文范围
|
||||||
<kbd>:</kbd>: 执行自定义命令
|
<kbd>:</kbd>: 执行自定义命令
|
||||||
|
<kbd><c-p></kbd>: 查看自定义补丁选项
|
||||||
|
<kbd>m</kbd>: 查看 合并/变基 选项
|
||||||
|
<kbd>R</kbd>: 刷新
|
||||||
|
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
|
||||||
|
<kbd>_</kbd>: 上一屏模式
|
||||||
|
<kbd>?</kbd>: 打开菜单
|
||||||
|
<kbd><c-s></kbd>: 查看按路径过滤选项
|
||||||
|
<kbd>W</kbd>: 打开 diff 菜单
|
||||||
|
<kbd><c-e></kbd>: 打开 diff 菜单
|
||||||
|
<kbd><c-w></kbd>: 切换是否在差异视图中显示空白字符差异
|
||||||
<kbd>z</kbd>: (通过 reflog)撤销「实验功能」
|
<kbd>z</kbd>: (通过 reflog)撤销「实验功能」
|
||||||
<kbd>ctrl+z</kbd>: (通过 reflog)重做「实验功能」
|
<kbd><c-z></kbd>: (通过 reflog)重做「实验功能」
|
||||||
<kbd>P</kbd>: 推送
|
<kbd>P</kbd>: 推送
|
||||||
<kbd>p</kbd>: 拉取
|
<kbd>p</kbd>: 拉取
|
||||||
</pre>
|
</pre>
|
||||||
@@ -33,9 +35,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<pre>
|
<pre>
|
||||||
<kbd>,</kbd>: 上一页
|
<kbd>,</kbd>: 上一页
|
||||||
<kbd>.</kbd>: 下一页
|
<kbd>.</kbd>: 下一页
|
||||||
<kbd><</kbd>: 滚动到顶部
|
<kbd><</kbd>: 滚动到顶部
|
||||||
|
<kbd>></kbd>: 滚动到底部
|
||||||
<kbd>/</kbd>: 开始搜索
|
<kbd>/</kbd>: 开始搜索
|
||||||
<kbd>></kbd>: 滚动到底部
|
|
||||||
<kbd>H</kbd>: 向左滚动
|
<kbd>H</kbd>: 向左滚动
|
||||||
<kbd>L</kbd>: 向右滚动
|
<kbd>L</kbd>: 向右滚动
|
||||||
<kbd>]</kbd>: 下一个标签
|
<kbd>]</kbd>: 下一个标签
|
||||||
@@ -45,28 +47,29 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## Reflog 页面
|
## Reflog 页面
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将提交的 SHA 复制到剪贴板
|
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||||
<kbd>space</kbd>: 检出提交
|
<kbd><space></kbd>: 检出提交
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: 在浏览器中打开提交
|
<kbd>o</kbd>: 在浏览器中打开提交
|
||||||
<kbd>n</kbd>: 从提交创建新分支
|
<kbd>n</kbd>: 从提交创建新分支
|
||||||
<kbd>g</kbd>: 查看重置选项
|
<kbd>g</kbd>: 查看重置选项
|
||||||
<kbd>c</kbd>: 复制提交(拣选)
|
<kbd>c</kbd>: 复制提交(拣选)
|
||||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||||
<kbd>ctrl+r</kbd>: 重置已拣选(复制)的提交
|
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||||
<kbd>enter</kbd>: 查看提交
|
<kbd><enter></kbd>: 查看提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 分支页面
|
## 分支页面
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将分支名称复制到剪贴板
|
<kbd><c-o></kbd>: 将分支名称复制到剪贴板
|
||||||
<kbd>i</kbd>: 显示 git-flow 选项
|
<kbd>i</kbd>: 显示 git-flow 选项
|
||||||
<kbd>space</kbd>: 检出
|
<kbd><space></kbd>: 检出
|
||||||
<kbd>n</kbd>: 新分支
|
<kbd>n</kbd>: 新分支
|
||||||
<kbd>o</kbd>: 创建抓取请求
|
<kbd>o</kbd>: 创建抓取请求
|
||||||
<kbd>O</kbd>: 创建抓取请求选项
|
<kbd>O</kbd>: 创建抓取请求选项
|
||||||
<kbd>ctrl+y</kbd>: 将抓取请求 URL 复制到剪贴板
|
<kbd><c-y></kbd>: 将抓取请求 URL 复制到剪贴板
|
||||||
<kbd>c</kbd>: 按名称检出
|
<kbd>c</kbd>: 按名称检出
|
||||||
<kbd>F</kbd>: 强制检出
|
<kbd>F</kbd>: 强制检出
|
||||||
<kbd>d</kbd>: 删除分支
|
<kbd>d</kbd>: 删除分支
|
||||||
@@ -76,43 +79,46 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>T</kbd>: 创建标签
|
<kbd>T</kbd>: 创建标签
|
||||||
<kbd>g</kbd>: 查看重置选项
|
<kbd>g</kbd>: 查看重置选项
|
||||||
<kbd>R</kbd>: 重命名分支
|
<kbd>R</kbd>: 重命名分支
|
||||||
<kbd>u</kbd>: set/unset upstream
|
<kbd>u</kbd>: Set/Unset upstream
|
||||||
<kbd>enter</kbd>: 查看提交
|
<kbd><enter></kbd>: 查看提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 子提交
|
## 子提交
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将提交的 SHA 复制到剪贴板
|
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||||
<kbd>space</kbd>: 检出提交
|
<kbd><space></kbd>: 检出提交
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: 在浏览器中打开提交
|
<kbd>o</kbd>: 在浏览器中打开提交
|
||||||
<kbd>n</kbd>: 从提交创建新分支
|
<kbd>n</kbd>: 从提交创建新分支
|
||||||
<kbd>g</kbd>: 查看重置选项
|
<kbd>g</kbd>: 查看重置选项
|
||||||
<kbd>c</kbd>: 复制提交(拣选)
|
<kbd>c</kbd>: 复制提交(拣选)
|
||||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||||
<kbd>ctrl+r</kbd>: 重置已拣选(复制)的提交
|
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||||
<kbd>enter</kbd>: 查看提交的文件
|
<kbd><enter></kbd>: 查看提交的文件
|
||||||
|
<kbd>/</kbd>: 开始搜索
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 子模块
|
## 子模块
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将子模块名称复制到剪贴板
|
<kbd><c-o></kbd>: 将子模块名称复制到剪贴板
|
||||||
<kbd>enter</kbd>: 输入子模块
|
<kbd><enter></kbd>: 输入子模块
|
||||||
<kbd>d</kbd>: 删除子模块
|
<kbd>d</kbd>: 删除子模块
|
||||||
<kbd>u</kbd>: 更新子模块
|
<kbd>u</kbd>: 更新子模块
|
||||||
<kbd>n</kbd>: 添加新的子模块
|
<kbd>n</kbd>: 添加新的子模块
|
||||||
<kbd>e</kbd>: 更新子模块 URL
|
<kbd>e</kbd>: 更新子模块 URL
|
||||||
<kbd>i</kbd>: 初始化子模块
|
<kbd>i</kbd>: 初始化子模块
|
||||||
<kbd>b</kbd>: 查看批量子模块选项
|
<kbd>b</kbd>: 查看批量子模块选项
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 提交
|
## 提交
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将提交的 SHA 复制到剪贴板
|
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||||
<kbd>ctrl+r</kbd>: 重置已拣选(复制)的提交
|
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||||
<kbd>b</kbd>: 查看二分查找选项
|
<kbd>b</kbd>: 查看二分查找选项
|
||||||
<kbd>s</kbd>: 向下压缩
|
<kbd>s</kbd>: 向下压缩
|
||||||
<kbd>f</kbd>: 修正提交(fixup)
|
<kbd>f</kbd>: 修正提交(fixup)
|
||||||
@@ -121,47 +127,56 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>d</kbd>: 删除提交
|
<kbd>d</kbd>: 删除提交
|
||||||
<kbd>e</kbd>: 编辑提交
|
<kbd>e</kbd>: 编辑提交
|
||||||
<kbd>p</kbd>: 选择提交(变基过程中)
|
<kbd>p</kbd>: 选择提交(变基过程中)
|
||||||
<kbd>F</kbd>: 为此提交创建修正
|
<kbd>F</kbd>: 创建修正提交
|
||||||
<kbd>S</kbd>: 压缩在所选提交之上的所有“fixup!”提交(自动压缩)
|
<kbd>S</kbd>: 压缩在所选提交之上的所有“fixup!”提交(自动压缩)
|
||||||
<kbd>ctrl+j</kbd>: 下移提交
|
<kbd><c-j></kbd>: 下移提交
|
||||||
<kbd>ctrl+k</kbd>: 上移提交
|
<kbd><c-k></kbd>: 上移提交
|
||||||
<kbd>v</kbd>: 粘贴提交(拣选)
|
<kbd>v</kbd>: 粘贴提交(拣选)
|
||||||
<kbd>A</kbd>: 用已暂存的更改来修补提交
|
<kbd>A</kbd>: 用已暂存的更改来修补提交
|
||||||
<kbd>a</kbd>: reset commit author
|
<kbd>a</kbd>: Set/Reset commit author
|
||||||
<kbd>t</kbd>: 还原提交
|
<kbd>t</kbd>: 还原提交
|
||||||
<kbd>T</kbd>: 标签提交
|
<kbd>T</kbd>: 标签提交
|
||||||
<kbd>ctrl+l</kbd>: 打开日志菜单
|
<kbd><c-l></kbd>: 打开日志菜单
|
||||||
<kbd>space</kbd>: 检出提交
|
<kbd><space></kbd>: 检出提交
|
||||||
<kbd>y</kbd>: copy commit attribute
|
<kbd>y</kbd>: Copy commit attribute
|
||||||
<kbd>o</kbd>: 在浏览器中打开提交
|
<kbd>o</kbd>: 在浏览器中打开提交
|
||||||
<kbd>n</kbd>: 从提交创建新分支
|
<kbd>n</kbd>: 从提交创建新分支
|
||||||
<kbd>g</kbd>: 查看重置选项
|
<kbd>g</kbd>: 查看重置选项
|
||||||
<kbd>c</kbd>: 复制提交(拣选)
|
<kbd>c</kbd>: 复制提交(拣选)
|
||||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||||
<kbd>enter</kbd>: 查看提交的文件
|
<kbd><enter></kbd>: 查看提交的文件
|
||||||
|
<kbd>/</kbd>: 开始搜索
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 提交文件
|
## 提交文件
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将提交的文件名复制到剪贴板
|
<kbd><c-o></kbd>: 将提交的文件名复制到剪贴板
|
||||||
<kbd>c</kbd>: 检出文件
|
<kbd>c</kbd>: 检出文件
|
||||||
<kbd>d</kbd>: 放弃对此文件的提交更改
|
<kbd>d</kbd>: 放弃对此文件的提交更改
|
||||||
<kbd>o</kbd>: 打开文件
|
<kbd>o</kbd>: 打开文件
|
||||||
<kbd>e</kbd>: 编辑文件
|
<kbd>e</kbd>: 编辑文件
|
||||||
<kbd>space</kbd>: 补丁中包含的切换文件
|
<kbd><space></kbd>: 补丁中包含的切换文件
|
||||||
<kbd>a</kbd>: toggle all files included in patch
|
<kbd>a</kbd>: Toggle all files included in patch
|
||||||
<kbd>enter</kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
|
<kbd><enter></kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
|
||||||
<kbd>`</kbd>: 切换文件树视图
|
<kbd>`</kbd>: 切换文件树视图
|
||||||
|
<kbd>/</kbd>: 开始搜索
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 提交讯息
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 确认
|
||||||
|
<kbd><esc></kbd>: 关闭
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 文件
|
## 文件
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将文件名复制到剪贴板
|
<kbd><c-o></kbd>: 将文件名复制到剪贴板
|
||||||
<kbd>d</kbd>: 查看'放弃更改'选项
|
<kbd>d</kbd>: 查看'放弃更改'选项
|
||||||
<kbd>space</kbd>: 切换暂存状态
|
<kbd><space></kbd>: 切换暂存状态
|
||||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
<kbd><c-b></kbd>: Filter files by status
|
||||||
<kbd>c</kbd>: 提交更改
|
<kbd>c</kbd>: 提交更改
|
||||||
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
||||||
<kbd>A</kbd>: 修补最后一次提交
|
<kbd>A</kbd>: 修补最后一次提交
|
||||||
@@ -173,38 +188,41 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>s</kbd>: 将所有更改加入贮藏
|
<kbd>s</kbd>: 将所有更改加入贮藏
|
||||||
<kbd>S</kbd>: 查看贮藏选项
|
<kbd>S</kbd>: 查看贮藏选项
|
||||||
<kbd>a</kbd>: 切换所有文件的暂存状态
|
<kbd>a</kbd>: 切换所有文件的暂存状态
|
||||||
<kbd>enter</kbd>: 暂存单个 块/行 用于文件, 或 折叠/展开 目录
|
<kbd><enter></kbd>: 暂存单个 块/行 用于文件, 或 折叠/展开 目录
|
||||||
<kbd>g</kbd>: 查看上游重置选项
|
<kbd>g</kbd>: 查看上游重置选项
|
||||||
<kbd>D</kbd>: 查看重置选项
|
<kbd>D</kbd>: 查看重置选项
|
||||||
<kbd>`</kbd>: 切换文件树视图
|
<kbd>`</kbd>: 切换文件树视图
|
||||||
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
|
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
|
||||||
<kbd>f</kbd>: 抓取
|
<kbd>f</kbd>: 抓取
|
||||||
|
<kbd>/</kbd>: 开始搜索
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 构建补丁中
|
## 构建补丁中
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: 选择上一个区块
|
<kbd><left></kbd>: 选择上一个区块
|
||||||
<kbd>▶</kbd>: 选择下一个区块
|
<kbd><right></kbd>: 选择下一个区块
|
||||||
<kbd>v</kbd>: 切换拖动选择
|
<kbd>v</kbd>: 切换拖动选择
|
||||||
<kbd>V</kbd>: 切换拖动选择
|
<kbd>V</kbd>: 切换拖动选择
|
||||||
<kbd>a</kbd>: 切换选择区块
|
<kbd>a</kbd>: 切换选择区块
|
||||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||||
<kbd>o</kbd>: 打开文件
|
<kbd>o</kbd>: 打开文件
|
||||||
<kbd>e</kbd>: 编辑文件
|
<kbd>e</kbd>: 编辑文件
|
||||||
<kbd>space</kbd>: 添加/移除 行到补丁
|
<kbd><space></kbd>: 添加/移除 行到补丁
|
||||||
<kbd>esc</kbd>: 退出逐行模式
|
<kbd><esc></kbd>: 退出逐行模式
|
||||||
|
<kbd>/</kbd>: 开始搜索
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 标签页面
|
## 标签页面
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: 检出
|
<kbd><space></kbd>: 检出
|
||||||
<kbd>d</kbd>: 删除标签
|
<kbd>d</kbd>: 删除标签
|
||||||
<kbd>P</kbd>: 推送标签
|
<kbd>P</kbd>: 推送标签
|
||||||
<kbd>n</kbd>: 创建标签
|
<kbd>n</kbd>: 创建标签
|
||||||
<kbd>g</kbd>: 查看重置选项
|
<kbd>g</kbd>: 查看重置选项
|
||||||
<kbd>enter</kbd>: 查看提交
|
<kbd><enter></kbd>: 查看提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 正在合并
|
## 正在合并
|
||||||
@@ -212,79 +230,96 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 编辑文件
|
<kbd>e</kbd>: 编辑文件
|
||||||
<kbd>o</kbd>: 打开文件
|
<kbd>o</kbd>: 打开文件
|
||||||
<kbd>◀</kbd>: 选择上一个冲突
|
<kbd><left></kbd>: 选择上一个冲突
|
||||||
<kbd>▶</kbd>: 选择下一个冲突
|
<kbd><right></kbd>: 选择下一个冲突
|
||||||
<kbd>▲</kbd>: 选择顶部块
|
<kbd><up></kbd>: 选择顶部块
|
||||||
<kbd>▼</kbd>: 选择底部块
|
<kbd><down></kbd>: 选择底部块
|
||||||
<kbd>z</kbd>: 撤销
|
<kbd>z</kbd>: 撤销
|
||||||
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
|
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
|
||||||
<kbd>space</kbd>: 选中区块
|
<kbd><space></kbd>: 选中区块
|
||||||
<kbd>b</kbd>: 选中所有区块
|
<kbd>b</kbd>: 选中所有区块
|
||||||
<kbd>esc</kbd>: 返回文件面板
|
<kbd><esc></kbd>: 返回文件面板
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 正在暂存
|
## 正在暂存
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>◀</kbd>: 选择上一个区块
|
<kbd><left></kbd>: 选择上一个区块
|
||||||
<kbd>▶</kbd>: 选择下一个区块
|
<kbd><right></kbd>: 选择下一个区块
|
||||||
<kbd>v</kbd>: 切换拖动选择
|
<kbd>v</kbd>: 切换拖动选择
|
||||||
<kbd>V</kbd>: 切换拖动选择
|
<kbd>V</kbd>: 切换拖动选择
|
||||||
<kbd>a</kbd>: 切换选择区块
|
<kbd>a</kbd>: 切换选择区块
|
||||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||||
<kbd>o</kbd>: 打开文件
|
<kbd>o</kbd>: 打开文件
|
||||||
<kbd>e</kbd>: 编辑文件
|
<kbd>e</kbd>: 编辑文件
|
||||||
<kbd>esc</kbd>: 返回文件面板
|
<kbd><esc></kbd>: 返回文件面板
|
||||||
<kbd>tab</kbd>: 切换到其他面板
|
<kbd><tab></kbd>: 切换到其他面板
|
||||||
<kbd>space</kbd>: 切换行暂存状态
|
<kbd><space></kbd>: 切换行暂存状态
|
||||||
<kbd>d</kbd>: 取消变更 (git reset)
|
<kbd>d</kbd>: 取消变更 (git reset)
|
||||||
<kbd>E</kbd>: edit hunk
|
<kbd>E</kbd>: Edit hunk
|
||||||
<kbd>c</kbd>: 提交更改
|
<kbd>c</kbd>: 提交更改
|
||||||
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
||||||
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
|
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
|
||||||
|
<kbd>/</kbd>: 开始搜索
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 正常
|
## 正常
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>mouse wheel ▼</kbd>: 向下滚动 (fn+up)
|
<kbd>mouse wheel down</kbd>: 向下滚动 (fn+up)
|
||||||
<kbd>mouse wheel ▲</kbd>: 向上滚动 (fn+down)
|
<kbd>mouse wheel up</kbd>: 向上滚动 (fn+down)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 状态
|
## 状态
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 编辑配置文件
|
|
||||||
<kbd>o</kbd>: 打开配置文件
|
<kbd>o</kbd>: 打开配置文件
|
||||||
|
<kbd>e</kbd>: 编辑配置文件
|
||||||
<kbd>u</kbd>: 检查更新
|
<kbd>u</kbd>: 检查更新
|
||||||
<kbd>enter</kbd>: 切换到最近的仓库
|
<kbd><enter></kbd>: 切换到最近的仓库
|
||||||
<kbd>a</kbd>: 显示所有分支的日志
|
<kbd>a</kbd>: 显示所有分支的日志
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## 确认面板
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 确认
|
||||||
|
<kbd><esc></kbd>: 关闭
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 菜单
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 执行
|
||||||
|
<kbd><esc></kbd>: 关闭
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
## 贮藏
|
## 贮藏
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>space</kbd>: 应用
|
<kbd><space></kbd>: 应用
|
||||||
<kbd>g</kbd>: 应用并删除
|
<kbd>g</kbd>: 应用并删除
|
||||||
<kbd>d</kbd>: 删除
|
<kbd>d</kbd>: 删除
|
||||||
<kbd>n</kbd>: 新分支
|
<kbd>n</kbd>: 新分支
|
||||||
<kbd>r</kbd>: rename stash
|
<kbd>r</kbd>: Rename stash
|
||||||
<kbd>enter</kbd>: 查看提交的文件
|
<kbd><enter></kbd>: 查看提交的文件
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 远程分支
|
## 远程分支
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>ctrl+o</kbd>: 将分支名称复制到剪贴板
|
<kbd><c-o></kbd>: 将分支名称复制到剪贴板
|
||||||
<kbd>space</kbd>: 检出
|
<kbd><space></kbd>: 检出
|
||||||
<kbd>n</kbd>: 新分支
|
<kbd>n</kbd>: 新分支
|
||||||
<kbd>M</kbd>: 合并到当前检出的分支
|
<kbd>M</kbd>: 合并到当前检出的分支
|
||||||
<kbd>r</kbd>: 将已检出的分支变基到该分支
|
<kbd>r</kbd>: 将已检出的分支变基到该分支
|
||||||
<kbd>d</kbd>: 删除分支
|
<kbd>d</kbd>: 删除分支
|
||||||
<kbd>u</kbd>: 设置为检出分支的上游
|
<kbd>u</kbd>: 设置为检出分支的上游
|
||||||
<kbd>esc</kbd>: 返回远程仓库列表
|
|
||||||
<kbd>g</kbd>: 查看重置选项
|
<kbd>g</kbd>: 查看重置选项
|
||||||
<kbd>enter</kbd>: 查看提交
|
<kbd><enter></kbd>: 查看提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 远程页面
|
## 远程页面
|
||||||
@@ -294,4 +329,5 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>n</kbd>: 添加新的远程仓库
|
<kbd>n</kbd>: 添加新的远程仓库
|
||||||
<kbd>d</kbd>: 删除远程
|
<kbd>d</kbd>: 删除远程
|
||||||
<kbd>e</kbd>: 编辑远程仓库
|
<kbd>e</kbd>: 编辑远程仓库
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
</pre>
|
</pre>
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||||
|
|
||||||
|
# Lazygit 鍵盤快捷鍵
|
||||||
|
|
||||||
|
_說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||||
|
|
||||||
|
## 全局快捷鍵
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-r></kbd>: 切換到最近使用的版本庫
|
||||||
|
<kbd><pgup></kbd>: 向上捲動主面板 (fn+up/shift+k)
|
||||||
|
<kbd><pgdown></kbd>: 向下捲動主面板 (fn+down/shift+j)
|
||||||
|
<kbd>@</kbd>: 開啟命令記錄選單
|
||||||
|
<kbd>}</kbd>: 增加差異檢視中顯示變更周圍上下文的大小
|
||||||
|
<kbd>{</kbd>: 減小差異檢視中顯示變更周圍上下文的大小
|
||||||
|
<kbd>:</kbd>: 執行自訂命令
|
||||||
|
<kbd><c-p></kbd>: 檢視自訂補丁選項
|
||||||
|
<kbd>m</kbd>: 查看合併/變基選項
|
||||||
|
<kbd>R</kbd>: 重新整理
|
||||||
|
<kbd>+</kbd>: 下一個螢幕模式(常規/半螢幕/全螢幕)
|
||||||
|
<kbd>_</kbd>: 上一個螢幕模式
|
||||||
|
<kbd>?</kbd>: 開啟選單
|
||||||
|
<kbd><c-s></kbd>: 檢視篩選路徑選項
|
||||||
|
<kbd>W</kbd>: 開啟差異比較選單
|
||||||
|
<kbd><c-e></kbd>: 開啟差異比較選單
|
||||||
|
<kbd><c-w></kbd>: 切換是否在差異檢視中顯示空格變更
|
||||||
|
<kbd>z</kbd>: 復原
|
||||||
|
<kbd><c-z></kbd>: 取消復原
|
||||||
|
<kbd>P</kbd>: 推送
|
||||||
|
<kbd>p</kbd>: 拉取
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 列表面板導航
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>,</kbd>: 上一頁
|
||||||
|
<kbd>.</kbd>: 下一頁
|
||||||
|
<kbd><</kbd>: 捲動到頂部
|
||||||
|
<kbd>></kbd>: 捲動到底部
|
||||||
|
<kbd>/</kbd>: 開始搜尋
|
||||||
|
<kbd>H</kbd>: 向左捲動
|
||||||
|
<kbd>L</kbd>: 向右捲動
|
||||||
|
<kbd>]</kbd>: 下一個索引標籤
|
||||||
|
<kbd>[</kbd>: 上一個索引標籤
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Reflog
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製提交 SHA 到剪貼簿
|
||||||
|
<kbd><space></kbd>: 檢出提交
|
||||||
|
<kbd>y</kbd>: 複製提交屬性
|
||||||
|
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||||
|
<kbd>n</kbd>: 從提交建立新分支
|
||||||
|
<kbd>g</kbd>: 檢視重設選項
|
||||||
|
<kbd>c</kbd>: 複製提交 (揀選)
|
||||||
|
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||||
|
<kbd><c-r></kbd>: 重設選定的揀選 (複製) 提交
|
||||||
|
<kbd><enter></kbd>: 檢視提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 主視窗 (一般)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>mouse wheel down</kbd>: 向下捲動 (fn+up)
|
||||||
|
<kbd>mouse wheel up</kbd>: 向上捲動 (fn+down)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 主視窗 (合併中)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>e</kbd>: 編輯檔案
|
||||||
|
<kbd>o</kbd>: 開啟檔案
|
||||||
|
<kbd><left></kbd>: 選擇上一個衝突
|
||||||
|
<kbd><right></kbd>: 選擇下一個衝突
|
||||||
|
<kbd><up></kbd>: 選擇上一段
|
||||||
|
<kbd><down></kbd>: 選擇下一段
|
||||||
|
<kbd>z</kbd>: 復原
|
||||||
|
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
|
||||||
|
<kbd><space></kbd>: 挑選程式碼片段
|
||||||
|
<kbd>b</kbd>: 挑選所有程式碼片段
|
||||||
|
<kbd><esc></kbd>: 返回檔案面板
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 主視窗 (預存中)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><left></kbd>: 選擇上一段
|
||||||
|
<kbd><right></kbd>: 選擇下一段
|
||||||
|
<kbd>v</kbd>: 切換拖曳選擇
|
||||||
|
<kbd>V</kbd>: 切換拖曳選擇
|
||||||
|
<kbd>a</kbd>: 切換選擇程式碼塊
|
||||||
|
<kbd><c-o></kbd>: 複製所選文本至剪貼簿
|
||||||
|
<kbd>o</kbd>: 開啟檔案
|
||||||
|
<kbd>e</kbd>: 編輯檔案
|
||||||
|
<kbd><esc></kbd>: 返回檔案面板
|
||||||
|
<kbd><tab></kbd>: 切換至另一個面板 (已預存/未預存更改)
|
||||||
|
<kbd><space></kbd>: 切換現有行的狀態 (已預存/未預存)
|
||||||
|
<kbd>d</kbd>: 刪除變更 (git reset)
|
||||||
|
<kbd>E</kbd>: 編輯程式碼塊
|
||||||
|
<kbd>c</kbd>: 提交變更
|
||||||
|
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
|
||||||
|
<kbd>C</kbd>: 使用 git 編輯器提交變更
|
||||||
|
<kbd>/</kbd>: 開始搜尋
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 主面板 (補丁生成)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><left></kbd>: 選擇上一段
|
||||||
|
<kbd><right></kbd>: 選擇下一段
|
||||||
|
<kbd>v</kbd>: 切換拖曳選擇
|
||||||
|
<kbd>V</kbd>: 切換拖曳選擇
|
||||||
|
<kbd>a</kbd>: 切換選擇程式碼塊
|
||||||
|
<kbd><c-o></kbd>: 複製所選文本至剪貼簿
|
||||||
|
<kbd>o</kbd>: 開啟檔案
|
||||||
|
<kbd>e</kbd>: 編輯檔案
|
||||||
|
<kbd><space></kbd>: 向 (或從) 補丁中添加/刪除行
|
||||||
|
<kbd><esc></kbd>: 退出自訂補丁建立器
|
||||||
|
<kbd>/</kbd>: 開始搜尋
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 功能表
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 執行
|
||||||
|
<kbd><esc></kbd>: 關閉
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 子提交
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製提交 SHA 到剪貼簿
|
||||||
|
<kbd><space></kbd>: 檢出提交
|
||||||
|
<kbd>y</kbd>: 複製提交屬性
|
||||||
|
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||||
|
<kbd>n</kbd>: 從提交建立新分支
|
||||||
|
<kbd>g</kbd>: 檢視重設選項
|
||||||
|
<kbd>c</kbd>: 複製提交 (揀選)
|
||||||
|
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||||
|
<kbd><c-r></kbd>: 重設選定的揀選 (複製) 提交
|
||||||
|
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||||
|
<kbd>/</kbd>: 開始搜尋
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 子模組
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製子模組名稱到剪貼簿
|
||||||
|
<kbd><enter></kbd>: 進入子模組
|
||||||
|
<kbd>d</kbd>: 移除子模組
|
||||||
|
<kbd>u</kbd>: 更新子模組
|
||||||
|
<kbd>n</kbd>: 新增子模組
|
||||||
|
<kbd>e</kbd>: 更新子模組 URL
|
||||||
|
<kbd>i</kbd>: 初始化子模組
|
||||||
|
<kbd>b</kbd>: 查看批量子模組選項
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 提交
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製提交 SHA 到剪貼簿
|
||||||
|
<kbd><c-r></kbd>: 重設選定的揀選 (複製) 提交
|
||||||
|
<kbd>b</kbd>: 查看二分選項
|
||||||
|
<kbd>s</kbd>: 向下壓縮
|
||||||
|
<kbd>f</kbd>: 修復提交 (Fixup)
|
||||||
|
<kbd>r</kbd>: 改寫提交
|
||||||
|
<kbd>R</kbd>: 使用編輯器改寫提交
|
||||||
|
<kbd>d</kbd>: 刪除提交
|
||||||
|
<kbd>e</kbd>: 編輯提交
|
||||||
|
<kbd>p</kbd>: 挑選提交 (於變基過程中)
|
||||||
|
<kbd>F</kbd>: 為此提交建立修復提交
|
||||||
|
<kbd>S</kbd>: 壓縮上方所有的“fixup!”提交 (自動壓縮)
|
||||||
|
<kbd><c-j></kbd>: 向下移動提交
|
||||||
|
<kbd><c-k></kbd>: 向上移動提交
|
||||||
|
<kbd>v</kbd>: 貼上提交 (揀選)
|
||||||
|
<kbd>A</kbd>: 使用已預存的更改修正提交
|
||||||
|
<kbd>a</kbd>: 設置/重設提交作者
|
||||||
|
<kbd>t</kbd>: 還原提交
|
||||||
|
<kbd>T</kbd>: 打標籤到提交
|
||||||
|
<kbd><c-l></kbd>: 開啟記錄選單
|
||||||
|
<kbd><space></kbd>: 檢出提交
|
||||||
|
<kbd>y</kbd>: 複製提交屬性
|
||||||
|
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||||
|
<kbd>n</kbd>: 從提交建立新分支
|
||||||
|
<kbd>g</kbd>: 檢視重設選項
|
||||||
|
<kbd>c</kbd>: 複製提交 (揀選)
|
||||||
|
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||||
|
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||||
|
<kbd>/</kbd>: 開始搜尋
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 提交摘要
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 確認
|
||||||
|
<kbd><esc></kbd>: 關閉
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 提交檔案
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製提交的檔案名稱到剪貼簿
|
||||||
|
<kbd>c</kbd>: 檢出檔案
|
||||||
|
<kbd>d</kbd>: 捨棄此提交對此檔案的更改
|
||||||
|
<kbd>o</kbd>: 開啟檔案
|
||||||
|
<kbd>e</kbd>: 編輯檔案
|
||||||
|
<kbd><space></kbd>: 切換檔案是否包含在補丁中
|
||||||
|
<kbd>a</kbd>: 切換所有檔案是否包含在補丁中
|
||||||
|
<kbd><enter></kbd>: 輸入檔案以將選定的行添加至補丁(或切換目錄折疊)
|
||||||
|
<kbd>`</kbd>: 切換檔案樹狀視圖
|
||||||
|
<kbd>/</kbd>: 開始搜尋
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 收藏 (Stash)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><space></kbd>: 套用
|
||||||
|
<kbd>g</kbd>: 還原
|
||||||
|
<kbd>d</kbd>: 捨棄
|
||||||
|
<kbd>n</kbd>: 新分支
|
||||||
|
<kbd>r</kbd>: 重新命名收藏
|
||||||
|
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 本地分支
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製分支名稱到剪貼簿
|
||||||
|
<kbd>i</kbd>: 顯示 git-flow 選項
|
||||||
|
<kbd><space></kbd>: 檢出
|
||||||
|
<kbd>n</kbd>: 新分支
|
||||||
|
<kbd>o</kbd>: 建立拉取請求
|
||||||
|
<kbd>O</kbd>: 建立拉取請求選項
|
||||||
|
<kbd><c-y></kbd>: 複製拉取請求的 URL 到剪貼板
|
||||||
|
<kbd>c</kbd>: 根據名稱檢出
|
||||||
|
<kbd>F</kbd>: 強制檢出
|
||||||
|
<kbd>d</kbd>: 刪除分支
|
||||||
|
<kbd>r</kbd>: 將已檢出的分支變基至此分支
|
||||||
|
<kbd>M</kbd>: 合併到當前檢出的分支
|
||||||
|
<kbd>f</kbd>: 從上游快進此分支
|
||||||
|
<kbd>T</kbd>: 建立標籤
|
||||||
|
<kbd>g</kbd>: 檢視重設選項
|
||||||
|
<kbd>R</kbd>: 重新命名分支
|
||||||
|
<kbd>u</kbd>: 設定/取消設定上游
|
||||||
|
<kbd><enter></kbd>: 檢視提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 標籤
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><space></kbd>: 檢出
|
||||||
|
<kbd>d</kbd>: 刪除標籤
|
||||||
|
<kbd>P</kbd>: 推送標籤
|
||||||
|
<kbd>n</kbd>: 建立標籤
|
||||||
|
<kbd>g</kbd>: 檢視重設選項
|
||||||
|
<kbd><enter></kbd>: 檢視提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 檔案
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製檔案名稱到剪貼簿
|
||||||
|
<kbd>d</kbd>: 檢視“捨棄更改”的選項
|
||||||
|
<kbd><space></kbd>: 切換預存
|
||||||
|
<kbd><c-b></kbd>: 篩選檔案 (預存/未預存)
|
||||||
|
<kbd>c</kbd>: 提交變更
|
||||||
|
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
|
||||||
|
<kbd>A</kbd>: 修正上次提交
|
||||||
|
<kbd>C</kbd>: 使用 git 編輯器提交變更
|
||||||
|
<kbd>e</kbd>: 編輯檔案
|
||||||
|
<kbd>o</kbd>: 開啟檔案
|
||||||
|
<kbd>i</kbd>: 忽略或排除檔案
|
||||||
|
<kbd>r</kbd>: 重新整理檔案
|
||||||
|
<kbd>s</kbd>: 收藏所有變更
|
||||||
|
<kbd>S</kbd>: 檢視收藏選項
|
||||||
|
<kbd>a</kbd>: 全部預存/取消預存
|
||||||
|
<kbd><enter></kbd>: 選擇檔案中的單個程式碼塊/行,或展開/折疊目錄
|
||||||
|
<kbd>g</kbd>: 檢視上游重設選項
|
||||||
|
<kbd>D</kbd>: 檢視重設選項
|
||||||
|
<kbd>`</kbd>: 切換檔案樹狀視圖
|
||||||
|
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
|
||||||
|
<kbd>f</kbd>: 擷取
|
||||||
|
<kbd>/</kbd>: 開始搜尋
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 狀態
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>o</kbd>: 開啟設定檔案
|
||||||
|
<kbd>e</kbd>: 編輯設定檔案
|
||||||
|
<kbd>u</kbd>: 檢查更新
|
||||||
|
<kbd><enter></kbd>: 切換到最近使用的版本庫
|
||||||
|
<kbd>a</kbd>: 顯示所有分支日誌
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 確認面板
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><enter></kbd>: 確認
|
||||||
|
<kbd><esc></kbd>: 關閉/取消
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 遠端
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>f</kbd>: 擷取遠端
|
||||||
|
<kbd>n</kbd>: 新增遠端
|
||||||
|
<kbd>d</kbd>: 移除遠端
|
||||||
|
<kbd>e</kbd>: 編輯遠端
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 遠端分支
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd><c-o></kbd>: 複製分支名稱到剪貼簿
|
||||||
|
<kbd><space></kbd>: 檢出
|
||||||
|
<kbd>n</kbd>: 新分支
|
||||||
|
<kbd>M</kbd>: 合併到當前檢出的分支
|
||||||
|
<kbd>r</kbd>: 將已檢出的分支變基至此分支
|
||||||
|
<kbd>d</kbd>: 刪除分支
|
||||||
|
<kbd>u</kbd>: 將此分支設為當前分支之上游
|
||||||
|
<kbd>g</kbd>: 檢視重設選項
|
||||||
|
<kbd><enter></kbd>: 檢視提交
|
||||||
|
<kbd>/</kbd>: Filter the current view by text
|
||||||
|
</pre>
|
||||||
@@ -9,7 +9,7 @@ require (
|
|||||||
github.com/cli/safeexec v1.0.0
|
github.com/cli/safeexec v1.0.0
|
||||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||||
github.com/creack/pty v1.1.11
|
github.com/creack/pty v1.1.11
|
||||||
github.com/fsmiamoto/git-todo-parser v0.0.4
|
github.com/fsmiamoto/git-todo-parser v0.0.5
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
github.com/fsnotify/fsnotify v1.4.7
|
||||||
github.com/gdamore/tcell/v2 v2.6.0
|
github.com/gdamore/tcell/v2 v2.6.0
|
||||||
github.com/go-errors/errors v1.4.2
|
github.com/go-errors/errors v1.4.2
|
||||||
@@ -18,11 +18,10 @@ 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.20230324073941-36f2e87458fa
|
github.com/jesseduffield/gocui v0.3.1-0.20230719120401-398f4965241f
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||||
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/jesseduffield/yaml v2.1.0+incompatible
|
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
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
|
||||||
@@ -67,9 +66,9 @@ require (
|
|||||||
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-20210921155107-089bfa567519 // indirect
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
|
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
golang.org/x/net v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.6.0 // indirect
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
golang.org/x/term v0.6.0 // indirect
|
golang.org/x/term v0.10.0 // indirect
|
||||||
golang.org/x/text v0.8.0 // indirect
|
golang.org/x/text v0.11.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
|
|||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=
|
|
||||||
github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
|
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
|
||||||
@@ -30,8 +28,8 @@ github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9
|
|||||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/fsmiamoto/git-todo-parser v0.0.4 h1:fzcGaoAFDHWzJRKw//CSZFrXucsLKplIvOSab3FtWWM=
|
github.com/fsmiamoto/git-todo-parser v0.0.5 h1:Bhzd/vz/6Qm3udfkd6NO9fWfD3TpwR9ucp3N75/J5I8=
|
||||||
github.com/fsmiamoto/git-todo-parser v0.0.4/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas=
|
github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||||
@@ -74,16 +72,14 @@ 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.20230324073941-36f2e87458fa h1:E9G1mj94rMal1YLaABwdxLUUgKq+xGbElFjHRNaDJUg=
|
github.com/jesseduffield/gocui v0.3.1-0.20230719120401-398f4965241f h1:w/pxI34XepTAx4HwxUu8ipimbVRgSTS+7ahmgFQwH80=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20230324073941-36f2e87458fa/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
github.com/jesseduffield/gocui v0.3.1-0.20230719120401-398f4965241f/go.mod h1:dJ/BEUt3OWtaRg/PmuJWendRqREhre9JQ1SLvqrVJ8s=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||||
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=
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
|
||||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
|
||||||
github.com/jesseduffield/yaml v2.1.0+incompatible/go.mod h1:w0xGhOSIJCGYYW+hnFPTutCy5aACpkcwbmORt5axGqk=
|
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
@@ -187,8 +183,9 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
|
||||||
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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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=
|
||||||
@@ -209,21 +206,22 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.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.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
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-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/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.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||||
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/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.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
|||||||
+27
-6
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||||
@@ -22,6 +23,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/env"
|
"github.com/jesseduffield/lazygit/pkg/env"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/logs"
|
||||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +35,6 @@ type App struct {
|
|||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
OSCommand *oscommands.OSCommand
|
OSCommand *oscommands.OSCommand
|
||||||
Gui *gui.Gui
|
Gui *gui.Gui
|
||||||
Updater *updates.Updater // may only need this on the Gui
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(
|
func Run(
|
||||||
@@ -77,6 +78,18 @@ func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
|
||||||
|
if cfg.GetDebug() {
|
||||||
|
logPath, err := config.LogPath()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return logs.NewDevelopmentLogger(logPath)
|
||||||
|
} else {
|
||||||
|
return logs.NewProductionLogger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewApp bootstrap a new application
|
// NewApp bootstrap a new application
|
||||||
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
||||||
app := &App{
|
app := &App{
|
||||||
@@ -87,8 +100,7 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
|||||||
|
|
||||||
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
|
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
|
||||||
|
|
||||||
var err error
|
updater, err := updates.NewUpdater(common, config, app.OSCommand)
|
||||||
app.Updater, err = updates.NewUpdater(common, config, app.OSCommand)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
@@ -108,7 +120,12 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
|||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Gui, err = gui.NewGui(common, config, gitVersion, app.Updater, showRecentRepos, dirName)
|
// used for testing purposes
|
||||||
|
if os.Getenv("SHOW_RECENT_REPOS") == "true" {
|
||||||
|
showRecentRepos = true
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
@@ -177,7 +194,7 @@ func (app *App) setupRepo() (bool, error) {
|
|||||||
fmt.Print(app.Tr.InitialBranch)
|
fmt.Print(app.Tr.InitialBranch)
|
||||||
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||||
if trimmedResponse := strings.Trim(response, " \r\n"); len(trimmedResponse) > 0 {
|
if trimmedResponse := strings.Trim(response, " \r\n"); len(trimmedResponse) > 0 {
|
||||||
initialBranchArg += "--initial-branch=" + app.OSCommand.Quote(trimmedResponse)
|
initialBranchArg += "--initial-branch=" + trimmedResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "create":
|
case "create":
|
||||||
@@ -193,7 +210,11 @@ func (app *App) setupRepo() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if shouldInitRepo {
|
if shouldInitRepo {
|
||||||
if err := app.OSCommand.Cmd.New("git init " + initialBranchArg).Run(); err != nil {
|
args := []string{"git", "init"}
|
||||||
|
if initialBranchArg != "" {
|
||||||
|
args = append(args, initialBranchArg)
|
||||||
|
}
|
||||||
|
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
@@ -90,6 +91,15 @@ func getDaemonKind() DaemonKind {
|
|||||||
return DaemonKind(intValue)
|
return DaemonKind(intValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCommentChar() byte {
|
||||||
|
cmd := secureexec.Command("git", "config", "--get", "--null", "core.commentChar")
|
||||||
|
if output, err := cmd.Output(); err == nil && len(output) == 2 {
|
||||||
|
return output[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return '#'
|
||||||
|
}
|
||||||
|
|
||||||
// An Instruction is a command to be run by lazygit in daemon mode.
|
// An Instruction is a command to be run by lazygit in daemon mode.
|
||||||
// It is serialized to json and passed to lazygit via environment variables
|
// It is serialized to json and passed to lazygit via environment variables
|
||||||
type Instruction interface {
|
type Instruction interface {
|
||||||
@@ -199,7 +209,7 @@ func (self *ChangeTodoActionsInstruction) SerializedInstructions() string {
|
|||||||
func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
|
func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
|
||||||
return handleInteractiveRebase(common, func(path string) error {
|
return handleInteractiveRebase(common, func(path string) error {
|
||||||
for _, c := range self.Changes {
|
for _, c := range self.Changes {
|
||||||
if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction); err != nil {
|
if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction, getCommentChar()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,7 +243,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.OriginalSha, self.FixupSha)
|
return utils.MoveFixupCommitDown(path, self.OriginalSha, self.FixupSha, getCommentChar())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +267,7 @@ func (self *MoveTodoUpInstruction) SerializedInstructions() string {
|
|||||||
|
|
||||||
func (self *MoveTodoUpInstruction) run(common *common.Common) error {
|
func (self *MoveTodoUpInstruction) run(common *common.Common) error {
|
||||||
return handleInteractiveRebase(common, func(path string) error {
|
return handleInteractiveRebase(common, func(path string) error {
|
||||||
return utils.MoveTodoUp(path, self.Sha, todo.Pick)
|
return utils.MoveTodoUp(path, self.Sha, todo.Pick, getCommentChar())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +291,7 @@ func (self *MoveTodoDownInstruction) SerializedInstructions() string {
|
|||||||
|
|
||||||
func (self *MoveTodoDownInstruction) run(common *common.Common) error {
|
func (self *MoveTodoDownInstruction) run(common *common.Common) error {
|
||||||
return handleInteractiveRebase(common, func(path string) error {
|
return handleInteractiveRebase(common, func(path string) error {
|
||||||
return utils.MoveTodoDown(path, self.Sha, todo.Pick)
|
return utils.MoveTodoDown(path, self.Sha, todo.Pick, getCommentChar())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/env"
|
"github.com/jesseduffield/lazygit/pkg/env"
|
||||||
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/logs"
|
"github.com/jesseduffield/lazygit/pkg/logs/tail"
|
||||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@@ -106,7 +106,12 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cliArgs.TailLogs {
|
if cliArgs.TailLogs {
|
||||||
logs.TailLogs()
|
logPath, err := config.LogPath()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
tail.TailLogs(logPath)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newLogger(config config.AppConfigurer) *logrus.Entry {
|
|
||||||
var log *logrus.Logger
|
|
||||||
if config.GetDebug() {
|
|
||||||
log = newDevelopmentLogger()
|
|
||||||
} else {
|
|
||||||
log = newProductionLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
// highly recommended: tail -f development.log | humanlog
|
|
||||||
// https://github.com/aybabtme/humanlog
|
|
||||||
log.Formatter = &logrus.JSONFormatter{}
|
|
||||||
|
|
||||||
return log.WithFields(logrus.Fields{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newProductionLogger() *logrus.Logger {
|
|
||||||
log := logrus.New()
|
|
||||||
log.Out = io.Discard
|
|
||||||
log.SetLevel(logrus.ErrorLevel)
|
|
||||||
return log
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDevelopmentLogger() *logrus.Logger {
|
|
||||||
logger := logrus.New()
|
|
||||||
logger.SetLevel(getLogLevel())
|
|
||||||
logPath, err := config.LogPath()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to log to log file: %v", err)
|
|
||||||
}
|
|
||||||
logger.SetOutput(file)
|
|
||||||
return logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogLevel() logrus.Level {
|
|
||||||
strLevel := os.Getenv("LOG_LEVEL")
|
|
||||||
level, err := logrus.ParseLevel(strLevel)
|
|
||||||
if err != nil {
|
|
||||||
return logrus.DebugLevel
|
|
||||||
}
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/maps"
|
"github.com/jesseduffield/generics/maps"
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
@@ -183,6 +184,8 @@ func getHeader(binding *types.Binding, tr *i18n.TranslationSet) header {
|
|||||||
func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection) string {
|
func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection) string {
|
||||||
content := fmt.Sprintf("# Lazygit %s\n", tr.Keybindings)
|
content := fmt.Sprintf("# Lazygit %s\n", tr.Keybindings)
|
||||||
|
|
||||||
|
content += fmt.Sprintf("\n%s\n", italicize(tr.KeybindingsLegend))
|
||||||
|
|
||||||
for _, section := range bindingSections {
|
for _, section := range bindingSections {
|
||||||
content += formatTitle(section.title)
|
content += formatTitle(section.title)
|
||||||
content += "<pre>\n"
|
content += "<pre>\n"
|
||||||
@@ -200,13 +203,21 @@ func formatTitle(title string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func formatBinding(binding *types.Binding) string {
|
func formatBinding(binding *types.Binding) string {
|
||||||
|
result := fmt.Sprintf(" <kbd>%s</kbd>: %s", escapeAngleBrackets(keybindings.LabelFromKey(binding.Key)), binding.Description)
|
||||||
if binding.Alternative != "" {
|
if binding.Alternative != "" {
|
||||||
return fmt.Sprintf(
|
result += fmt.Sprintf(" (%s)", binding.Alternative)
|
||||||
" <kbd>%s</kbd>: %s (%s)\n",
|
|
||||||
keybindings.LabelFromKey(binding.Key),
|
|
||||||
binding.Description,
|
|
||||||
binding.Alternative,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(" <kbd>%s</kbd>: %s\n", keybindings.LabelFromKey(binding.Key), binding.Description)
|
result += "\n"
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeAngleBrackets(str string) string {
|
||||||
|
result := strings.ReplaceAll(str, ">", ">")
|
||||||
|
result = strings.ReplaceAll(result, "<", "<")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func italicize(str string) string {
|
||||||
|
return fmt.Sprintf("_%s_", str)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func TestGetBindingSections(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: []*bindingSection{
|
expected: []*bindingSection{
|
||||||
{
|
{
|
||||||
title: "Global Keybindings",
|
title: "Global keybindings",
|
||||||
bindings: []*types.Binding{
|
bindings: []*types.Binding{
|
||||||
{
|
{
|
||||||
ViewName: "",
|
ViewName: "",
|
||||||
@@ -139,7 +139,7 @@ func TestGetBindingSections(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: []*bindingSection{
|
expected: []*bindingSection{
|
||||||
{
|
{
|
||||||
title: "List Panel Navigation",
|
title: "List panel navigation",
|
||||||
bindings: []*types.Binding{
|
bindings: []*types.Binding{
|
||||||
{
|
{
|
||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
@@ -215,7 +215,7 @@ func TestGetBindingSections(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: []*bindingSection{
|
expected: []*bindingSection{
|
||||||
{
|
{
|
||||||
title: "List Panel Navigation",
|
title: "List panel navigation",
|
||||||
bindings: []*types.Binding{
|
bindings: []*types.Binding{
|
||||||
{
|
{
|
||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
|
|||||||
+7
-5
@@ -24,6 +24,7 @@ type GitCommand struct {
|
|||||||
Commit *git_commands.CommitCommands
|
Commit *git_commands.CommitCommands
|
||||||
Config *git_commands.ConfigCommands
|
Config *git_commands.ConfigCommands
|
||||||
Custom *git_commands.CustomCommands
|
Custom *git_commands.CustomCommands
|
||||||
|
Diff *git_commands.DiffCommands
|
||||||
File *git_commands.FileCommands
|
File *git_commands.FileCommands
|
||||||
Flow *git_commands.FlowCommands
|
Flow *git_commands.FlowCommands
|
||||||
Patch *git_commands.PatchCommands
|
Patch *git_commands.PatchCommands
|
||||||
@@ -112,13 +113,13 @@ func NewGitCommandAux(
|
|||||||
tagCommands := git_commands.NewTagCommands(gitCommon)
|
tagCommands := git_commands.NewTagCommands(gitCommon)
|
||||||
commitCommands := git_commands.NewCommitCommands(gitCommon)
|
commitCommands := git_commands.NewCommitCommands(gitCommon)
|
||||||
customCommands := git_commands.NewCustomCommands(gitCommon)
|
customCommands := git_commands.NewCustomCommands(gitCommon)
|
||||||
|
diffCommands := git_commands.NewDiffCommands(gitCommon)
|
||||||
fileCommands := git_commands.NewFileCommands(gitCommon)
|
fileCommands := git_commands.NewFileCommands(gitCommon)
|
||||||
submoduleCommands := git_commands.NewSubmoduleCommands(gitCommon)
|
submoduleCommands := git_commands.NewSubmoduleCommands(gitCommon)
|
||||||
workingTreeCommands := git_commands.NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
workingTreeCommands := git_commands.NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
||||||
rebaseCommands := git_commands.NewRebaseCommands(gitCommon, commitCommands, workingTreeCommands)
|
rebaseCommands := git_commands.NewRebaseCommands(gitCommon, commitCommands, workingTreeCommands)
|
||||||
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
|
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
|
||||||
// TODO: have patch builder take workingTreeCommands in its entirety
|
patchBuilder := patch.NewPatchBuilder(cmn.Log,
|
||||||
patchBuilder := patch.NewPatchBuilder(cmn.Log, workingTreeCommands.ApplyPatch,
|
|
||||||
func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
|
func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
|
||||||
// TODO: make patch builder take Gui.IgnoreWhitespaceInDiffView into
|
// TODO: make patch builder take Gui.IgnoreWhitespaceInDiffView into
|
||||||
// account. For now we just pass false.
|
// account. For now we just pass false.
|
||||||
@@ -127,9 +128,9 @@ func NewGitCommandAux(
|
|||||||
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
||||||
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
||||||
|
|
||||||
branchLoader := git_commands.NewBranchLoader(cmn, branchCommands.GetRawBranches, branchCommands.CurrentBranchInfo, configCommands)
|
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, branchCommands.CurrentBranchInfo, statusCommands.RebaseMode)
|
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode, gitCommon)
|
||||||
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
||||||
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
|
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
|
||||||
stashLoader := git_commands.NewStashLoader(cmn, cmd)
|
stashLoader := git_commands.NewStashLoader(cmn, cmd)
|
||||||
@@ -140,6 +141,7 @@ func NewGitCommandAux(
|
|||||||
Commit: commitCommands,
|
Commit: commitCommands,
|
||||||
Config: configCommands,
|
Config: configCommands,
|
||||||
Custom: customCommands,
|
Custom: customCommands,
|
||||||
|
Diff: diffCommands,
|
||||||
File: fileCommands,
|
File: fileCommands,
|
||||||
Flow: flowCommands,
|
Flow: flowCommands,
|
||||||
Patch: patchCommands,
|
Patch: patchCommands,
|
||||||
@@ -275,5 +277,5 @@ func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filenam
|
|||||||
}
|
}
|
||||||
|
|
||||||
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
|
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
|
||||||
return osCommand.Cmd.New("git rev-parse --git-dir").DontLog().Run()
|
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,8 @@ func NewGitCmdObjBuilder(log *logrus.Entry, innerBuilder *oscommands.CmdObjBuild
|
|||||||
|
|
||||||
var defaultEnvVar = "GIT_OPTIONAL_LOCKS=0"
|
var defaultEnvVar = "GIT_OPTIONAL_LOCKS=0"
|
||||||
|
|
||||||
func (self *gitCmdObjBuilder) New(cmdStr string) oscommands.ICmdObj {
|
func (self *gitCmdObjBuilder) New(args []string) oscommands.ICmdObj {
|
||||||
return self.innerBuilder.New(cmdStr).AddEnvVars(defaultEnvVar)
|
return self.innerBuilder.New(args).AddEnvVars(defaultEnvVar)
|
||||||
}
|
|
||||||
|
|
||||||
func (self *gitCmdObjBuilder) NewFromArgs(args []string) oscommands.ICmdObj {
|
|
||||||
return self.innerBuilder.NewFromArgs(args).AddEnvVars(defaultEnvVar)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
|
func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// here we're wrapping the default command runner in some git-specific stuff e.g. retry logic if we get an error due to the presence of .git/index.lock
|
// here we're wrapping the default command runner in some git-specific stuff e.g. retry logic if we get an error due to the presence of .git/index.lock
|
||||||
|
|
||||||
|
const (
|
||||||
|
WaitTime = 50 * time.Millisecond
|
||||||
|
RetryCount = 5
|
||||||
|
)
|
||||||
|
|
||||||
type gitCmdObjRunner struct {
|
type gitCmdObjRunner struct {
|
||||||
log *logrus.Entry
|
log *logrus.Entry
|
||||||
innerRunner oscommands.ICmdObjRunner
|
innerRunner oscommands.ICmdObjRunner
|
||||||
@@ -18,13 +26,44 @@ func (self *gitCmdObjRunner) Run(cmdObj oscommands.ICmdObj) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *gitCmdObjRunner) RunWithOutput(cmdObj oscommands.ICmdObj) (string, error) {
|
func (self *gitCmdObjRunner) RunWithOutput(cmdObj oscommands.ICmdObj) (string, error) {
|
||||||
return self.innerRunner.RunWithOutput(cmdObj)
|
var output string
|
||||||
|
var err error
|
||||||
|
for i := 0; i < RetryCount; i++ {
|
||||||
|
newCmdObj := cmdObj.Clone()
|
||||||
|
output, err = self.innerRunner.RunWithOutput(newCmdObj)
|
||||||
|
|
||||||
|
if err == nil || !strings.Contains(output, ".git/index.lock") {
|
||||||
|
return output, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have an error based on the index lock, we should wait a bit and then retry
|
||||||
|
self.log.Warn("index.lock prevented command from running. Retrying command after a small wait")
|
||||||
|
time.Sleep(WaitTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *gitCmdObjRunner) RunWithOutputs(cmdObj oscommands.ICmdObj) (string, string, error) {
|
func (self *gitCmdObjRunner) RunWithOutputs(cmdObj oscommands.ICmdObj) (string, string, error) {
|
||||||
return self.innerRunner.RunWithOutputs(cmdObj)
|
var stdout, stderr string
|
||||||
|
var err error
|
||||||
|
for i := 0; i < RetryCount; i++ {
|
||||||
|
newCmdObj := cmdObj.Clone()
|
||||||
|
stdout, stderr, err = self.innerRunner.RunWithOutputs(newCmdObj)
|
||||||
|
|
||||||
|
if err == nil || !strings.Contains(stdout+stderr, ".git/index.lock") {
|
||||||
|
return stdout, stderr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have an error based on the index lock, we should wait a bit and then retry
|
||||||
|
self.log.Warn("index.lock prevented command from running. Retrying command after a small wait")
|
||||||
|
time.Sleep(WaitTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout, stderr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retry logic not implemented here, but these commands typically don't need to obtain a lock.
|
||||||
func (self *gitCmdObjRunner) RunAndProcessLines(cmdObj oscommands.ICmdObj, onLine func(line string) (bool, error)) error {
|
func (self *gitCmdObjRunner) RunAndProcessLines(cmdObj oscommands.ICmdObj, onLine func(line string) (bool, error)) error {
|
||||||
return self.innerRunner.RunAndProcessLines(cmdObj, onLine)
|
return self.innerRunner.RunAndProcessLines(cmdObj, onLine)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -98,13 +97,15 @@ func (self *BisectCommands) GetInfo() *BisectInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BisectCommands) Reset() error {
|
func (self *BisectCommands) Reset() error {
|
||||||
return self.cmd.New("git bisect reset").StreamOutput().Run()
|
cmdArgs := NewGitCmd("bisect").Arg("reset").ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).StreamOutput().Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BisectCommands) Mark(ref string, term string) error {
|
func (self *BisectCommands) Mark(ref string, term string) error {
|
||||||
return self.cmd.New(
|
cmdArgs := NewGitCmd("bisect").Arg(term, ref).ToArgv()
|
||||||
fmt.Sprintf("git bisect %s %s", term, ref),
|
|
||||||
).
|
return self.cmd.New(cmdArgs).
|
||||||
IgnoreEmptyError().
|
IgnoreEmptyError().
|
||||||
StreamOutput().
|
StreamOutput().
|
||||||
Run()
|
Run()
|
||||||
@@ -115,7 +116,9 @@ func (self *BisectCommands) Skip(ref string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BisectCommands) Start() error {
|
func (self *BisectCommands) Start() error {
|
||||||
return self.cmd.New("git bisect start").StreamOutput().Run()
|
cmdArgs := NewGitCmd("bisect").Arg("start").ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).StreamOutput().Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// tells us whether we've found our problem commit(s). We return a string slice of
|
// tells us whether we've found our problem commit(s). We return a string slice of
|
||||||
@@ -137,7 +140,8 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
|
|||||||
done := false
|
done := false
|
||||||
candidates := []string{}
|
candidates := []string{}
|
||||||
|
|
||||||
err := self.cmd.New(fmt.Sprintf("git rev-list %s", newSha)).RunAndProcessLines(func(line string) (bool, error) {
|
cmdArgs := NewGitCmd("rev-list").Arg(newSha).ToArgv()
|
||||||
|
err := self.cmd.New(cmdArgs).RunAndProcessLines(func(line string) (bool, error) {
|
||||||
sha := strings.TrimSpace(line)
|
sha := strings.TrimSpace(line)
|
||||||
|
|
||||||
if status, ok := info.statusMap[sha]; ok {
|
if status, ok := info.statusMap[sha]; ok {
|
||||||
@@ -167,9 +171,11 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
|
|||||||
// bisecting is actually a descendant of our current bisect commit. If it's not, we need to
|
// bisecting is actually a descendant of our current bisect commit. If it's not, we need to
|
||||||
// render the commits from the bad commit.
|
// render the commits from the bad commit.
|
||||||
func (self *BisectCommands) ReachableFromStart(bisectInfo *BisectInfo) bool {
|
func (self *BisectCommands) ReachableFromStart(bisectInfo *BisectInfo) bool {
|
||||||
err := self.cmd.New(
|
cmdArgs := NewGitCmd("merge-base").
|
||||||
fmt.Sprintf("git merge-base --is-ancestor %s %s", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()),
|
Arg("--is-ancestor", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()).
|
||||||
).DontLog().Run()
|
ToArgv()
|
||||||
|
|
||||||
|
err := self.cmd.New(cmdArgs).DontLog().Run()
|
||||||
|
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BranchCommands struct {
|
type BranchCommands struct {
|
||||||
@@ -20,12 +21,20 @@ func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
|
|||||||
|
|
||||||
// New creates a new branch
|
// New creates a new branch
|
||||||
func (self *BranchCommands) New(name string, base string) error {
|
func (self *BranchCommands) New(name string, base string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git checkout -b %s %s", self.cmd.Quote(name), self.cmd.Quote(base))).Run()
|
cmdArgs := NewGitCmd("checkout").
|
||||||
|
Arg("-b", name, base).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentBranchInfo get the current branch information.
|
// CurrentBranchInfo get the current branch information.
|
||||||
func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||||
branchName, err := self.cmd.New("git symbolic-ref --short HEAD").DontLog().RunWithOutput()
|
branchName, err := self.cmd.New(
|
||||||
|
NewGitCmd("symbolic-ref").
|
||||||
|
Arg("--short", "HEAD").
|
||||||
|
ToArgv(),
|
||||||
|
).DontLog().RunWithOutput()
|
||||||
if err == nil && branchName != "HEAD\n" {
|
if err == nil && branchName != "HEAD\n" {
|
||||||
trimmedBranchName := strings.TrimSpace(branchName)
|
trimmedBranchName := strings.TrimSpace(branchName)
|
||||||
return BranchInfo{
|
return BranchInfo{
|
||||||
@@ -34,7 +43,11 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
|||||||
DetachedHead: false,
|
DetachedHead: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
output, err := self.cmd.New(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`).DontLog().RunWithOutput()
|
output, err := self.cmd.New(
|
||||||
|
NewGitCmd("branch").
|
||||||
|
Arg("--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)").
|
||||||
|
ToArgv(),
|
||||||
|
).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BranchInfo{}, err
|
return BranchInfo{}, err
|
||||||
}
|
}
|
||||||
@@ -59,13 +72,12 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
|||||||
|
|
||||||
// Delete delete branch
|
// Delete delete branch
|
||||||
func (self *BranchCommands) Delete(branch string, force bool) error {
|
func (self *BranchCommands) Delete(branch string, force bool) error {
|
||||||
command := "git branch -d"
|
cmdArgs := NewGitCmd("branch").
|
||||||
|
ArgIfElse(force, "-D", "-d").
|
||||||
|
Arg(branch).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
if force {
|
return self.cmd.New(cmdArgs).Run()
|
||||||
command = "git branch -D"
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.cmd.New(fmt.Sprintf("%s %s", command, self.cmd.Quote(branch))).Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
|
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
|
||||||
@@ -75,12 +87,12 @@ type CheckoutOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) Checkout(branch string, options CheckoutOptions) error {
|
func (self *BranchCommands) Checkout(branch string, options CheckoutOptions) error {
|
||||||
forceArg := ""
|
cmdArgs := NewGitCmd("checkout").
|
||||||
if options.Force {
|
ArgIf(options.Force, "--force").
|
||||||
forceArg = " --force"
|
Arg(branch).
|
||||||
}
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(fmt.Sprintf("git checkout%s %s", forceArg, self.cmd.Quote(branch))).
|
return self.cmd.New(cmdArgs).
|
||||||
// prevents git from prompting us for input which would freeze the program
|
// prevents git from prompting us for input which would freeze the program
|
||||||
// TODO: see if this is actually needed here
|
// TODO: see if this is actually needed here
|
||||||
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
||||||
@@ -100,19 +112,34 @@ func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj
|
|||||||
templateValues := map[string]string{
|
templateValues := map[string]string{
|
||||||
"branchName": self.cmd.Quote(branchName),
|
"branchName": self.cmd.Quote(branchName),
|
||||||
}
|
}
|
||||||
return self.cmd.New(utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)).DontLog()
|
|
||||||
|
resolvedTemplate := utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)
|
||||||
|
|
||||||
|
return self.cmd.New(str.ToArgv(resolvedTemplate)).DontLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) SetCurrentBranchUpstream(remoteName string, remoteBranchName string) error {
|
func (self *BranchCommands) SetCurrentBranchUpstream(remoteName string, remoteBranchName string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git branch --set-upstream-to=%s/%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName))).Run()
|
cmdArgs := NewGitCmd("branch").
|
||||||
|
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", remoteName, remoteBranchName)).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) SetUpstream(remoteName string, remoteBranchName string, branchName string) error {
|
func (self *BranchCommands) SetUpstream(remoteName string, remoteBranchName string, branchName string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git branch --set-upstream-to=%s/%s %s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName), self.cmd.Quote(branchName))).Run()
|
cmdArgs := NewGitCmd("branch").
|
||||||
|
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", remoteName, remoteBranchName)).
|
||||||
|
Arg(branchName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) UnsetUpstream(branchName string) error {
|
func (self *BranchCommands) UnsetUpstream(branchName string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git branch --unset-upstream %s", self.cmd.Quote(branchName))).Run()
|
cmdArgs := NewGitCmd("branch").Arg("--unset-upstream", branchName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
func (self *BranchCommands) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
||||||
@@ -126,29 +153,39 @@ func (self *BranchCommands) GetUpstreamDifferenceCount(branchName string) (strin
|
|||||||
// GetCommitDifferences checks how many pushables/pullables there are for the
|
// GetCommitDifferences checks how many pushables/pullables there are for the
|
||||||
// current branch
|
// current branch
|
||||||
func (self *BranchCommands) GetCommitDifferences(from, to string) (string, string) {
|
func (self *BranchCommands) GetCommitDifferences(from, to string) (string, string) {
|
||||||
command := "git rev-list %s..%s --count"
|
pushableCount, err := self.countDifferences(to, from)
|
||||||
pushableCount, err := self.cmd.New(fmt.Sprintf(command, to, from)).DontLog().RunWithOutput()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "?", "?"
|
return "?", "?"
|
||||||
}
|
}
|
||||||
pullableCount, err := self.cmd.New(fmt.Sprintf(command, from, to)).DontLog().RunWithOutput()
|
pullableCount, err := self.countDifferences(from, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "?", "?"
|
return "?", "?"
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount)
|
return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BranchCommands) countDifferences(from, to string) (string, error) {
|
||||||
|
cmdArgs := NewGitCmd("rev-list").
|
||||||
|
Arg(fmt.Sprintf("%s..%s", from, to)).
|
||||||
|
Arg("--count").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) IsHeadDetached() bool {
|
func (self *BranchCommands) IsHeadDetached() bool {
|
||||||
err := self.cmd.New("git symbolic-ref -q HEAD").DontLog().Run()
|
cmdArgs := NewGitCmd("symbolic-ref").Arg("-q", "HEAD").ToArgv()
|
||||||
|
|
||||||
|
err := self.cmd.New(cmdArgs).DontLog().Run()
|
||||||
return err != nil
|
return err != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) Rename(oldName string, newName string) error {
|
func (self *BranchCommands) Rename(oldName string, newName string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git branch --move %s %s", self.cmd.Quote(oldName), self.cmd.Quote(newName))).Run()
|
cmdArgs := NewGitCmd("branch").
|
||||||
}
|
Arg("--move", oldName, newName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
func (self *BranchCommands) GetRawBranches() (string, error) {
|
return self.cmd.New(cmdArgs).Run()
|
||||||
return self.cmd.New(`git for-each-ref --sort=-committerdate --format="%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)" refs/heads`).DontLog().RunWithOutput()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MergeOpts struct {
|
type MergeOpts struct {
|
||||||
@@ -156,19 +193,16 @@ type MergeOpts struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
||||||
mergeArg := ""
|
cmdArgs := NewGitCmd("merge").
|
||||||
if self.UserConfig.Git.Merging.Args != "" {
|
Arg("--no-edit").
|
||||||
mergeArg = " " + self.UserConfig.Git.Merging.Args
|
ArgIf(self.UserConfig.Git.Merging.Args != "", self.UserConfig.Git.Merging.Args).
|
||||||
}
|
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||||
|
Arg(branchName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
command := fmt.Sprintf("git merge --no-edit%s %s", mergeArg, self.cmd.Quote(branchName))
|
return self.cmd.New(cmdArgs).Run()
|
||||||
if opts.FastForwardOnly {
|
|
||||||
command = fmt.Sprintf("%s --ff-only", command)
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.cmd.New(command).Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
|
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New(self.UserConfig.Git.AllBranchesLogCmd).DontLog()
|
return self.cmd.New(str.ToArgv(self.UserConfig.Git.AllBranchesLogCmd)).DontLog()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -8,8 +9,10 @@ import (
|
|||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/go-git/v5/config"
|
"github.com/jesseduffield/go-git/v5/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// context:
|
// context:
|
||||||
@@ -36,20 +39,20 @@ type BranchInfo struct {
|
|||||||
// BranchLoader returns a list of Branch objects for the current repo
|
// BranchLoader returns a list of Branch objects for the current repo
|
||||||
type BranchLoader struct {
|
type BranchLoader struct {
|
||||||
*common.Common
|
*common.Common
|
||||||
getRawBranches func() (string, error)
|
cmd oscommands.ICmdObjBuilder
|
||||||
getCurrentBranchInfo func() (BranchInfo, error)
|
getCurrentBranchInfo func() (BranchInfo, error)
|
||||||
config BranchLoaderConfigCommands
|
config BranchLoaderConfigCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBranchLoader(
|
func NewBranchLoader(
|
||||||
cmn *common.Common,
|
cmn *common.Common,
|
||||||
getRawBranches func() (string, error),
|
cmd oscommands.ICmdObjBuilder,
|
||||||
getCurrentBranchInfo func() (BranchInfo, error),
|
getCurrentBranchInfo func() (BranchInfo, error),
|
||||||
config BranchLoaderConfigCommands,
|
config BranchLoaderConfigCommands,
|
||||||
) *BranchLoader {
|
) *BranchLoader {
|
||||||
return &BranchLoader{
|
return &BranchLoader{
|
||||||
Common: cmn,
|
Common: cmn,
|
||||||
getRawBranches: getRawBranches,
|
cmd: cmd,
|
||||||
getCurrentBranchInfo: getCurrentBranchInfo,
|
getCurrentBranchInfo: getCurrentBranchInfo,
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
@@ -128,8 +131,8 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
split := strings.Split(line, "\x00")
|
split := strings.Split(line, "\x00")
|
||||||
if len(split) != 4 {
|
if len(split) != len(branchFields) {
|
||||||
// Ignore line if it isn't separated into 4 parts
|
// Ignore line if it isn't separated into the expected number of parts
|
||||||
// This is probably a warning message, for more info see:
|
// This is probably a warning message, for more info see:
|
||||||
// https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439
|
// https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439
|
||||||
return nil, false
|
return nil, false
|
||||||
@@ -139,47 +142,81 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain branch information from parsed line output of getRawBranches()
|
func (self *BranchLoader) getRawBranches() (string, error) {
|
||||||
// split contains the '|' separated tokens in the line of output
|
format := strings.Join(
|
||||||
func obtainBranch(split []string) *models.Branch {
|
lo.Map(branchFields, func(thing string, _ int) string {
|
||||||
name := strings.TrimPrefix(split[1], "heads/")
|
return "%(" + thing + ")"
|
||||||
branch := &models.Branch{
|
}),
|
||||||
Name: name,
|
"%00",
|
||||||
Pullables: "?",
|
)
|
||||||
Pushables: "?",
|
|
||||||
Head: split[0] == "*",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
cmdArgs := NewGitCmd("for-each-ref").
|
||||||
|
Arg("--sort=-committerdate").
|
||||||
|
Arg(fmt.Sprintf("--format=%s", format)).
|
||||||
|
Arg("refs/heads").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
|
}
|
||||||
|
|
||||||
|
var branchFields = []string{
|
||||||
|
"HEAD",
|
||||||
|
"refname:short",
|
||||||
|
"upstream:short",
|
||||||
|
"upstream:track",
|
||||||
|
"subject",
|
||||||
|
fmt.Sprintf("objectname:short=%d", utils.COMMIT_HASH_SHORT_SIZE),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain branch information from parsed line output of getRawBranches()
|
||||||
|
func obtainBranch(split []string) *models.Branch {
|
||||||
|
headMarker := split[0]
|
||||||
|
fullName := split[1]
|
||||||
upstreamName := split[2]
|
upstreamName := split[2]
|
||||||
|
track := split[3]
|
||||||
|
subject := split[4]
|
||||||
|
commitHash := split[5]
|
||||||
|
|
||||||
|
name := strings.TrimPrefix(fullName, "heads/")
|
||||||
|
pushables, pullables, gone := parseUpstreamInfo(upstreamName, track)
|
||||||
|
|
||||||
|
return &models.Branch{
|
||||||
|
Name: name,
|
||||||
|
Pushables: pushables,
|
||||||
|
Pullables: pullables,
|
||||||
|
UpstreamGone: gone,
|
||||||
|
Head: headMarker == "*",
|
||||||
|
Subject: subject,
|
||||||
|
CommitHash: commitHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUpstreamInfo(upstreamName string, track string) (string, string, bool) {
|
||||||
if upstreamName == "" {
|
if upstreamName == "" {
|
||||||
// if we're here then it means we do not have a local version of the remote.
|
// if we're here then it means we do not have a local version of the remote.
|
||||||
// The branch might still be tracking a remote though, we just don't know
|
// The branch might still be tracking a remote though, we just don't know
|
||||||
// how many commits ahead/behind it is
|
// how many commits ahead/behind it is
|
||||||
return branch
|
return "?", "?", false
|
||||||
}
|
}
|
||||||
|
|
||||||
track := split[3]
|
|
||||||
if track == "[gone]" {
|
if track == "[gone]" {
|
||||||
branch.UpstreamGone = true
|
return "?", "?", true
|
||||||
} else {
|
|
||||||
re := regexp.MustCompile(`ahead (\d+)`)
|
|
||||||
match := re.FindStringSubmatch(track)
|
|
||||||
if len(match) > 1 {
|
|
||||||
branch.Pushables = match[1]
|
|
||||||
} else {
|
|
||||||
branch.Pushables = "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
re = regexp.MustCompile(`behind (\d+)`)
|
|
||||||
match = re.FindStringSubmatch(track)
|
|
||||||
if len(match) > 1 {
|
|
||||||
branch.Pullables = match[1]
|
|
||||||
} else {
|
|
||||||
branch.Pullables = "0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return branch
|
pushables := parseDifference(track, `ahead (\d+)`)
|
||||||
|
pullables := parseDifference(track, `behind (\d+)`)
|
||||||
|
|
||||||
|
return pushables, pullables, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDifference(track string, regexStr string) string {
|
||||||
|
re := regexp.MustCompile(regexStr)
|
||||||
|
match := re.FindStringSubmatch(track)
|
||||||
|
if len(match) > 1 {
|
||||||
|
return match[1]
|
||||||
|
} else {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: only look at the new reflog commits, and otherwise store the recencies in
|
// TODO: only look at the new reflog commits, and otherwise store the recencies in
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestObtainBanch(t *testing.T) {
|
func TestObtainBranch(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
input []string
|
input []string
|
||||||
@@ -17,29 +17,65 @@ func TestObtainBanch(t *testing.T) {
|
|||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
testName: "TrimHeads",
|
testName: "TrimHeads",
|
||||||
input: []string{"", "heads/a_branch", "", ""},
|
input: []string{"", "heads/a_branch", "", "", "subject", "123"},
|
||||||
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false},
|
expectedBranch: &models.Branch{
|
||||||
|
Name: "a_branch",
|
||||||
|
Pushables: "?",
|
||||||
|
Pullables: "?",
|
||||||
|
Head: false,
|
||||||
|
Subject: "subject",
|
||||||
|
CommitHash: "123",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "NoUpstream",
|
testName: "NoUpstream",
|
||||||
input: []string{"", "a_branch", "", ""},
|
input: []string{"", "a_branch", "", "", "subject", "123"},
|
||||||
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false},
|
expectedBranch: &models.Branch{
|
||||||
|
Name: "a_branch",
|
||||||
|
Pushables: "?",
|
||||||
|
Pullables: "?",
|
||||||
|
Head: false,
|
||||||
|
Subject: "subject",
|
||||||
|
CommitHash: "123",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "IsHead",
|
testName: "IsHead",
|
||||||
input: []string{"*", "a_branch", "", ""},
|
input: []string{"*", "a_branch", "", "", "subject", "123"},
|
||||||
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: true},
|
expectedBranch: &models.Branch{
|
||||||
|
Name: "a_branch",
|
||||||
|
Pushables: "?",
|
||||||
|
Pullables: "?",
|
||||||
|
Head: true,
|
||||||
|
Subject: "subject",
|
||||||
|
CommitHash: "123",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "IsBehindAndAhead",
|
testName: "IsBehindAndAhead",
|
||||||
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]"},
|
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123"},
|
||||||
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "3", Pullables: "2", Head: false},
|
expectedBranch: &models.Branch{
|
||||||
|
Name: "a_branch",
|
||||||
|
Pushables: "3",
|
||||||
|
Pullables: "2",
|
||||||
|
Head: false,
|
||||||
|
Subject: "subject",
|
||||||
|
CommitHash: "123",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "RemoteBranchIsGone",
|
testName: "RemoteBranchIsGone",
|
||||||
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]"},
|
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123"},
|
||||||
expectedBranch: &models.Branch{Name: "a_branch", UpstreamGone: true, Pushables: "?", Pullables: "?", Head: false},
|
expectedBranch: &models.Branch{
|
||||||
|
Name: "a_branch",
|
||||||
|
UpstreamGone: true,
|
||||||
|
Pushables: "?",
|
||||||
|
Pullables: "?",
|
||||||
|
Head: false,
|
||||||
|
Subject: "subject",
|
||||||
|
CommitHash: "123",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"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/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,21 +21,21 @@ func TestBranchGetCommitDifferences(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Can't retrieve pushable count",
|
"Can't retrieve pushable count",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect("git rev-list @{u}..HEAD --count", "", errors.New("error")),
|
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "", errors.New("error")),
|
||||||
"?", "?",
|
"?", "?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Can't retrieve pullable count",
|
"Can't retrieve pullable count",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect("git rev-list @{u}..HEAD --count", "1\n", nil).
|
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "1\n", nil).
|
||||||
Expect("git rev-list HEAD..@{u} --count", "", errors.New("error")),
|
ExpectGitArgs([]string{"rev-list", "HEAD..@{u}", "--count"}, "", errors.New("error")),
|
||||||
"?", "?",
|
"?", "?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Retrieve pullable and pushable count",
|
"Retrieve pullable and pushable count",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect("git rev-list @{u}..HEAD --count", "1\n", nil).
|
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "1\n", nil).
|
||||||
Expect("git rev-list HEAD..@{u} --count", "2\n", nil),
|
ExpectGitArgs([]string{"rev-list", "HEAD..@{u}", "--count"}, "2\n", nil),
|
||||||
"1", "2",
|
"1", "2",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ func TestBranchGetCommitDifferences(t *testing.T) {
|
|||||||
|
|
||||||
func TestBranchNewBranch(t *testing.T) {
|
func TestBranchNewBranch(t *testing.T) {
|
||||||
runner := oscommands.NewFakeRunner(t).
|
runner := oscommands.NewFakeRunner(t).
|
||||||
Expect(`git checkout -b "test" "refs/heads/master"`, "", nil)
|
ExpectGitArgs([]string{"checkout", "-b", "test", "refs/heads/master"}, "", nil)
|
||||||
instance := buildBranchCommands(commonDeps{runner: runner})
|
instance := buildBranchCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
assert.NoError(t, instance.New("test", "refs/heads/master"))
|
assert.NoError(t, instance.New("test", "refs/heads/master"))
|
||||||
@@ -72,7 +73,7 @@ func TestBranchDeleteBranch(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Delete a branch",
|
"Delete a branch",
|
||||||
false,
|
false,
|
||||||
oscommands.NewFakeRunner(t).Expect(`git 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)
|
||||||
},
|
},
|
||||||
@@ -80,7 +81,7 @@ func TestBranchDeleteBranch(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Force delete a branch",
|
"Force delete a branch",
|
||||||
true,
|
true,
|
||||||
oscommands.NewFakeRunner(t).Expect(`git 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)
|
||||||
},
|
},
|
||||||
@@ -99,12 +100,53 @@ func TestBranchDeleteBranch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBranchMerge(t *testing.T) {
|
func TestBranchMerge(t *testing.T) {
|
||||||
runner := oscommands.NewFakeRunner(t).
|
scenarios := []struct {
|
||||||
Expect(`git merge --no-edit "test"`, "", nil)
|
testName string
|
||||||
instance := buildBranchCommands(commonDeps{runner: runner})
|
userConfig *config.UserConfig
|
||||||
|
opts MergeOpts
|
||||||
|
branchName string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "basic",
|
||||||
|
userConfig: &config.UserConfig{},
|
||||||
|
opts: MergeOpts{},
|
||||||
|
branchName: "mybranch",
|
||||||
|
expected: []string{"merge", "--no-edit", "mybranch"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "merging args",
|
||||||
|
userConfig: &config.UserConfig{
|
||||||
|
Git: config.GitConfig{
|
||||||
|
Merging: config.MergingConfig{
|
||||||
|
Args: "--merging-args", // it's up to the user what they put here
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
opts: MergeOpts{},
|
||||||
|
branchName: "mybranch",
|
||||||
|
expected: []string{"merge", "--no-edit", "--merging-args", "mybranch"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "fast forward only",
|
||||||
|
userConfig: &config.UserConfig{},
|
||||||
|
opts: MergeOpts{FastForwardOnly: true},
|
||||||
|
branchName: "mybranch",
|
||||||
|
expected: []string{"merge", "--no-edit", "--ff-only", "mybranch"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
assert.NoError(t, instance.Merge("test", MergeOpts{}))
|
for _, s := range scenarios {
|
||||||
runner.CheckForMissingCalls()
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
runner := oscommands.NewFakeRunner(t).
|
||||||
|
ExpectGitArgs(s.expected, "", nil)
|
||||||
|
instance := buildBranchCommands(commonDeps{runner: runner, userConfig: s.userConfig})
|
||||||
|
|
||||||
|
assert.NoError(t, instance.Merge(s.branchName, s.opts))
|
||||||
|
runner.CheckForMissingCalls()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBranchCheckout(t *testing.T) {
|
func TestBranchCheckout(t *testing.T) {
|
||||||
@@ -118,7 +160,7 @@ func TestBranchCheckout(t *testing.T) {
|
|||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"Checkout",
|
"Checkout",
|
||||||
oscommands.NewFakeRunner(t).Expect(`git checkout "test"`, "", nil),
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"checkout", "test"}, "", nil),
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -126,7 +168,7 @@ func TestBranchCheckout(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Checkout forced",
|
"Checkout forced",
|
||||||
oscommands.NewFakeRunner(t).Expect(`git checkout --force "test"`, "", nil),
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"checkout", "--force", "test"}, "", nil),
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -172,7 +214,7 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
|
|||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"says we are on the master branch if we are",
|
"says we are on the master branch if we are",
|
||||||
oscommands.NewFakeRunner(t).Expect(`git symbolic-ref --short HEAD`, "master", nil),
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "master", nil),
|
||||||
func(info BranchInfo, err error) {
|
func(info BranchInfo, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, "master", info.RefName)
|
assert.EqualValues(t, "master", info.RefName)
|
||||||
@@ -183,8 +225,9 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"falls back to git `git branch --points-at=HEAD` if symbolic-ref fails",
|
"falls back to git `git branch --points-at=HEAD` if symbolic-ref fails",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
|
||||||
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "*\x006f71c57a8d4bd6c11399c3f55f42c815527a73a4\x00(HEAD detached at 6f71c57a)\n", nil),
|
ExpectGitArgs([]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"},
|
||||||
|
"*\x006f71c57a8d4bd6c11399c3f55f42c815527a73a4\x00(HEAD detached at 6f71c57a)\n", nil),
|
||||||
func(info BranchInfo, err error) {
|
func(info BranchInfo, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, "6f71c57a8d4bd6c11399c3f55f42c815527a73a4", info.RefName)
|
assert.EqualValues(t, "6f71c57a8d4bd6c11399c3f55f42c815527a73a4", info.RefName)
|
||||||
@@ -195,9 +238,9 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"handles a detached head (LANG=zh_CN.UTF-8)",
|
"handles a detached head (LANG=zh_CN.UTF-8)",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
|
||||||
Expect(
|
ExpectGitArgs(
|
||||||
`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`,
|
[]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"},
|
||||||
"*\x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00(头指针在 679b0456 分离)\n"+
|
"*\x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00(头指针在 679b0456 分离)\n"+
|
||||||
" \x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00refs/heads/master\n",
|
" \x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00refs/heads/master\n",
|
||||||
nil),
|
nil),
|
||||||
@@ -211,8 +254,8 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"bubbles up error if there is one",
|
"bubbles up error if there is one",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
|
||||||
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "", errors.New("error")),
|
ExpectGitArgs([]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"}, "", errors.New("error")),
|
||||||
func(info BranchInfo, err error) {
|
func(info BranchInfo, err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.EqualValues(t, "", info.RefName)
|
assert.EqualValues(t, "", info.RefName)
|
||||||
|
|||||||
@@ -22,18 +22,27 @@ func NewCommitCommands(gitCommon *GitCommon) *CommitCommands {
|
|||||||
|
|
||||||
// ResetAuthor resets the author of the topmost commit
|
// ResetAuthor resets the author of the topmost commit
|
||||||
func (self *CommitCommands) ResetAuthor() error {
|
func (self *CommitCommands) ResetAuthor() error {
|
||||||
return self.cmd.New("git commit --allow-empty --only --no-edit --amend --reset-author").Run()
|
cmdArgs := NewGitCmd("commit").
|
||||||
|
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--reset-author").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the commit's author to the supplied value. Value is expected to be of the form 'Name <Email>'
|
// Sets the commit's author to the supplied value. Value is expected to be of the form 'Name <Email>'
|
||||||
func (self *CommitCommands) SetAuthor(value string) error {
|
func (self *CommitCommands) SetAuthor(value string) error {
|
||||||
commandStr := fmt.Sprintf("git commit --allow-empty --only --no-edit --amend --author=%s", self.cmd.Quote(value))
|
cmdArgs := NewGitCmd("commit").
|
||||||
return self.cmd.New(commandStr).Run()
|
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--author="+value).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetToCommit reset to commit
|
// ResetToCommit reset to commit
|
||||||
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
|
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git reset --%s %s", strength, sha)).
|
cmdArgs := NewGitCmd("reset").Arg("--"+strength, sha).ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).
|
||||||
// prevents git from prompting us for input which would freeze the program
|
// prevents git from prompting us for input which would freeze the program
|
||||||
// TODO: see if this is actually needed here
|
// TODO: see if this is actually needed here
|
||||||
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
||||||
@@ -45,70 +54,82 @@ func (self *CommitCommands) CommitCmdObj(message string) oscommands.ICmdObj {
|
|||||||
messageArgs := self.commitMessageArgs(message)
|
messageArgs := self.commitMessageArgs(message)
|
||||||
|
|
||||||
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
|
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
|
||||||
noVerifyFlag := ""
|
|
||||||
if skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix) {
|
|
||||||
noVerifyFlag = " --no-verify"
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.cmd.New(fmt.Sprintf("git commit%s%s%s", noVerifyFlag, self.signoffFlag(), messageArgs))
|
cmdArgs := NewGitCmd("commit").
|
||||||
|
ArgIf(skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix), "--no-verify").
|
||||||
|
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
|
||||||
|
Arg(messageArgs...).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitCommands) RewordLastCommitInEditorCmdObj() oscommands.ICmdObj {
|
||||||
|
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
|
||||||
}
|
}
|
||||||
|
|
||||||
// RewordLastCommit rewords the topmost commit with the given message
|
// RewordLastCommit rewords the topmost commit with the given message
|
||||||
func (self *CommitCommands) RewordLastCommit(message string) error {
|
func (self *CommitCommands) RewordLastCommit(message string) error {
|
||||||
messageArgs := self.commitMessageArgs(message)
|
messageArgs := self.commitMessageArgs(message)
|
||||||
return self.cmd.New(fmt.Sprintf("git commit --allow-empty --amend --only%s", messageArgs)).Run()
|
|
||||||
|
cmdArgs := NewGitCmd("commit").
|
||||||
|
Arg("--allow-empty", "--amend", "--only").
|
||||||
|
Arg(messageArgs...).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) commitMessageArgs(message string) string {
|
func (self *CommitCommands) commitMessageArgs(message string) []string {
|
||||||
msg, description, _ := strings.Cut(message, "\n")
|
msg, description, _ := strings.Cut(message, "\n")
|
||||||
descriptionArgs := ""
|
args := []string{"-m", msg}
|
||||||
|
|
||||||
if description != "" {
|
if description != "" {
|
||||||
descriptionArgs = fmt.Sprintf(" -m %s", self.cmd.Quote(description))
|
args = append(args, "-m", description)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(" -m %s%s", self.cmd.Quote(msg), descriptionArgs)
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs git commit without the -m argument meaning it will invoke the user's editor
|
// runs git commit without the -m argument meaning it will invoke the user's editor
|
||||||
func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
|
func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New(fmt.Sprintf("git commit%s%s", self.signoffFlag(), self.verboseFlag()))
|
cmdArgs := NewGitCmd("commit").
|
||||||
|
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) verboseFlag() string {
|
|
||||||
switch self.config.UserConfig.Git.Commit.Verbose {
|
|
||||||
case "always":
|
|
||||||
return " --verbose"
|
|
||||||
case "never":
|
|
||||||
return " --no-verbose"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the subject of the HEAD commit
|
// Get the subject of the HEAD commit
|
||||||
func (self *CommitCommands) GetHeadCommitMessage() (string, error) {
|
func (self *CommitCommands) GetHeadCommitMessage() (string, error) {
|
||||||
message, err := self.cmd.New("git log -1 --pretty=%s").DontLog().RunWithOutput()
|
cmdArgs := NewGitCmd("log").Arg("-1", "--pretty=%s").ToArgv()
|
||||||
|
|
||||||
|
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
return strings.TrimSpace(message), err
|
return strings.TrimSpace(message), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
|
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
|
||||||
cmdStr := "git rev-list --format=%B --max-count=1 " + commitSha
|
cmdArgs := NewGitCmd("rev-list").
|
||||||
messageWithHeader, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
Arg("--format=%B", "--max-count=1", commitSha).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
messageWithHeader, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
|
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
|
||||||
return strings.TrimSpace(message), err
|
return strings.TrimSpace(message), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
|
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
|
||||||
cmdStr := "git show --no-color " + commitSha
|
cmdArgs := NewGitCmd("show").Arg("--no-color", commitSha).ToArgv()
|
||||||
diff, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
|
||||||
|
diff, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
return diff, err
|
return diff, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +139,11 @@ type Author struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
|
func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
|
||||||
cmdStr := "git show --no-patch --pretty=format:'%an%x00%ae' " + commitSha
|
cmdArgs := NewGitCmd("show").
|
||||||
output, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitSha).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Author{}, err
|
return Author{}, err
|
||||||
}
|
}
|
||||||
@@ -138,15 +162,21 @@ func (self *CommitCommands) GetCommitMessageFirstLine(sha string) (string, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, error) {
|
func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, error) {
|
||||||
return self.cmd.New(
|
cmdArgs := NewGitCmd("show").
|
||||||
fmt.Sprintf("git show --no-patch --pretty=format:%%s %s", strings.Join(shas, " ")),
|
Arg("--no-patch", "--pretty=format:%s").
|
||||||
).DontLog().RunWithOutput()
|
Arg(shas...).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
|
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
|
||||||
return self.cmd.New(
|
cmdArgs := NewGitCmd("show").
|
||||||
fmt.Sprintf("git show --no-patch --oneline %s", strings.Join(shas, " ")),
|
Arg("--no-patch", "--oneline").
|
||||||
).DontLog().RunWithOutput()
|
Arg(shas...).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AmendHead amends HEAD with whatever is staged in your working tree
|
// AmendHead amends HEAD with whatever is staged in your working tree
|
||||||
@@ -155,42 +185,58 @@ func (self *CommitCommands) AmendHead() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
|
func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New("git commit --amend --no-edit --allow-empty")
|
cmdArgs := NewGitCmd("commit").
|
||||||
|
Arg("--amend", "--no-edit", "--allow-empty").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
|
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||||
contextSize := self.UserConfig.Git.DiffContextSize
|
contextSize := self.UserConfig.Git.DiffContextSize
|
||||||
filterPathArg := ""
|
|
||||||
if filterPath != "" {
|
|
||||||
filterPathArg = fmt.Sprintf(" -- %s", self.cmd.Quote(filterPath))
|
|
||||||
}
|
|
||||||
ignoreWhitespaceArg := ""
|
|
||||||
if ignoreWhitespace {
|
|
||||||
ignoreWhitespaceArg = " --ignore-all-space"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdStr := fmt.Sprintf("git show --submodule --color=%s --unified=%d --stat -p %s%s%s",
|
cmdArgs := NewGitCmd("show").
|
||||||
self.UserConfig.Git.Paging.ColorArg, contextSize, sha, ignoreWhitespaceArg, filterPathArg)
|
Arg("--submodule").
|
||||||
return self.cmd.New(cmdStr).DontLog()
|
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
|
||||||
|
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||||
|
Arg("--stat").
|
||||||
|
Arg("--decorate").
|
||||||
|
Arg("-p").
|
||||||
|
Arg(sha).
|
||||||
|
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||||
|
ArgIf(filterPath != "", "--", filterPath).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert reverts the selected commit by sha
|
// Revert reverts the selected commit by sha
|
||||||
func (self *CommitCommands) Revert(sha string) error {
|
func (self *CommitCommands) Revert(sha string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git revert %s", sha)).Run()
|
cmdArgs := NewGitCmd("revert").Arg(sha).ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) RevertMerge(sha string, parentNumber int) error {
|
func (self *CommitCommands) RevertMerge(sha string, parentNumber int) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git revert %s -m %d", sha, parentNumber)).Run()
|
cmdArgs := NewGitCmd("revert").Arg(sha, "-m", fmt.Sprintf("%d", parentNumber)).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFixupCommit creates a commit that fixes up a previous commit
|
// CreateFixupCommit creates a commit that fixes up a previous commit
|
||||||
func (self *CommitCommands) CreateFixupCommit(sha string) error {
|
func (self *CommitCommands) CreateFixupCommit(sha string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git commit --fixup=%s", sha)).Run()
|
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + sha).ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// a value of 0 means the head commit, 1 is the parent commit, etc
|
// a value of 0 means the head commit, 1 is the parent commit, etc
|
||||||
func (self *CommitCommands) GetCommitMessageFromHistory(value int) (string, error) {
|
func (self *CommitCommands) GetCommitMessageFromHistory(value int) (string, error) {
|
||||||
hash, _ := self.cmd.New(fmt.Sprintf("git log -1 --skip=%d --pretty=%%H", value)).DontLog().RunWithOutput()
|
cmdArgs := NewGitCmd("log").Arg("-1", fmt.Sprintf("--skip=%d", value), "--pretty=%H").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
hash, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
formattedHash := strings.TrimSpace(hash)
|
formattedHash := strings.TrimSpace(hash)
|
||||||
if len(formattedHash) == 0 {
|
if len(formattedHash) == 0 {
|
||||||
return "", ErrInvalidCommitIndex
|
return "", ErrInvalidCommitIndex
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
@@ -25,12 +24,18 @@ func NewCommitFileLoader(common *common.Common, cmd oscommands.ICmdObjBuilder) *
|
|||||||
|
|
||||||
// GetFilesInDiff get the specified commit files
|
// GetFilesInDiff get the specified commit files
|
||||||
func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) {
|
func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) {
|
||||||
reverseFlag := ""
|
cmdArgs := NewGitCmd("diff").
|
||||||
if reverse {
|
Arg("--submodule").
|
||||||
reverseFlag = " -R "
|
Arg("--no-ext-diff").
|
||||||
}
|
Arg("--name-status").
|
||||||
|
Arg("-z").
|
||||||
|
Arg("--no-renames").
|
||||||
|
ArgIf(reverse, "-R").
|
||||||
|
Arg(from).
|
||||||
|
Arg(to).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
filenames, err := self.cmd.New(fmt.Sprintf("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)).DontLog().RunWithOutput()
|
filenames, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// context:
|
// context:
|
||||||
@@ -28,11 +29,16 @@ type CommitLoader struct {
|
|||||||
*common.Common
|
*common.Common
|
||||||
cmd oscommands.ICmdObjBuilder
|
cmd oscommands.ICmdObjBuilder
|
||||||
|
|
||||||
getCurrentBranchInfo func() (BranchInfo, error)
|
getRebaseMode func() (enums.RebaseMode, error)
|
||||||
getRebaseMode func() (enums.RebaseMode, error)
|
readFile func(filename string) ([]byte, error)
|
||||||
readFile func(filename string) ([]byte, error)
|
walkFiles func(root string, fn filepath.WalkFunc) error
|
||||||
walkFiles func(root string, fn filepath.WalkFunc) error
|
dotGitDir string
|
||||||
dotGitDir string
|
// List of main branches that exist in the repo.
|
||||||
|
// We use these to obtain the merge base of the branch.
|
||||||
|
// When nil, we're yet to obtain the list of existing main branches.
|
||||||
|
// When an empty slice, we've obtained the list and it's empty.
|
||||||
|
mainBranches []string
|
||||||
|
*GitCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
// making our dependencies explicit for the sake of easier testing
|
// making our dependencies explicit for the sake of easier testing
|
||||||
@@ -40,17 +46,18 @@ func NewCommitLoader(
|
|||||||
cmn *common.Common,
|
cmn *common.Common,
|
||||||
cmd oscommands.ICmdObjBuilder,
|
cmd oscommands.ICmdObjBuilder,
|
||||||
dotGitDir string,
|
dotGitDir string,
|
||||||
getCurrentBranchInfo func() (BranchInfo, error),
|
|
||||||
getRebaseMode func() (enums.RebaseMode, error),
|
getRebaseMode func() (enums.RebaseMode, error),
|
||||||
|
gitCommon *GitCommon,
|
||||||
) *CommitLoader {
|
) *CommitLoader {
|
||||||
return &CommitLoader{
|
return &CommitLoader{
|
||||||
Common: cmn,
|
Common: cmn,
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
getCurrentBranchInfo: getCurrentBranchInfo,
|
getRebaseMode: getRebaseMode,
|
||||||
getRebaseMode: getRebaseMode,
|
readFile: os.ReadFile,
|
||||||
readFile: os.ReadFile,
|
walkFiles: filepath.Walk,
|
||||||
walkFiles: filepath.Walk,
|
dotGitDir: dotGitDir,
|
||||||
dotGitDir: dotGitDir,
|
mainBranches: nil,
|
||||||
|
GitCommon: gitCommon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +108,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
|||||||
return commits, nil
|
return commits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commits, err = self.setCommitMergedStatuses(opts.RefName, commits)
|
commits = self.setCommitMergedStatuses(opts.RefName, commits)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return commits, nil
|
return commits, nil
|
||||||
}
|
}
|
||||||
@@ -158,11 +162,17 @@ func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
|
|||||||
tags := []string{}
|
tags := []string{}
|
||||||
|
|
||||||
if extraInfo != "" {
|
if extraInfo != "" {
|
||||||
re := regexp.MustCompile(`tag: ([^,\)]+)`)
|
extraInfoFields := strings.Split(extraInfo, ",")
|
||||||
tagMatch := re.FindStringSubmatch(extraInfo)
|
for _, extraInfoField := range extraInfoFields {
|
||||||
if len(tagMatch) > 1 {
|
extraInfoField = strings.TrimSpace(extraInfoField)
|
||||||
tags = append(tags, tagMatch[1])
|
re := regexp.MustCompile(`tag: (.+)`)
|
||||||
|
tagMatch := re.FindStringSubmatch(extraInfoField)
|
||||||
|
if len(tagMatch) > 1 {
|
||||||
|
tags = append(tags, tagMatch[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extraInfo = "(" + extraInfo + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
unitTimestampInt, _ := strconv.Atoi(unixTimestamp)
|
unitTimestampInt, _ := strconv.Atoi(unixTimestamp)
|
||||||
@@ -201,12 +211,11 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
|||||||
// note that we're not filtering these as we do non-rebasing commits just because
|
// note that we're not filtering these as we do non-rebasing commits just because
|
||||||
// I suspect that will cause some damage
|
// I suspect that will cause some damage
|
||||||
cmdObj := self.cmd.New(
|
cmdObj := self.cmd.New(
|
||||||
fmt.Sprintf(
|
NewGitCmd("show").
|
||||||
"git -c log.showSignature=false show %s --no-patch --oneline %s --abbrev=%d",
|
Config("log.showSignature=false").
|
||||||
strings.Join(commitShas, " "),
|
Arg("--no-patch", "--oneline", "--abbrev=20", prettyFormat).
|
||||||
prettyFormat,
|
Arg(commitShas...).
|
||||||
20,
|
ToArgv(),
|
||||||
),
|
|
||||||
).DontLog()
|
).DontLog()
|
||||||
|
|
||||||
fullCommits := map[string]*models.Commit{}
|
fullCommits := map[string]*models.Commit{}
|
||||||
@@ -219,11 +228,24 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findFullCommit := lo.Ternary(self.version.IsOlderThan(2, 25, 2),
|
||||||
|
func(sha string) *models.Commit {
|
||||||
|
for s, c := range fullCommits {
|
||||||
|
if strings.HasPrefix(s, sha) {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(sha string) *models.Commit {
|
||||||
|
return fullCommits[sha]
|
||||||
|
})
|
||||||
|
|
||||||
hydratedCommits := make([]*models.Commit, 0, len(commits))
|
hydratedCommits := make([]*models.Commit, 0, len(commits))
|
||||||
for _, rebasingCommit := range commits {
|
for _, rebasingCommit := range commits {
|
||||||
if rebasingCommit.Sha == "" {
|
if rebasingCommit.Sha == "" {
|
||||||
hydratedCommits = append(hydratedCommits, rebasingCommit)
|
hydratedCommits = append(hydratedCommits, rebasingCommit)
|
||||||
} else if commit := fullCommits[rebasingCommit.Sha]; commit != nil {
|
} else if commit := findFullCommit(rebasingCommit.Sha); commit != nil {
|
||||||
commit.Action = rebasingCommit.Action
|
commit.Action = rebasingCommit.Action
|
||||||
commit.Status = rebasingCommit.Status
|
commit.Status = rebasingCommit.Status
|
||||||
hydratedCommits = append(hydratedCommits, commit)
|
hydratedCommits = append(hydratedCommits, commit)
|
||||||
@@ -304,12 +326,23 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
|
|||||||
|
|
||||||
commits := []*models.Commit{}
|
commits := []*models.Commit{}
|
||||||
|
|
||||||
todos, err := todo.Parse(bytes.NewBuffer(bytesContent))
|
todos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.Log.Error(fmt.Sprintf("error occurred while parsing git-rebase-todo file: %s", err.Error()))
|
self.Log.Error(fmt.Sprintf("error occurred while parsing git-rebase-todo file: %s", err.Error()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if the current commit couldn't be applied because it conflicted; if
|
||||||
|
// so, add a fake entry for it
|
||||||
|
if conflictedCommitSha := self.getConflictedCommit(todos); conflictedCommitSha != "" {
|
||||||
|
commits = append(commits, &models.Commit{
|
||||||
|
Sha: conflictedCommitSha,
|
||||||
|
Name: "",
|
||||||
|
Status: models.StatusRebasing,
|
||||||
|
Action: models.ActionConflict,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for _, t := range todos {
|
for _, t := range todos {
|
||||||
if t.Command == todo.UpdateRef {
|
if t.Command == todo.UpdateRef {
|
||||||
t.Msg = strings.TrimPrefix(t.Ref, "refs/heads/")
|
t.Msg = strings.TrimPrefix(t.Ref, "refs/heads/")
|
||||||
@@ -328,6 +361,93 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
|
|||||||
return commits, nil
|
return commits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string {
|
||||||
|
bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-merge/done"))
|
||||||
|
if err != nil {
|
||||||
|
self.Log.Error(fmt.Sprintf("error occurred reading rebase-merge/done: %s", err.Error()))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
doneTodos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar())
|
||||||
|
if err != nil {
|
||||||
|
self.Log.Error(fmt.Sprintf("error occurred while parsing rebase-merge/done file: %s", err.Error()))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
amendFileExists := false
|
||||||
|
if _, err := os.Stat(filepath.Join(self.dotGitDir, "rebase-merge/amend")); err == nil {
|
||||||
|
amendFileExists = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.getConflictedCommitImpl(todos, doneTodos, amendFileExists)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitLoader) getConflictedCommitImpl(todos []todo.Todo, doneTodos []todo.Todo, amendFileExists bool) string {
|
||||||
|
// Should never be possible, but just to be safe:
|
||||||
|
if len(doneTodos) == 0 {
|
||||||
|
self.Log.Error("no done entries in rebase-merge/done file")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
lastTodo := doneTodos[len(doneTodos)-1]
|
||||||
|
if lastTodo.Command == todo.Break || lastTodo.Command == todo.Exec || lastTodo.Command == todo.Reword {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// In certain cases, git reschedules commands that failed. One example is if
|
||||||
|
// a patch would overwrite an untracked file (another one is an "exec" that
|
||||||
|
// failed, but we don't care about that here because we dealt with exec
|
||||||
|
// already above). To detect this, compare the last command of the "done"
|
||||||
|
// file against the first command of "git-rebase-todo"; if they are the
|
||||||
|
// same, the command was rescheduled.
|
||||||
|
if len(doneTodos) > 0 && len(todos) > 0 && doneTodos[len(doneTodos)-1] == todos[0] {
|
||||||
|
// Command was rescheduled, no need to display it
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Older versions of git have a bug whereby, if a command is rescheduled,
|
||||||
|
// the last successful command is appended to the "done" file again. To
|
||||||
|
// detect this, we need to compare the second-to-last done entry against the
|
||||||
|
// first todo entry, and also compare the last done entry against the
|
||||||
|
// last-but-two done entry; this latter check is needed for the following
|
||||||
|
// case:
|
||||||
|
// pick A
|
||||||
|
// exec make test
|
||||||
|
// pick B
|
||||||
|
// exec make test
|
||||||
|
// If pick B fails with conflicts, then the "done" file contains
|
||||||
|
// pick A
|
||||||
|
// exec make test
|
||||||
|
// pick B
|
||||||
|
// and git-rebase-todo contains
|
||||||
|
// exec make test
|
||||||
|
// Without the last condition we would erroneously treat this as the exec
|
||||||
|
// command being rescheduled, so we wouldn't display our fake entry for
|
||||||
|
// "pick B".
|
||||||
|
if len(doneTodos) >= 3 && len(todos) > 0 && doneTodos[len(doneTodos)-2] == todos[0] &&
|
||||||
|
doneTodos[len(doneTodos)-1] == doneTodos[len(doneTodos)-3] {
|
||||||
|
// Command was rescheduled, no need to display it
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastTodo.Command == todo.Edit {
|
||||||
|
if amendFileExists {
|
||||||
|
// Special case for "edit": if the "amend" file exists, the "edit"
|
||||||
|
// command was successful, otherwise it wasn't
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I don't think this is ever possible, but again, just to be safe:
|
||||||
|
if lastTodo.Commit == "" {
|
||||||
|
self.Log.Error("last command in rebase-merge/done file doesn't have a commit")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any other todo that has a commit associated with it must have failed with
|
||||||
|
// a conflict, otherwise we wouldn't have stopped the rebase:
|
||||||
|
return lastTodo.Commit
|
||||||
|
}
|
||||||
|
|
||||||
// assuming the file starts like this:
|
// assuming the file starts like this:
|
||||||
// From e93d4193e6dd45ca9cf3a5a273d7ba6cd8b8fb20 Mon Sep 17 00:00:00 2001
|
// From e93d4193e6dd45ca9cf3a5a273d7ba6cd8b8fb20 Mon Sep 17 00:00:00 2001
|
||||||
// From: Lazygit Tester <test@example.com>
|
// From: Lazygit Tester <test@example.com>
|
||||||
@@ -344,43 +464,82 @@ func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*models.Commit) ([]*models.Commit, error) {
|
func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*models.Commit) []*models.Commit {
|
||||||
ancestor, err := self.getMergeBase(refName)
|
ancestor := self.getMergeBase(refName)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ancestor == "" {
|
if ancestor == "" {
|
||||||
return commits, nil
|
return commits
|
||||||
}
|
}
|
||||||
passedAncestor := false
|
passedAncestor := false
|
||||||
for i, commit := range commits {
|
for i, commit := range commits {
|
||||||
if strings.HasPrefix(ancestor, commit.Sha) {
|
if strings.HasPrefix(ancestor, commit.Sha) {
|
||||||
passedAncestor = true
|
passedAncestor = true
|
||||||
}
|
}
|
||||||
if commit.Status != models.StatusPushed {
|
if commit.Status != models.StatusPushed && commit.Status != models.StatusUnpushed {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if passedAncestor {
|
if passedAncestor {
|
||||||
commits[i].Status = models.StatusMerged
|
commits[i].Status = models.StatusMerged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return commits, nil
|
return commits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getMergeBase(refName string) (string, error) {
|
func (self *CommitLoader) getMergeBase(refName string) string {
|
||||||
info, err := self.getCurrentBranchInfo()
|
if self.mainBranches == nil {
|
||||||
|
self.mainBranches = self.getExistingMainBranches()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(self.mainBranches) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// We pass all configured main branches to the merge-base call; git will
|
||||||
|
// return the base commit for the closest one.
|
||||||
|
|
||||||
|
output, err := self.cmd.New(
|
||||||
|
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
|
||||||
|
ToArgv(),
|
||||||
|
).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
// If there's an error, it must be because one of the main branches that
|
||||||
|
// used to exist when we called getExistingMainBranches() was deleted
|
||||||
|
// meanwhile. To fix this for next time, throw away our cache.
|
||||||
|
self.mainBranches = nil
|
||||||
}
|
}
|
||||||
|
return ignoringWarnings(output)
|
||||||
|
}
|
||||||
|
|
||||||
baseBranch := "master"
|
func (self *CommitLoader) getExistingMainBranches() []string {
|
||||||
if strings.HasPrefix(info.RefName, "feature/") {
|
return lo.FilterMap(self.UserConfig.Git.MainBranches,
|
||||||
baseBranch = "develop"
|
func(branchName string, _ int) (string, bool) {
|
||||||
}
|
// Try to determine upstream of local main branch
|
||||||
|
if ref, err := self.cmd.New(
|
||||||
|
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
|
||||||
|
).DontLog().RunWithOutput(); err == nil {
|
||||||
|
return strings.TrimSpace(ref), true
|
||||||
|
}
|
||||||
|
|
||||||
// swallowing error because it's not a big deal; probably because there are no commits yet
|
// If this failed, a local branch for this main branch doesn't exist or it
|
||||||
output, _ := self.cmd.New(fmt.Sprintf("git merge-base %s %s", self.cmd.Quote(refName), self.cmd.Quote(baseBranch))).DontLog().RunWithOutput()
|
// has no upstream configured. Try looking for one in the "origin" remote.
|
||||||
return ignoringWarnings(output), nil
|
ref := "refs/remotes/origin/" + branchName
|
||||||
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||||
|
).DontLog().Run(); err == nil {
|
||||||
|
return ref, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this failed as well, try if we have the main branch as a local
|
||||||
|
// branch. This covers the case where somebody is using git locally
|
||||||
|
// for something, but never pushing anywhere.
|
||||||
|
ref = "refs/heads/" + branchName
|
||||||
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||||
|
).DontLog().Run(); err == nil {
|
||||||
|
return ref, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ignoringWarnings(commandOutput string) string {
|
func ignoringWarnings(commandOutput string) string {
|
||||||
@@ -396,12 +555,12 @@ func ignoringWarnings(commandOutput string) string {
|
|||||||
// getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream.
|
// getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream.
|
||||||
// all commits above this are deemed unpushed and marked as such.
|
// all commits above this are deemed unpushed and marked as such.
|
||||||
func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
||||||
output, err := self.cmd.
|
output, err := self.cmd.New(
|
||||||
New(
|
NewGitCmd("merge-base").
|
||||||
fmt.Sprintf("git merge-base %s %s@{u}",
|
Arg(refName).
|
||||||
self.cmd.Quote(refName),
|
Arg(strings.TrimPrefix(refName, "refs/heads/") + "@{u}").
|
||||||
self.cmd.Quote(strings.TrimPrefix(refName, "refs/heads/"))),
|
ToArgv(),
|
||||||
).
|
).
|
||||||
DontLog().
|
DontLog().
|
||||||
RunWithOutput()
|
RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -413,42 +572,23 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
|||||||
|
|
||||||
// getLog gets the git log.
|
// getLog gets the git log.
|
||||||
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||||
limitFlag := ""
|
|
||||||
if opts.Limit {
|
|
||||||
limitFlag = " -300"
|
|
||||||
}
|
|
||||||
|
|
||||||
followFlag := ""
|
|
||||||
filterFlag := ""
|
|
||||||
if opts.FilterPath != "" {
|
|
||||||
followFlag = " --follow"
|
|
||||||
filterFlag = fmt.Sprintf(" %s", self.cmd.Quote(opts.FilterPath))
|
|
||||||
}
|
|
||||||
|
|
||||||
config := self.UserConfig.Git.Log
|
config := self.UserConfig.Git.Log
|
||||||
|
|
||||||
orderFlag := ""
|
cmdArgs := NewGitCmd("log").
|
||||||
if config.Order != "default" {
|
Arg(opts.RefName).
|
||||||
orderFlag = " --" + config.Order
|
ArgIf(config.Order != "default", "--"+config.Order).
|
||||||
}
|
ArgIf(opts.All, "--all").
|
||||||
allFlag := ""
|
Arg("--oneline").
|
||||||
if opts.All {
|
Arg(prettyFormat).
|
||||||
allFlag = " --all"
|
Arg("--abbrev=40").
|
||||||
}
|
ArgIf(opts.Limit, "-300").
|
||||||
|
ArgIf(opts.FilterPath != "", "--follow").
|
||||||
|
Arg("--no-show-signature").
|
||||||
|
Arg("--").
|
||||||
|
ArgIf(opts.FilterPath != "", opts.FilterPath).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
fmt.Sprintf(
|
|
||||||
"git log %s%s%s --oneline %s%s --abbrev=%d%s --no-show-signature --%s",
|
|
||||||
self.cmd.Quote(opts.RefName),
|
|
||||||
orderFlag,
|
|
||||||
allFlag,
|
|
||||||
prettyFormat,
|
|
||||||
limitFlag,
|
|
||||||
40,
|
|
||||||
followFlag,
|
|
||||||
filterFlag,
|
|
||||||
),
|
|
||||||
).DontLog()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const prettyFormat = `--pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s"`
|
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s`
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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/commands/types/enums"
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
@@ -12,67 +14,74 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commitsOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com| (HEAD -> better-tests)|b21997d6b4cbdf84b149|better typing for rebase mode
|
var commitsOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|better typing for rebase mode
|
||||||
b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164|1640824515|Jesse Duffield|jessedduffield@gmail.com| (origin/better-tests)|e94e8fc5b6fab4cb755f|fix logging
|
b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164|1640824515|Jesse Duffield|jessedduffield@gmail.com|origin/better-tests|e94e8fc5b6fab4cb755f|fix logging
|
||||||
e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com||d8084cd558925eb7c9c3|refactor
|
e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com|tag: 123, tag: 456|d8084cd558925eb7c9c3|refactor
|
||||||
d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com||65f910ebd85283b5cce9|WIP
|
d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com||65f910ebd85283b5cce9|WIP
|
||||||
65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com||26c07b1ab33860a1a759|WIP
|
65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com||26c07b1ab33860a1a759|WIP
|
||||||
26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com||3d4470a6c072208722e5|WIP
|
26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com||3d4470a6c072208722e5|WIP
|
||||||
3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|WIP
|
3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|WIP
|
||||||
053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|refactoring the config struct`, "|", "\x00", -1)
|
053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|refactoring the config struct`, "|", "\x00", -1)
|
||||||
|
|
||||||
|
var singleCommitOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|better typing for rebase mode`, "|", "\x00", -1)
|
||||||
|
|
||||||
func TestGetCommits(t *testing.T) {
|
func TestGetCommits(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
runner *oscommands.FakeCmdObjRunner
|
runner *oscommands.FakeCmdObjRunner
|
||||||
expectedCommits []*models.Commit
|
expectedCommits []*models.Commit
|
||||||
expectedError error
|
expectedError error
|
||||||
logOrder string
|
logOrder string
|
||||||
rebaseMode enums.RebaseMode
|
rebaseMode enums.RebaseMode
|
||||||
currentBranchName string
|
opts GetCommitsOptions
|
||||||
opts GetCommitsOptions
|
mainBranches []string
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
testName: "should return no commits if there are none",
|
testName: "should return no commits if there are none",
|
||||||
logOrder: "topo-order",
|
logOrder: "topo-order",
|
||||||
rebaseMode: enums.REBASE_MODE_NONE,
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
currentBranchName: "master",
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "should use proper upstream name for branch",
|
testName: "should use proper upstream name for branch",
|
||||||
logOrder: "topo-order",
|
logOrder: "topo-order",
|
||||||
rebaseMode: enums.REBASE_MODE_NONE,
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
currentBranchName: "mybranch",
|
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
||||||
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git merge-base "refs/heads/mybranch" "mybranch"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
Expect(`git log "refs/heads/mybranch" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
|
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "should return commits if they are present",
|
testName: "should return commits if they are present",
|
||||||
logOrder: "topo-order",
|
logOrder: "topo-order",
|
||||||
rebaseMode: enums.REBASE_MODE_NONE,
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
currentBranchName: "master",
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
mainBranches: []string{"master", "main", "develop"},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, commitsOutput, nil).
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
||||||
|
// here it's testing which of the configured main branches have an upstream
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", nil). // yep, origin/main exists
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/develop"}, "", errors.New("error")). // doesn't exist there, either, so it checks for a local branch
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/develop"}, "", errors.New("error")). // no local branch either
|
||||||
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
||||||
Expect(`git merge-base "HEAD" "master"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/remotes/origin/master", "refs/remotes/origin/main"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{
|
expectedCommits: []*models.Commit{
|
||||||
{
|
{
|
||||||
@@ -108,8 +117,8 @@ func TestGetCommits(t *testing.T) {
|
|||||||
Name: "refactor",
|
Name: "refactor",
|
||||||
Status: models.StatusPushed,
|
Status: models.StatusPushed,
|
||||||
Action: models.ActionNone,
|
Action: models.ActionNone,
|
||||||
Tags: []string{},
|
Tags: []string{"123", "456"},
|
||||||
ExtraInfo: "",
|
ExtraInfo: "(tag: 123, tag: 456)",
|
||||||
AuthorName: "Jesse Duffield",
|
AuthorName: "Jesse Duffield",
|
||||||
AuthorEmail: "jessedduffield@gmail.com",
|
AuthorEmail: "jessedduffield@gmail.com",
|
||||||
UnixTimestamp: 1640823749,
|
UnixTimestamp: 1640823749,
|
||||||
@@ -191,27 +200,101 @@ func TestGetCommits(t *testing.T) {
|
|||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "should not specify order if `log.order` is `default`",
|
testName: "should not call merge-base for mainBranches if none exist",
|
||||||
logOrder: "default",
|
logOrder: "topo-order",
|
||||||
rebaseMode: enums.REBASE_MODE_NONE,
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
currentBranchName: "master",
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
mainBranches: []string{"master", "main"},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
// here it's seeing which commits are yet to be pushed
|
||||||
Expect(`git log "HEAD" --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
|
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||||
|
// here it's testing which of the configured main branches exist; neither does
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")),
|
||||||
|
|
||||||
|
expectedCommits: []*models.Commit{
|
||||||
|
{
|
||||||
|
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
|
||||||
|
Name: "better typing for rebase mode",
|
||||||
|
Status: models.StatusUnpushed,
|
||||||
|
Action: models.ActionNone,
|
||||||
|
Tags: []string{},
|
||||||
|
ExtraInfo: "(HEAD -> better-tests)",
|
||||||
|
AuthorName: "Jesse Duffield",
|
||||||
|
AuthorEmail: "jessedduffield@gmail.com",
|
||||||
|
UnixTimestamp: 1640826609,
|
||||||
|
Parents: []string{
|
||||||
|
"b21997d6b4cbdf84b149",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "should call merge-base for all main branches that exist",
|
||||||
|
logOrder: "topo-order",
|
||||||
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
|
mainBranches: []string{"master", "main", "develop", "1.0-hotfixes"},
|
||||||
|
runner: oscommands.NewFakeRunner(t).
|
||||||
|
// here it's seeing which commits are yet to be pushed
|
||||||
|
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||||
|
// here it's testing which of the configured main branches exist
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "refs/remotes/origin/develop", nil).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "1.0-hotfixes@{u}"}, "refs/remotes/origin/1.0-hotfixes", nil).
|
||||||
|
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
||||||
|
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/remotes/origin/master", "refs/remotes/origin/develop", "refs/remotes/origin/1.0-hotfixes"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||||
|
|
||||||
|
expectedCommits: []*models.Commit{
|
||||||
|
{
|
||||||
|
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
|
||||||
|
Name: "better typing for rebase mode",
|
||||||
|
Status: models.StatusUnpushed,
|
||||||
|
Action: models.ActionNone,
|
||||||
|
Tags: []string{},
|
||||||
|
ExtraInfo: "(HEAD -> better-tests)",
|
||||||
|
AuthorName: "Jesse Duffield",
|
||||||
|
AuthorEmail: "jessedduffield@gmail.com",
|
||||||
|
UnixTimestamp: 1640826609,
|
||||||
|
Parents: []string{
|
||||||
|
"b21997d6b4cbdf84b149",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "should not specify order if `log.order` is `default`",
|
||||||
|
logOrder: "default",
|
||||||
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
|
runner: oscommands.NewFakeRunner(t).
|
||||||
|
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
|
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "should set filter path",
|
testName: "should set filter path",
|
||||||
logOrder: "default",
|
logOrder: "default",
|
||||||
rebaseMode: enums.REBASE_MODE_NONE,
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
currentBranchName: "master",
|
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
|
||||||
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
|
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
Expect(`git log "HEAD" --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --follow --no-show-signature -- "src"`, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@@ -225,11 +308,8 @@ func TestGetCommits(t *testing.T) {
|
|||||||
common.UserConfig.Git.Log.Order = scenario.logOrder
|
common.UserConfig.Git.Log.Order = scenario.logOrder
|
||||||
|
|
||||||
builder := &CommitLoader{
|
builder := &CommitLoader{
|
||||||
Common: common,
|
Common: common,
|
||||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||||
getCurrentBranchInfo: func() (BranchInfo, error) {
|
|
||||||
return BranchInfo{RefName: scenario.currentBranchName, DisplayName: scenario.currentBranchName, DetachedHead: false}, nil
|
|
||||||
},
|
|
||||||
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
|
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
|
||||||
dotGitDir: ".git",
|
dotGitDir: ".git",
|
||||||
readFile: func(filename string) ([]byte, error) {
|
readFile: func(filename string) ([]byte, error) {
|
||||||
@@ -240,6 +320,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common.UserConfig.Git.MainBranches = scenario.mainBranches
|
||||||
commits, err := builder.GetCommits(scenario.opts)
|
commits, err := builder.GetCommits(scenario.opts)
|
||||||
|
|
||||||
assert.Equal(t, scenario.expectedCommits, commits)
|
assert.Equal(t, scenario.expectedCommits, commits)
|
||||||
@@ -249,3 +330,179 @@ func TestGetCommits(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
testName string
|
||||||
|
todos []todo.Todo
|
||||||
|
doneTodos []todo.Todo
|
||||||
|
amendFileExists bool
|
||||||
|
expectedSha string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "no done todos",
|
||||||
|
todos: []todo.Todo{},
|
||||||
|
doneTodos: []todo.Todo{},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "common case (conflict)",
|
||||||
|
todos: []todo.Todo{},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "deadbeef",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "fa1afe1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "last command was 'break'",
|
||||||
|
todos: []todo.Todo{},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{Command: todo.Break},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "last command was 'exec'",
|
||||||
|
todos: []todo.Todo{},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Exec,
|
||||||
|
ExecCommand: "make test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "last command was 'reword'",
|
||||||
|
todos: []todo.Todo{},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{Command: todo.Reword},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "'pick' was rescheduled",
|
||||||
|
todos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "'pick' was rescheduled, buggy git version",
|
||||||
|
todos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "deadbeaf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "deadbeaf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "conflicting 'pick' after 'exec'",
|
||||||
|
todos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Exec,
|
||||||
|
ExecCommand: "make test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "deadbeaf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: todo.Exec,
|
||||||
|
ExecCommand: "make test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: todo.Pick,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "fa1afe1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "'edit' with amend file",
|
||||||
|
todos: []todo.Todo{},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Edit,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amendFileExists: true,
|
||||||
|
expectedSha: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "'edit' without amend file",
|
||||||
|
todos: []todo.Todo{},
|
||||||
|
doneTodos: []todo.Todo{
|
||||||
|
{
|
||||||
|
Command: todo.Edit,
|
||||||
|
Commit: "fa1afe1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amendFileExists: false,
|
||||||
|
expectedSha: "fa1afe1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
t.Run(scenario.testName, func(t *testing.T) {
|
||||||
|
common := utils.NewDummyCommon()
|
||||||
|
|
||||||
|
builder := &CommitLoader{
|
||||||
|
Common: common,
|
||||||
|
cmd: oscommands.NewDummyCmdObjBuilder(oscommands.NewFakeRunner(t)),
|
||||||
|
getRebaseMode: func() (enums.RebaseMode, error) { return enums.REBASE_MODE_INTERACTIVE, nil },
|
||||||
|
dotGitDir: ".git",
|
||||||
|
readFile: func(filename string) ([]byte, error) {
|
||||||
|
return []byte(""), nil
|
||||||
|
},
|
||||||
|
walkFiles: func(root string, fn filepath.WalkFunc) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sha := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists)
|
||||||
|
assert.Equal(t, scenario.expectedSha, sha)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func TestCommitCommitCmdObj(t *testing.T) {
|
|||||||
message string
|
message string
|
||||||
configSignoff bool
|
configSignoff bool
|
||||||
configSkipHookPrefix string
|
configSkipHookPrefix string
|
||||||
expected string
|
expectedArgs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
@@ -62,35 +62,35 @@ func TestCommitCommitCmdObj(t *testing.T) {
|
|||||||
message: "test",
|
message: "test",
|
||||||
configSignoff: false,
|
configSignoff: false,
|
||||||
configSkipHookPrefix: "",
|
configSkipHookPrefix: "",
|
||||||
expected: `git commit -m "test"`,
|
expectedArgs: []string{"commit", "-m", "test"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Commit with --no-verify flag",
|
testName: "Commit with --no-verify flag",
|
||||||
message: "WIP: test",
|
message: "WIP: test",
|
||||||
configSignoff: false,
|
configSignoff: false,
|
||||||
configSkipHookPrefix: "WIP",
|
configSkipHookPrefix: "WIP",
|
||||||
expected: `git commit --no-verify -m "WIP: test"`,
|
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Commit with multiline message",
|
testName: "Commit with multiline message",
|
||||||
message: "line1\nline2",
|
message: "line1\nline2",
|
||||||
configSignoff: false,
|
configSignoff: false,
|
||||||
configSkipHookPrefix: "",
|
configSkipHookPrefix: "",
|
||||||
expected: `git commit -m "line1" -m "line2"`,
|
expectedArgs: []string{"commit", "-m", "line1", "-m", "line2"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Commit with signoff",
|
testName: "Commit with signoff",
|
||||||
message: "test",
|
message: "test",
|
||||||
configSignoff: true,
|
configSignoff: true,
|
||||||
configSkipHookPrefix: "",
|
configSkipHookPrefix: "",
|
||||||
expected: `git commit --signoff -m "test"`,
|
expectedArgs: []string{"commit", "--signoff", "-m", "test"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Commit with signoff and no-verify",
|
testName: "Commit with signoff and no-verify",
|
||||||
message: "WIP: test",
|
message: "WIP: test",
|
||||||
configSignoff: true,
|
configSignoff: true,
|
||||||
configSkipHookPrefix: "WIP",
|
configSkipHookPrefix: "WIP",
|
||||||
expected: `git commit --no-verify --signoff -m "WIP: test"`,
|
expectedArgs: []string{"commit", "--no-verify", "--signoff", "-m", "WIP: test"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +101,11 @@ func TestCommitCommitCmdObj(t *testing.T) {
|
|||||||
userConfig.Git.Commit.SignOff = s.configSignoff
|
userConfig.Git.Commit.SignOff = s.configSignoff
|
||||||
userConfig.Git.SkipHookPrefix = s.configSkipHookPrefix
|
userConfig.Git.SkipHookPrefix = s.configSkipHookPrefix
|
||||||
|
|
||||||
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
|
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expectedArgs, "", nil)
|
||||||
|
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
|
||||||
|
|
||||||
cmdStr := instance.CommitCmdObj(s.message).ToString()
|
assert.NoError(t, instance.CommitCmdObj(s.message).Run())
|
||||||
assert.Equal(t, s.expected, cmdStr)
|
runner.CheckForMissingCalls()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,40 +114,19 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
configSignoff bool
|
configSignoff bool
|
||||||
configVerbose string
|
expected []string
|
||||||
expected string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
testName: "Commit using editor",
|
testName: "Commit using editor",
|
||||||
configSignoff: false,
|
configSignoff: false,
|
||||||
configVerbose: "default",
|
expected: []string{"commit"},
|
||||||
expected: `git commit`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Commit with --no-verbose flag",
|
|
||||||
configSignoff: false,
|
|
||||||
configVerbose: "never",
|
|
||||||
expected: `git commit --no-verbose`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Commit with --verbose flag",
|
|
||||||
configSignoff: false,
|
|
||||||
configVerbose: "always",
|
|
||||||
expected: `git commit --verbose`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Commit with --signoff",
|
testName: "Commit with --signoff",
|
||||||
configSignoff: true,
|
configSignoff: true,
|
||||||
configVerbose: "default",
|
expected: []string{"commit", "--signoff"},
|
||||||
expected: `git commit --signoff`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Commit with --signoff and --no-verbose",
|
|
||||||
configSignoff: true,
|
|
||||||
configVerbose: "never",
|
|
||||||
expected: `git commit --signoff --no-verbose`,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,12 +135,12 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
|
|||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
userConfig := config.GetDefaultConfig()
|
userConfig := config.GetDefaultConfig()
|
||||||
userConfig.Git.Commit.SignOff = s.configSignoff
|
userConfig.Git.Commit.SignOff = s.configSignoff
|
||||||
userConfig.Git.Commit.Verbose = s.configVerbose
|
|
||||||
|
|
||||||
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
|
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
|
||||||
|
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
|
||||||
|
|
||||||
cmdStr := instance.CommitEditorCmdObj().ToString()
|
assert.NoError(t, instance.CommitEditorCmdObj().Run())
|
||||||
assert.Equal(t, s.expected, cmdStr)
|
runner.CheckForMissingCalls()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,7 +158,7 @@ func TestCommitCreateFixupCommit(t *testing.T) {
|
|||||||
testName: "valid case",
|
testName: "valid case",
|
||||||
sha: "12345",
|
sha: "12345",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git commit --fixup=12345`, "", nil),
|
ExpectGitArgs([]string{"commit", "--fixup=12345"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -201,7 +181,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
|||||||
filterPath string
|
filterPath string
|
||||||
contextSize int
|
contextSize int
|
||||||
ignoreWhitespace bool
|
ignoreWhitespace bool
|
||||||
expected string
|
expected []string
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
@@ -210,28 +190,28 @@ func TestCommitShowCmdObj(t *testing.T) {
|
|||||||
filterPath: "",
|
filterPath: "",
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
expected: "git show --submodule --color=always --unified=3 --stat -p 1234567890",
|
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Default case with filter path",
|
testName: "Default case with filter path",
|
||||||
filterPath: "file.txt",
|
filterPath: "file.txt",
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
expected: `git show --submodule --color=always --unified=3 --stat -p 1234567890 -- "file.txt"`,
|
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff with custom context size",
|
testName: "Show diff with custom context size",
|
||||||
filterPath: "",
|
filterPath: "",
|
||||||
contextSize: 77,
|
contextSize: 77,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
expected: "git show --submodule --color=always --unified=77 --stat -p 1234567890",
|
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff, ignoring whitespace",
|
testName: "Show diff, ignoring whitespace",
|
||||||
filterPath: "",
|
filterPath: "",
|
||||||
contextSize: 77,
|
contextSize: 77,
|
||||||
ignoreWhitespace: true,
|
ignoreWhitespace: true,
|
||||||
expected: "git show --submodule --color=always --unified=77 --stat -p 1234567890 --ignore-all-space",
|
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,10 +221,11 @@ func TestCommitShowCmdObj(t *testing.T) {
|
|||||||
userConfig := config.GetDefaultConfig()
|
userConfig := config.GetDefaultConfig()
|
||||||
userConfig.Git.DiffContextSize = s.contextSize
|
userConfig.Git.DiffContextSize = s.contextSize
|
||||||
|
|
||||||
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
|
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
|
||||||
|
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
|
||||||
|
|
||||||
cmdStr := instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).ToString()
|
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).Run())
|
||||||
assert.Equal(t, s.expected, cmdStr)
|
runner.CheckForMissingCalls()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +264,7 @@ Merge pull request #1750 from mark2185/fix-issue-template
|
|||||||
s := s
|
s := s
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
instance := buildCommitCommands(commonDeps{
|
instance := buildCommitCommands(commonDeps{
|
||||||
runner: oscommands.NewFakeRunner(t).Expect("git rev-list --format=%B --max-count=1 deadbeef", s.input, nil),
|
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
|
||||||
})
|
})
|
||||||
|
|
||||||
output, err := instance.GetCommitMessage("deadbeef")
|
output, err := instance.GetCommitMessage("deadbeef")
|
||||||
@@ -304,14 +285,14 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
|
|||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"Empty message",
|
"Empty message",
|
||||||
oscommands.NewFakeRunner(t).Expect("git log -1 --skip=2 --pretty=%H", "", nil).Expect("git rev-list --format=%B --max-count=1 ", "", nil),
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1"}, "", nil),
|
||||||
func(output string, err error) {
|
func(output string, err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Default case to retrieve a commit in history",
|
"Default case to retrieve a commit in history",
|
||||||
oscommands.NewFakeRunner(t).Expect("git log -1 --skip=2 --pretty=%H", "sha3 \n", nil).Expect("git rev-list --format=%B --max-count=1 sha3", `commit sha3
|
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "sha3"}, `commit sha3
|
||||||
use generics to DRY up context code`, nil),
|
use generics to DRY up context code`, nil),
|
||||||
func(output string, err error) {
|
func(output string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -99,3 +99,11 @@ func (self *ConfigCommands) Branches() (map[string]*config.Branch, error) {
|
|||||||
func (self *ConfigCommands) GetGitFlowPrefixes() string {
|
func (self *ConfigCommands) GetGitFlowPrefixes() string {
|
||||||
return self.gitConfig.GetGeneral("--local --get-regexp gitflow.prefix")
|
return self.gitConfig.GetGeneral("--local --get-regexp gitflow.prefix")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ConfigCommands) GetCoreCommentChar() byte {
|
||||||
|
if commentCharStr := self.gitConfig.Get("core.commentChar"); len(commentCharStr) == 1 {
|
||||||
|
return commentCharStr[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return '#'
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
|
import "github.com/mgutz/str"
|
||||||
|
|
||||||
type CustomCommands struct {
|
type CustomCommands struct {
|
||||||
*GitCommon
|
*GitCommon
|
||||||
}
|
}
|
||||||
@@ -14,5 +16,5 @@ func NewCustomCommands(gitCommon *GitCommon) *CustomCommands {
|
|||||||
// If you want to run a new command, try finding a place for it in one of the neighbouring
|
// If you want to run a new command, try finding a place for it in one of the neighbouring
|
||||||
// files, or creating a new BlahCommands struct to hold it.
|
// files, or creating a new BlahCommands struct to hold it.
|
||||||
func (self *CustomCommands) RunWithOutput(cmdStr string) (string, error) {
|
func (self *CustomCommands) RunWithOutput(cmdStr string) (string, error) {
|
||||||
return self.cmd.New(cmdStr).RunWithOutput()
|
return self.cmd.New(str.ToArgv(cmdStr)).RunWithOutput()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
gogit "github.com/jesseduffield/go-git/v5"
|
gogit "github.com/jesseduffield/go-git/v5"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@@ -117,6 +118,26 @@ func buildWorkingTreeCommands(deps commonDeps) *WorkingTreeCommands {
|
|||||||
return NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
return NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildPatchCommands(deps commonDeps) *PatchCommands {
|
||||||
|
gitCommon := buildGitCommon(deps)
|
||||||
|
rebaseCommands := buildRebaseCommands(deps)
|
||||||
|
commitCommands := buildCommitCommands(deps)
|
||||||
|
statusCommands := buildStatusCommands(deps)
|
||||||
|
stashCommands := buildStashCommands(deps)
|
||||||
|
loadFileFn := func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
patchBuilder := patch.NewPatchBuilder(gitCommon.Log, loadFileFn)
|
||||||
|
|
||||||
|
return NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildStatusCommands(deps commonDeps) *StatusCommands {
|
||||||
|
gitCommon := buildGitCommon(deps)
|
||||||
|
|
||||||
|
return NewStatusCommands(gitCommon)
|
||||||
|
}
|
||||||
|
|
||||||
func buildStashCommands(deps commonDeps) *StashCommands {
|
func buildStashCommands(deps commonDeps) *StashCommands {
|
||||||
gitCommon := buildGitCommon(deps)
|
gitCommon := buildGitCommon(deps)
|
||||||
fileLoader := buildFileLoader(gitCommon)
|
fileLoader := buildFileLoader(gitCommon)
|
||||||
@@ -150,3 +171,9 @@ func buildBranchCommands(deps commonDeps) *BranchCommands {
|
|||||||
|
|
||||||
return NewBranchCommands(gitCommon)
|
return NewBranchCommands(gitCommon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildFlowCommands(deps commonDeps) *FlowCommands {
|
||||||
|
gitCommon := buildGitCommon(deps)
|
||||||
|
|
||||||
|
return NewFlowCommands(gitCommon)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package git_commands
|
||||||
|
|
||||||
|
import "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
|
||||||
|
type DiffCommands struct {
|
||||||
|
*GitCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
|
||||||
|
return &DiffCommands{
|
||||||
|
GitCommon: gitCommon,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||||
|
return self.cmd.New(
|
||||||
|
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
|
|||||||
editor = self.os.Getenv("EDITOR")
|
editor = self.os.Getenv("EDITOR")
|
||||||
}
|
}
|
||||||
if editor == "" {
|
if editor == "" {
|
||||||
if err := self.cmd.New("which vi").DontLog().Run(); err == nil {
|
if err := self.cmd.New([]string{"which", "vi"}).DontLog().Run(); err == nil {
|
||||||
editor = "vi"
|
editor = "vi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
|||||||
}
|
}
|
||||||
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
|
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
|
||||||
|
|
||||||
statuses, err := self.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
|
statuses, err := self.gitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.Log.Error(err)
|
self.Log.Error(err)
|
||||||
}
|
}
|
||||||
@@ -81,13 +81,15 @@ type FileStatus struct {
|
|||||||
PreviousName string
|
PreviousName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FileLoader) GitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
func (c *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||||
noRenamesFlag := ""
|
cmdArgs := NewGitCmd("status").
|
||||||
if opts.NoRenames {
|
Arg(opts.UntrackedFilesArg).
|
||||||
noRenamesFlag = " --no-renames"
|
Arg("--porcelain").
|
||||||
}
|
Arg("-z").
|
||||||
|
ArgIf(opts.NoRenames, "--no-renames").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
statusLines, _, err := c.cmd.New(fmt.Sprintf("git status %s --porcelain -z%s", opts.UntrackedFilesArg, noRenamesFlag)).DontLog().RunWithOutputs()
|
statusLines, _, err := c.cmd.New(cmdArgs).DontLog().RunWithOutputs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []FileStatus{}, err
|
return []FileStatus{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,14 +20,13 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"No files found",
|
"No files found",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git status --untracked-files=yes --porcelain -z`, "", nil),
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "", nil),
|
||||||
[]*models.File{},
|
[]*models.File{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Several files found",
|
"Several files found",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
|
||||||
`git status --untracked-files=yes --porcelain -z`,
|
|
||||||
"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,
|
||||||
),
|
),
|
||||||
@@ -102,7 +101,7 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"File with new line char",
|
"File with new line char",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git status --untracked-files=yes --porcelain -z`, "MM a\nb.txt", nil),
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "MM a\nb.txt", nil),
|
||||||
[]*models.File{
|
[]*models.File{
|
||||||
{
|
{
|
||||||
Name: "a\nb.txt",
|
Name: "a\nb.txt",
|
||||||
@@ -122,8 +121,7 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Renamed files",
|
"Renamed files",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
|
||||||
`git status --untracked-files=yes --porcelain -z`,
|
|
||||||
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
|
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
@@ -161,8 +159,7 @@ func TestFileGetStatusFiles(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"File with arrow in name",
|
"File with arrow in name",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
|
||||||
`git status --untracked-files=yes --porcelain -z`,
|
|
||||||
`?? a -> b.txt`,
|
`?? a -> b.txt`,
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
|
|||||||
configEditCommand: "",
|
configEditCommand: "",
|
||||||
configEditCommandTemplate: "{{editor}} {{filename}}",
|
configEditCommandTemplate: "{{editor}} {{filename}}",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`which vi`, "", errors.New("error")),
|
ExpectArgs([]string{"which", "vi"}, "", errors.New("error")),
|
||||||
getenv: func(env string) string {
|
getenv: func(env string) string {
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
@@ -105,7 +105,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
|
|||||||
configEditCommand: "",
|
configEditCommand: "",
|
||||||
configEditCommandTemplate: "{{editor}} {{filename}}",
|
configEditCommandTemplate: "{{editor}} {{filename}}",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`which vi`, "/usr/bin/vi", nil),
|
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
|
||||||
getenv: func(env string) string {
|
getenv: func(env string) string {
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
@@ -120,7 +120,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
|
|||||||
configEditCommand: "",
|
configEditCommand: "",
|
||||||
configEditCommandTemplate: "{{editor}} {{filename}}",
|
configEditCommandTemplate: "{{editor}} {{filename}}",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`which vi`, "/usr/bin/vi", nil),
|
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
|
||||||
getenv: func(env string) string {
|
getenv: func(env string) string {
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ func (self *FlowCommands) FinishCmdObj(branchName string) (oscommands.ICmdObj, e
|
|||||||
branchType := ""
|
branchType := ""
|
||||||
for _, line := range strings.Split(strings.TrimSpace(prefixes), "\n") {
|
for _, line := range strings.Split(strings.TrimSpace(prefixes), "\n") {
|
||||||
if strings.HasPrefix(line, "gitflow.prefix.") && strings.HasSuffix(line, prefix) {
|
if strings.HasPrefix(line, "gitflow.prefix.") && strings.HasSuffix(line, prefix) {
|
||||||
|
|
||||||
regex := regexp.MustCompile("gitflow.prefix.([^ ]*) .*")
|
regex := regexp.MustCompile("gitflow.prefix.([^ ]*) .*")
|
||||||
matches := regex.FindAllStringSubmatch(line, 1)
|
matches := regex.FindAllStringSubmatch(line, 1)
|
||||||
|
|
||||||
@@ -48,9 +49,13 @@ func (self *FlowCommands) FinishCmdObj(branchName string) (oscommands.ICmdObj, e
|
|||||||
return nil, errors.New(self.Tr.NotAGitFlowBranch)
|
return nil, errors.New(self.Tr.NotAGitFlowBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.cmd.New("git flow " + branchType + " finish " + suffix), nil
|
cmdArgs := NewGitCmd("flow").Arg(branchType, "finish", suffix).ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FlowCommands) StartCmdObj(branchType string, name string) oscommands.ICmdObj {
|
func (self *FlowCommands) StartCmdObj(branchType string, name string) oscommands.ICmdObj {
|
||||||
return self.cmd.New("git flow " + branchType + " start " + name)
|
cmdArgs := NewGitCmd("flow").Arg(branchType, "start", name).ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package git_commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStartCmdObj(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
testName string
|
||||||
|
branchType string
|
||||||
|
name string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "basic",
|
||||||
|
branchType: "feature",
|
||||||
|
name: "test",
|
||||||
|
expected: []string{"git", "flow", "feature", "start", "test"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
instance := buildFlowCommands(commonDeps{})
|
||||||
|
|
||||||
|
assert.Equal(t,
|
||||||
|
instance.StartCmdObj(s.branchType, s.name).Args(),
|
||||||
|
s.expected,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFinishCmdObj(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
testName string
|
||||||
|
branchName string
|
||||||
|
expected []string
|
||||||
|
expectedError string
|
||||||
|
gitConfigMockResponses map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "not a git flow branch",
|
||||||
|
branchName: "mybranch",
|
||||||
|
expected: nil,
|
||||||
|
expectedError: "This does not seem to be a git flow branch",
|
||||||
|
gitConfigMockResponses: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "feature branch without config",
|
||||||
|
branchName: "feature/mybranch",
|
||||||
|
expected: nil,
|
||||||
|
expectedError: "This does not seem to be a git flow branch",
|
||||||
|
gitConfigMockResponses: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "feature branch with config",
|
||||||
|
branchName: "feature/mybranch",
|
||||||
|
expected: []string{"git", "flow", "feature", "finish", "mybranch"},
|
||||||
|
expectedError: "",
|
||||||
|
gitConfigMockResponses: map[string]string{
|
||||||
|
"--local --get-regexp gitflow.prefix": "gitflow.prefix.feature feature/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
instance := buildFlowCommands(commonDeps{
|
||||||
|
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd, err := instance.FinishCmdObj(s.branchName)
|
||||||
|
|
||||||
|
if s.expectedError != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error, got nil")
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, err.Error(), s.expectedError)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, cmd.Args(), s.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package git_commands
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// convenience struct for building git commands. Especially useful when
|
||||||
|
// including conditional args
|
||||||
|
type GitCommandBuilder struct {
|
||||||
|
// command string
|
||||||
|
args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitCmd(command string) *GitCommandBuilder {
|
||||||
|
return &GitCommandBuilder{args: []string{command}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) Arg(args ...string) *GitCommandBuilder {
|
||||||
|
self.args = append(self.args, args...)
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) ArgIf(condition bool, ifTrue ...string) *GitCommandBuilder {
|
||||||
|
if condition {
|
||||||
|
self.Arg(ifTrue...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) ArgIfElse(condition bool, ifTrue string, ifFalse string) *GitCommandBuilder {
|
||||||
|
if condition {
|
||||||
|
return self.Arg(ifTrue)
|
||||||
|
} else {
|
||||||
|
return self.Arg(ifFalse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) Config(value string) *GitCommandBuilder {
|
||||||
|
// config settings come before the command
|
||||||
|
self.args = append([]string{"-c", value}, self.args...)
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) RepoPath(value string) *GitCommandBuilder {
|
||||||
|
// repo path comes before the command
|
||||||
|
self.args = append([]string{"-C", value}, self.args...)
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) ToArgv() []string {
|
||||||
|
return append([]string{"git"}, self.args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GitCommandBuilder) ToString() string {
|
||||||
|
return strings.Join(self.ToArgv(), " ")
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package git_commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGitCommandBuilder(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
input []string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").
|
||||||
|
Arg("--force-with-lease").
|
||||||
|
Arg("--set-upstream").
|
||||||
|
Arg("origin").
|
||||||
|
Arg("master").
|
||||||
|
ToArgv(),
|
||||||
|
expected: []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").ArgIf(true, "--test").ToArgv(),
|
||||||
|
expected: []string{"git", "push", "--test"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").ArgIf(false, "--test").ToArgv(),
|
||||||
|
expected: []string{"git", "push"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").ArgIfElse(true, "-b", "-a").ToArgv(),
|
||||||
|
expected: []string{"git", "push", "-b"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").ArgIfElse(false, "-a", "-b").ToArgv(),
|
||||||
|
expected: []string{"git", "push", "-b"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").Arg("-a", "-b").ToArgv(),
|
||||||
|
expected: []string{"git", "push", "-a", "-b"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").Config("user.name=foo").Config("user.email=bar").ToArgv(),
|
||||||
|
expected: []string{"git", "-c", "user.email=bar", "-c", "user.name=foo", "push"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: NewGitCmd("push").RepoPath("a/b/c").ToArgv(),
|
||||||
|
expected: []string{"git", "-C", "a/b/c", "push"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
assert.Equal(t, s.input, s.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package git_commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
@@ -9,6 +11,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PatchCommands struct {
|
type PatchCommands struct {
|
||||||
@@ -39,14 +42,61 @@ func NewPatchCommands(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ApplyPatchOpts struct {
|
||||||
|
ThreeWay bool
|
||||||
|
Cached bool
|
||||||
|
Index bool
|
||||||
|
Reverse bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PatchCommands) ApplyCustomPatch(reverse bool) error {
|
||||||
|
patch := self.PatchBuilder.PatchToApply(reverse)
|
||||||
|
|
||||||
|
return self.ApplyPatch(patch, ApplyPatchOpts{
|
||||||
|
Index: true,
|
||||||
|
ThreeWay: true,
|
||||||
|
Reverse: reverse,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PatchCommands) ApplyPatch(patch string, opts ApplyPatchOpts) error {
|
||||||
|
filepath, err := self.SaveTemporaryPatch(patch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.applyPatchFile(filepath, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PatchCommands) applyPatchFile(filepath string, opts ApplyPatchOpts) error {
|
||||||
|
cmdArgs := NewGitCmd("apply").
|
||||||
|
ArgIf(opts.ThreeWay, "--3way").
|
||||||
|
ArgIf(opts.Cached, "--cached").
|
||||||
|
ArgIf(opts.Index, "--index").
|
||||||
|
ArgIf(opts.Reverse, "--reverse").
|
||||||
|
Arg(filepath).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PatchCommands) SaveTemporaryPatch(patch string) (string, error) {
|
||||||
|
filepath := filepath.Join(self.os.GetTempDir(), utils.GetCurrentRepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch")
|
||||||
|
self.Log.Infof("saving temporary patch to %s", filepath)
|
||||||
|
if err := self.os.CreateFileWithContent(filepath, patch); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeletePatchesFromCommit applies a patch in reverse for a commit
|
// DeletePatchesFromCommit applies a patch in reverse for a commit
|
||||||
func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, commitIndex int) error {
|
func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, commitIndex int) error {
|
||||||
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIndex); err != nil {
|
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIndex, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply each patch in reverse
|
// apply each patch in reverse
|
||||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
if err := self.ApplyCustomPatch(true); err != nil {
|
||||||
_ = self.rebase.AbortRebase()
|
_ = self.rebase.AbortRebase()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -67,12 +117,15 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com
|
|||||||
|
|
||||||
func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, sourceCommitIdx int, destinationCommitIdx int) error {
|
func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, sourceCommitIdx int, destinationCommitIdx int) error {
|
||||||
if sourceCommitIdx < destinationCommitIdx {
|
if sourceCommitIdx < destinationCommitIdx {
|
||||||
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, destinationCommitIdx); err != nil {
|
// Passing true for keepCommitsThatBecomeEmpty: if the moved-from
|
||||||
|
// commit becomes empty, we want to keep it, mainly for consistency with
|
||||||
|
// moving the patch to a *later* commit, which behaves the same.
|
||||||
|
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, destinationCommitIdx, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply each patch forward
|
// apply each patch forward
|
||||||
if err := self.PatchBuilder.ApplyPatches(false); err != nil {
|
if err := self.ApplyCustomPatch(false); err != nil {
|
||||||
// Don't abort the rebase here; this might cause conflicts, so give
|
// Don't abort the rebase here; this might cause conflicts, so give
|
||||||
// the user a chance to resolve them
|
// the user a chance to resolve them
|
||||||
return err
|
return err
|
||||||
@@ -121,7 +174,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// apply each patch in reverse
|
// apply each patch in reverse
|
||||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
if err := self.ApplyCustomPatch(true); err != nil {
|
||||||
_ = self.rebase.AbortRebase()
|
_ = self.rebase.AbortRebase()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -144,7 +197,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
|||||||
self.rebase.onSuccessfulContinue = func() error {
|
self.rebase.onSuccessfulContinue = func() error {
|
||||||
// now we should be up to the destination, so let's apply forward these patches to that.
|
// now we should be up to the destination, so let's apply forward these patches to that.
|
||||||
// ideally we would ensure we're on the right commit but I'm not sure if that check is necessary
|
// ideally we would ensure we're on the right commit but I'm not sure if that check is necessary
|
||||||
if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil {
|
if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil {
|
||||||
// Don't abort the rebase here; this might cause conflicts, so give
|
// Don't abort the rebase here; this might cause conflicts, so give
|
||||||
// the user a chance to resolve them
|
// the user a chance to resolve them
|
||||||
return err
|
return err
|
||||||
@@ -168,16 +221,16 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
|||||||
|
|
||||||
func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitIdx int, stash bool) error {
|
func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitIdx int, stash bool) error {
|
||||||
if stash {
|
if stash {
|
||||||
if err := self.stash.Save(self.Tr.StashPrefix + commits[commitIdx].Sha); err != nil {
|
if err := self.stash.Push(self.Tr.StashPrefix + commits[commitIdx].Sha); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx); err != nil {
|
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
if err := self.ApplyCustomPatch(true); err != nil {
|
||||||
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||||
_ = self.rebase.AbortRebase()
|
_ = self.rebase.AbortRebase()
|
||||||
}
|
}
|
||||||
@@ -201,7 +254,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
|
|||||||
|
|
||||||
self.rebase.onSuccessfulContinue = func() error {
|
self.rebase.onSuccessfulContinue = func() error {
|
||||||
// add patches to index
|
// add patches to index
|
||||||
if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil {
|
if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil {
|
||||||
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||||
_ = self.rebase.AbortRebase()
|
_ = self.rebase.AbortRebase()
|
||||||
}
|
}
|
||||||
@@ -222,11 +275,11 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, commitIdx int) error {
|
func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, commitIdx int) error {
|
||||||
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx); err != nil {
|
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
if err := self.ApplyCustomPatch(true); err != nil {
|
||||||
_ = self.rebase.AbortRebase()
|
_ = self.rebase.AbortRebase()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -242,7 +295,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil {
|
if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil {
|
||||||
_ = self.rebase.AbortRebase()
|
_ = self.rebase.AbortRebase()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -267,5 +320,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm
|
|||||||
// only some lines of a range of adjacent added lines. To solve this, we
|
// only some lines of a range of adjacent added lines. To solve this, we
|
||||||
// get the diff of HEAD and the original commit and then apply that.
|
// get the diff of HEAD and the original commit and then apply that.
|
||||||
func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string, error) {
|
func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string, error) {
|
||||||
return self.cmd.New(fmt.Sprintf("git diff HEAD..%s", commit.Sha)).RunWithOutput()
|
cmdArgs := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).RunWithOutput()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package git_commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPatchApplyPatch(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
opts ApplyPatchOpts
|
||||||
|
runner *oscommands.FakeCmdObjRunner
|
||||||
|
test func(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// expectedArgs excludes the last argument which is an indeterminate filename
|
||||||
|
expectFn := func(expectedArgs []string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) {
|
||||||
|
return func(cmdObj oscommands.ICmdObj) (string, error) {
|
||||||
|
args := cmdObj.Args()
|
||||||
|
|
||||||
|
assert.Equal(t, len(args), len(expectedArgs)+1, fmt.Sprintf("unexpected command: %s", cmdObj.ToString()))
|
||||||
|
|
||||||
|
filename := args[len(args)-1]
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filename)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "test", string(content))
|
||||||
|
|
||||||
|
return "", errToReturn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
testName: "valid case",
|
||||||
|
opts: ApplyPatchOpts{Cached: true},
|
||||||
|
runner: oscommands.NewFakeRunner(t).
|
||||||
|
ExpectFunc(expectFn([]string{"git", "apply", "--cached"}, nil)),
|
||||||
|
test: func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "command returns error",
|
||||||
|
opts: ApplyPatchOpts{Cached: true},
|
||||||
|
runner: oscommands.NewFakeRunner(t).
|
||||||
|
ExpectFunc(expectFn([]string{"git", "apply", "--cached"}, errors.New("error"))),
|
||||||
|
test: func(err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
instance := buildPatchCommands(commonDeps{runner: s.runner})
|
||||||
|
s.test(instance.ApplyPatch("test", s.opts))
|
||||||
|
s.runner.CheckForMissingCalls()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, me
|
|||||||
return self.commit.RewordLastCommit(message)
|
return self.commit.RewordLastCommit(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := self.BeginInteractiveRebaseForCommit(commits, index)
|
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f
|
|||||||
return f()
|
return f()
|
||||||
}
|
}
|
||||||
|
|
||||||
err := self.BeginInteractiveRebaseForCommit(commits, index)
|
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -165,9 +165,10 @@ func logTodoChanges(changes []daemon.ChangeTodoAction) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PrepareInteractiveRebaseCommandOpts struct {
|
type PrepareInteractiveRebaseCommandOpts struct {
|
||||||
baseShaOrRoot string
|
baseShaOrRoot string
|
||||||
instruction daemon.Instruction
|
instruction daemon.Instruction
|
||||||
overrideEditor bool
|
overrideEditor bool
|
||||||
|
keepCommitsThatBecomeEmpty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
|
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
|
||||||
@@ -176,26 +177,24 @@ type PrepareInteractiveRebaseCommandOpts struct {
|
|||||||
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
|
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
|
||||||
ex := oscommands.GetLazygitPath()
|
ex := oscommands.GetLazygitPath()
|
||||||
|
|
||||||
|
cmdArgs := NewGitCmd("rebase").
|
||||||
|
Arg("--interactive").
|
||||||
|
Arg("--autostash").
|
||||||
|
Arg("--keep-empty").
|
||||||
|
ArgIf(opts.keepCommitsThatBecomeEmpty && !self.version.IsOlderThan(2, 26, 0), "--empty=keep").
|
||||||
|
Arg("--no-autosquash").
|
||||||
|
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
|
||||||
|
Arg(opts.baseShaOrRoot).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
debug := "FALSE"
|
debug := "FALSE"
|
||||||
if self.Debug {
|
if self.Debug {
|
||||||
debug = "TRUE"
|
debug = "TRUE"
|
||||||
}
|
}
|
||||||
|
|
||||||
emptyArg := " --empty=keep"
|
self.Log.WithField("command", cmdArgs).Debug("RunCommand")
|
||||||
if self.version.IsOlderThan(2, 26, 0) {
|
|
||||||
emptyArg = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
rebaseMergesArg := " --rebase-merges"
|
cmdObj := self.cmd.New(cmdArgs)
|
||||||
if self.version.IsOlderThan(2, 22, 0) {
|
|
||||||
rebaseMergesArg = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty%s --no-autosquash%s %s",
|
|
||||||
emptyArg, rebaseMergesArg, opts.baseShaOrRoot)
|
|
||||||
self.Log.WithField("command", cmdStr).Debug("RunCommand")
|
|
||||||
|
|
||||||
cmdObj := self.cmd.New(cmdStr)
|
|
||||||
|
|
||||||
gitSequenceEditor := ex
|
gitSequenceEditor := ex
|
||||||
|
|
||||||
@@ -228,7 +227,8 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the sha of the commit we just created
|
// Get the sha of the commit we just created
|
||||||
fixupSha, err := self.cmd.New("git rev-parse --verify HEAD").RunWithOutput()
|
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
|
||||||
|
fixupSha, err := self.cmd.New(cmdArgs).RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -243,19 +243,19 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
|||||||
// EditRebaseTodo sets the action for a given rebase commit in the git-rebase-todo file
|
// EditRebaseTodo sets the action for a given rebase commit in the git-rebase-todo file
|
||||||
func (self *RebaseCommands) EditRebaseTodo(commit *models.Commit, action todo.TodoCommand) error {
|
func (self *RebaseCommands) EditRebaseTodo(commit *models.Commit, action todo.TodoCommand) error {
|
||||||
return utils.EditRebaseTodo(
|
return utils.EditRebaseTodo(
|
||||||
filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action)
|
filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MoveTodoDown moves a rebase todo item down by one position
|
// MoveTodoDown moves a rebase todo item down by one position
|
||||||
func (self *RebaseCommands) MoveTodoDown(commit *models.Commit) error {
|
func (self *RebaseCommands) MoveTodoDown(commit *models.Commit) error {
|
||||||
fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo")
|
fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo")
|
||||||
return utils.MoveTodoDown(fileName, commit.Sha, commit.Action)
|
return utils.MoveTodoDown(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MoveTodoDown moves a rebase todo item down by one position
|
// MoveTodoDown moves a rebase todo item down by one position
|
||||||
func (self *RebaseCommands) MoveTodoUp(commit *models.Commit) error {
|
func (self *RebaseCommands) MoveTodoUp(commit *models.Commit) error {
|
||||||
fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo")
|
fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo")
|
||||||
return utils.MoveTodoUp(fileName, commit.Sha, commit.Action)
|
return utils.MoveTodoUp(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
|
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
|
||||||
@@ -265,19 +265,18 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
|
|||||||
shaOrRoot = "--root"
|
shaOrRoot = "--root"
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.runSkipEditorCommand(
|
cmdArgs := NewGitCmd("rebase").
|
||||||
self.cmd.New(
|
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", shaOrRoot).
|
||||||
fmt.Sprintf(
|
ToArgv()
|
||||||
"git rebase --interactive --rebase-merges --autostash --autosquash %s",
|
|
||||||
shaOrRoot,
|
return self.runSkipEditorCommand(self.cmd.New(cmdArgs))
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeginInteractiveRebaseForCommit starts an interactive rebase to edit the current
|
// BeginInteractiveRebaseForCommit starts an interactive rebase to edit the current
|
||||||
// commit and pick all others. After this you'll want to call `self.ContinueRebase()
|
// commit and pick all others. After this you'll want to call `self.ContinueRebase()
|
||||||
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(commits []*models.Commit, commitIndex int) error {
|
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
|
||||||
|
commits []*models.Commit, commitIndex int, keepCommitsThatBecomeEmpty bool,
|
||||||
|
) error {
|
||||||
if len(commits)-1 < commitIndex {
|
if len(commits)-1 < commitIndex {
|
||||||
return errors.New("index outside of range of commits")
|
return errors.New("index outside of range of commits")
|
||||||
}
|
}
|
||||||
@@ -296,9 +295,10 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(commits []*models.Co
|
|||||||
self.os.LogCommand(logTodoChanges(changes), false)
|
self.os.LogCommand(logTodoChanges(changes), false)
|
||||||
|
|
||||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||||
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
|
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
|
||||||
overrideEditor: true,
|
overrideEditor: true,
|
||||||
instruction: daemon.NewChangeTodoActionsInstruction(changes),
|
keepCommitsThatBecomeEmpty: keepCommitsThatBecomeEmpty,
|
||||||
|
instruction: daemon.NewChangeTodoActionsInstruction(changes),
|
||||||
}).Run()
|
}).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +308,9 @@ func (self *RebaseCommands) RebaseBranch(branchName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
|
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
|
||||||
return self.cmd.New("git " + commandType + " --" + command)
|
cmdArgs := NewGitCmd(commandType).Arg("--" + command).ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RebaseCommands) ContinueRebase() error {
|
func (self *RebaseCommands) ContinueRebase() error {
|
||||||
@@ -360,12 +362,14 @@ func (self *RebaseCommands) runSkipEditorCommand(cmdObj oscommands.ICmdObj) erro
|
|||||||
|
|
||||||
// DiscardOldFileChanges discards changes to a file from an old commit
|
// DiscardOldFileChanges discards changes to a file from an old commit
|
||||||
func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, fileName string) error {
|
func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, fileName string) error {
|
||||||
if err := self.BeginInteractiveRebaseForCommit(commits, commitIndex); err != nil {
|
if err := self.BeginInteractiveRebaseForCommit(commits, commitIndex, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
||||||
if err := self.cmd.New("git cat-file -e HEAD^:" + self.cmd.Quote(fileName)).Run(); err != nil {
|
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+fileName).ToArgv()
|
||||||
|
|
||||||
|
if err := self.cmd.New(cmdArgs).Run(); err != nil {
|
||||||
if err := self.os.Remove(fileName); err != nil {
|
if err := self.os.Remove(fileName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
|
|||||||
arg: "master",
|
arg: "master",
|
||||||
gitVersion: &GitVersion{2, 26, 0, ""},
|
gitVersion: &GitVersion{2, 26, 0, ""},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges master`, "", nil),
|
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -39,7 +39,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
|
|||||||
arg: "master",
|
arg: "master",
|
||||||
gitVersion: &GitVersion{2, 26, 0, ""},
|
gitVersion: &GitVersion{2, 26, 0, ""},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges master`, "", errors.New("error")),
|
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", errors.New("error")),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
},
|
},
|
||||||
@@ -49,7 +49,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
|
|||||||
arg: "master",
|
arg: "master",
|
||||||
gitVersion: &GitVersion{2, 25, 5, ""},
|
gitVersion: &GitVersion{2, 25, 5, ""},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash --rebase-merges master`, "", nil),
|
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -59,7 +59,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
|
|||||||
arg: "master",
|
arg: "master",
|
||||||
gitVersion: &GitVersion{2, 21, 9, ""},
|
gitVersion: &GitVersion{2, 21, 9, ""},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash master`, "", nil),
|
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "master"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -78,9 +78,9 @@ func TestRebaseRebaseBranch(t *testing.T) {
|
|||||||
// TestRebaseSkipEditorCommand confirms that SkipEditorCommand injects
|
// TestRebaseSkipEditorCommand confirms that SkipEditorCommand injects
|
||||||
// environment variables that suppress an interactive editor
|
// environment variables that suppress an interactive editor
|
||||||
func TestRebaseSkipEditorCommand(t *testing.T) {
|
func TestRebaseSkipEditorCommand(t *testing.T) {
|
||||||
commandStr := "git blah"
|
cmdArgs := []string{"git", "blah"}
|
||||||
runner := oscommands.NewFakeRunner(t).ExpectFunc(func(cmdObj oscommands.ICmdObj) (string, error) {
|
runner := oscommands.NewFakeRunner(t).ExpectFunc(func(cmdObj oscommands.ICmdObj) (string, error) {
|
||||||
assert.Equal(t, commandStr, cmdObj.ToString())
|
assert.EqualValues(t, cmdArgs, cmdObj.Args())
|
||||||
envVars := cmdObj.GetEnvVars()
|
envVars := cmdObj.GetEnvVars()
|
||||||
for _, regexStr := range []string{
|
for _, regexStr := range []string{
|
||||||
`^VISUAL=.*$`,
|
`^VISUAL=.*$`,
|
||||||
@@ -100,7 +100,7 @@ func TestRebaseSkipEditorCommand(t *testing.T) {
|
|||||||
return "", nil
|
return "", nil
|
||||||
})
|
})
|
||||||
instance := buildRebaseCommands(commonDeps{runner: runner})
|
instance := buildRebaseCommands(commonDeps{runner: runner})
|
||||||
err := instance.runSkipEditorCommand(instance.cmd.New(commandStr))
|
err := instance.runSkipEditorCommand(instance.cmd.New(cmdArgs))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
runner.CheckForMissingCalls()
|
runner.CheckForMissingCalls()
|
||||||
}
|
}
|
||||||
@@ -149,11 +149,11 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
|||||||
commitIndex: 0,
|
commitIndex: 0,
|
||||||
fileName: "test999.txt",
|
fileName: "test999.txt",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges abcdef`, "", nil).
|
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "abcdef"}, "", nil).
|
||||||
Expect(`git cat-file -e HEAD^:"test999.txt"`, "", nil).
|
ExpectGitArgs([]string{"cat-file", "-e", "HEAD^:test999.txt"}, "", nil).
|
||||||
Expect(`git checkout HEAD^ -- "test999.txt"`, "", nil).
|
ExpectGitArgs([]string{"checkout", "HEAD^", "--", "test999.txt"}, "", nil).
|
||||||
Expect(`git commit --amend --no-edit --allow-empty`, "", nil).
|
ExpectGitArgs([]string{"commit", "--amend", "--no-edit", "--allow-empty"}, "", nil).
|
||||||
Expect(`git rebase --continue`, "", nil),
|
ExpectGitArgs([]string{"rebase", "--continue"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -27,12 +26,15 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
|
|||||||
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string) ([]*models.Commit, bool, error) {
|
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string) ([]*models.Commit, bool, error) {
|
||||||
commits := make([]*models.Commit, 0)
|
commits := make([]*models.Commit, 0)
|
||||||
|
|
||||||
filterPathArg := ""
|
cmdArgs := NewGitCmd("log").
|
||||||
if filterPath != "" {
|
Config("log.showSignature=false").
|
||||||
filterPathArg = fmt.Sprintf(" --follow -- %s", self.cmd.Quote(filterPath))
|
Arg("-g").
|
||||||
}
|
Arg("--abbrev=40").
|
||||||
|
Arg("--format=%h%x00%ct%x00%gs%x00%p").
|
||||||
|
ArgIf(filterPath != "", "--follow", "--", filterPath).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
cmdObj := self.cmd.New(fmt.Sprintf(`git -c log.showSignature=false log -g --abbrev=40 --format="%s"%s`, "%h%x00%ct%x00%gs%x00%p", filterPathArg)).DontLog()
|
cmdObj := self.cmd.New(cmdArgs).DontLog()
|
||||||
onlyObtainedNewReflogCommits := false
|
onlyObtainedNewReflogCommits := false
|
||||||
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||||
fields := strings.SplitN(line, "\x00", 4)
|
fields := strings.SplitN(line, "\x00", 4)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "no reflog entries",
|
testName: "no reflog entries",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, "", nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, "", nil),
|
||||||
|
|
||||||
lastReflogCommit: nil,
|
lastReflogCommit: nil,
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
@@ -44,7 +44,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "some reflog entries",
|
testName: "some reflog entries",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: nil,
|
lastReflogCommit: nil,
|
||||||
expectedCommits: []*models.Commit{
|
expectedCommits: []*models.Commit{
|
||||||
@@ -90,7 +90,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "some reflog entries where last commit is given",
|
testName: "some reflog entries where last commit is given",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: &models.Commit{
|
lastReflogCommit: &models.Commit{
|
||||||
Sha: "c3c4b66b64c97ffeecde",
|
Sha: "c3c4b66b64c97ffeecde",
|
||||||
@@ -114,7 +114,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "when passing filterPath",
|
testName: "when passing filterPath",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p" --follow -- "path"`, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--follow", "--", "path"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: &models.Commit{
|
lastReflogCommit: &models.Commit{
|
||||||
Sha: "c3c4b66b64c97ffeecde",
|
Sha: "c3c4b66b64c97ffeecde",
|
||||||
@@ -139,7 +139,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "when command returns error",
|
testName: "when command returns error",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, "", errors.New("haha")),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, "", errors.New("haha")),
|
||||||
|
|
||||||
lastReflogCommit: nil,
|
lastReflogCommit: nil,
|
||||||
filterPath: "",
|
filterPath: "",
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package git_commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RemoteCommands struct {
|
type RemoteCommands struct {
|
||||||
@@ -15,44 +17,52 @@ func NewRemoteCommands(gitCommon *GitCommon) *RemoteCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *RemoteCommands) AddRemote(name string, url string) error {
|
func (self *RemoteCommands) AddRemote(name string, url string) error {
|
||||||
return self.cmd.
|
cmdArgs := NewGitCmd("remote").
|
||||||
New(fmt.Sprintf("git remote add %s %s", self.cmd.Quote(name), self.cmd.Quote(url))).
|
Arg("add", name, url).
|
||||||
Run()
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RemoteCommands) RemoveRemote(name string) error {
|
func (self *RemoteCommands) RemoveRemote(name string) error {
|
||||||
return self.cmd.
|
cmdArgs := NewGitCmd("remote").
|
||||||
New(fmt.Sprintf("git remote remove %s", self.cmd.Quote(name))).
|
Arg("remove", name).
|
||||||
Run()
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RemoteCommands) RenameRemote(oldRemoteName string, newRemoteName string) error {
|
func (self *RemoteCommands) RenameRemote(oldRemoteName string, newRemoteName string) error {
|
||||||
return self.cmd.
|
cmdArgs := NewGitCmd("remote").
|
||||||
New(fmt.Sprintf("git remote rename %s %s", self.cmd.Quote(oldRemoteName), self.cmd.Quote(newRemoteName))).
|
Arg("rename", oldRemoteName, newRemoteName).
|
||||||
Run()
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RemoteCommands) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
|
func (self *RemoteCommands) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
|
||||||
return self.cmd.
|
cmdArgs := NewGitCmd("remote").
|
||||||
New(fmt.Sprintf("git remote set-url %s %s", self.cmd.Quote(remoteName), self.cmd.Quote(updatedUrl))).
|
Arg("set-url", remoteName, updatedUrl).
|
||||||
Run()
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RemoteCommands) DeleteRemoteBranch(remoteName string, branchName string) error {
|
func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName string, branchName string) error {
|
||||||
command := fmt.Sprintf("git push %s --delete %s", self.cmd.Quote(remoteName), self.cmd.Quote(branchName))
|
cmdArgs := NewGitCmd("push").
|
||||||
return self.cmd.New(command).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
Arg(remoteName, "--delete", branchName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckRemoteBranchExists Returns remote branch
|
// CheckRemoteBranchExists Returns remote branch
|
||||||
func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
|
func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
|
||||||
_, err := self.cmd.
|
cmdArgs := NewGitCmd("show-ref").
|
||||||
New(
|
Arg("--verify", "--", fmt.Sprintf("refs/remotes/origin/%s", branchName)).
|
||||||
fmt.Sprintf("git show-ref --verify -- refs/remotes/origin/%s",
|
ToArgv()
|
||||||
self.cmd.Quote(branchName),
|
|
||||||
),
|
_, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
).
|
|
||||||
DontLog().
|
|
||||||
RunWithOutput()
|
|
||||||
|
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ func NewRemoteLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
||||||
remoteBranchesStr, err := self.cmd.New("git branch -r").DontLog().RunWithOutput()
|
cmdArgs := NewGitCmd("branch").Arg("-r").ToArgv()
|
||||||
|
remoteBranchesStr, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,57 +26,95 @@ func NewStashCommands(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) DropNewest() error {
|
func (self *StashCommands) DropNewest() error {
|
||||||
return self.cmd.New("git stash drop").Run()
|
cmdArgs := NewGitCmd("stash").Arg("drop").ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) Drop(index int) error {
|
func (self *StashCommands) Drop(index int) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git stash drop stash@{%d}", index)).Run()
|
cmdArgs := NewGitCmd("stash").Arg("drop", fmt.Sprintf("stash@{%d}", index)).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) Pop(index int) error {
|
func (self *StashCommands) Pop(index int) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git stash pop stash@{%d}", index)).Run()
|
cmdArgs := NewGitCmd("stash").Arg("pop", fmt.Sprintf("stash@{%d}", index)).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) Apply(index int) error {
|
func (self *StashCommands) Apply(index int) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git stash apply stash@{%d}", index)).Run()
|
cmdArgs := NewGitCmd("stash").Arg("apply", fmt.Sprintf("stash@{%d}", index)).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save save stash
|
// Push push stash
|
||||||
func (self *StashCommands) Save(message string) error {
|
func (self *StashCommands) Push(message string) error {
|
||||||
return self.cmd.New("git stash save " + self.cmd.Quote(message)).Run()
|
cmdArgs := NewGitCmd("stash").Arg("push", "-m", message).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) Store(sha string, message string) error {
|
func (self *StashCommands) Store(sha string, message string) error {
|
||||||
trimmedMessage := strings.Trim(message, " \t")
|
trimmedMessage := strings.Trim(message, " \t")
|
||||||
if len(trimmedMessage) > 0 {
|
|
||||||
return self.cmd.New(fmt.Sprintf("git stash store %s -m %s", self.cmd.Quote(sha), self.cmd.Quote(trimmedMessage))).Run()
|
cmdArgs := NewGitCmd("stash").Arg("store").
|
||||||
}
|
ArgIf(trimmedMessage != "", "-m", trimmedMessage).
|
||||||
return self.cmd.New(fmt.Sprintf("git stash store %s", self.cmd.Quote(sha))).Run()
|
Arg(sha).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) Sha(index int) (string, error) {
|
func (self *StashCommands) Sha(index int) (string, error) {
|
||||||
sha, _, err := self.cmd.New(fmt.Sprintf("git rev-parse refs/stash@{%d}", index)).DontLog().RunWithOutputs()
|
cmdArgs := NewGitCmd("rev-parse").
|
||||||
|
Arg(fmt.Sprintf("refs/stash@{%d}", index)).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
sha, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
|
||||||
return strings.Trim(sha, "\r\n"), err
|
return strings.Trim(sha, "\r\n"), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
|
func (self *StashCommands) ShowStashEntryCmdObj(index int, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||||
cmdStr := fmt.Sprintf("git stash show -p --stat --color=%s --unified=%d stash@{%d}", self.UserConfig.Git.Paging.ColorArg, self.UserConfig.Git.DiffContextSize, index)
|
cmdArgs := NewGitCmd("stash").Arg("show").
|
||||||
|
Arg("-p").
|
||||||
|
Arg("--stat").
|
||||||
|
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
|
||||||
|
Arg(fmt.Sprintf("--unified=%d", self.UserConfig.Git.DiffContextSize)).
|
||||||
|
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||||
|
Arg(fmt.Sprintf("stash@{%d}", index)).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(cmdStr).DontLog()
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) StashAndKeepIndex(message string) error {
|
func (self *StashCommands) StashAndKeepIndex(message string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git stash save %s --keep-index", self.cmd.Quote(message))).Run()
|
cmdArgs := NewGitCmd("stash").Arg("push", "--keep-index", "-m", message).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) StashUnstagedChanges(message string) error {
|
func (self *StashCommands) StashUnstagedChanges(message string) error {
|
||||||
if err := self.cmd.New("git commit --no-verify -m \"[lazygit] stashing unstaged changes\"").Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("commit").
|
||||||
|
Arg("--no-verify", "-m", "[lazygit] stashing unstaged changes").
|
||||||
|
ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := self.Save(message); err != nil {
|
if err := self.Push(message); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := self.cmd.New("git reset --soft HEAD^").Run(); err != nil {
|
|
||||||
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("reset").Arg("--soft", "HEAD^").ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -86,23 +124,32 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
|
|||||||
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
|
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
|
||||||
func (self *StashCommands) SaveStagedChanges(message string) error {
|
func (self *StashCommands) SaveStagedChanges(message string) error {
|
||||||
// wrap in 'writing', which uses a mutex
|
// wrap in 'writing', which uses a mutex
|
||||||
if err := self.cmd.New("git stash --keep-index").Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("stash").Arg("--keep-index").ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.Save(message); err != nil {
|
if err := self.Push(message); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.cmd.New("git stash apply stash@{1}").Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("stash").Arg("apply", "stash@{1}").ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.os.PipeCommands("git stash show -p", "git apply -R"); err != nil {
|
if err := self.os.PipeCommands(
|
||||||
|
self.cmd.New(NewGitCmd("stash").Arg("show", "-p").ToArgv()),
|
||||||
|
self.cmd.New(NewGitCmd("apply").Arg("-R").ToArgv()),
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.cmd.New("git stash drop stash@{1}").Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("stash").Arg("drop", "stash@{1}").ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +171,10 @@ func (self *StashCommands) SaveStagedChanges(message string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
|
func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git stash save %s --include-untracked", self.cmd.Quote(message))).Run()
|
return self.cmd.New(
|
||||||
|
NewGitCmd("stash").Arg("push", "--include-untracked", "-m", message).
|
||||||
|
ToArgv(),
|
||||||
|
).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashCommands) Rename(index int, message string) error {
|
func (self *StashCommands) Rename(index int, message string) error {
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
|
|||||||
return self.getUnfilteredStashEntries()
|
return self.getUnfilteredStashEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
rawString, err := self.cmd.New("git stash list --name-only").DontLog().RunWithOutput()
|
cmdArgs := NewGitCmd("stash").Arg("list", "--name-only").ToArgv()
|
||||||
|
rawString, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.getUnfilteredStashEntries()
|
return self.getUnfilteredStashEntries()
|
||||||
}
|
}
|
||||||
@@ -65,7 +66,9 @@ outer:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
||||||
rawString, _ := self.cmd.New("git stash list -z --pretty='%gs'").DontLog().RunWithOutput()
|
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
|
||||||
|
|
||||||
|
rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||||
return self.stashEntryFromLine(line, index)
|
return self.stashEntryFromLine(line, index)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -22,15 +22,14 @@ func TestGetStashEntries(t *testing.T) {
|
|||||||
"No stash entries found",
|
"No stash entries found",
|
||||||
"",
|
"",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git stash list -z --pretty='%gs'`, "", nil),
|
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"}, "", nil),
|
||||||
[]*models.StashEntry{},
|
[]*models.StashEntry{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Several stash entries found",
|
"Several stash entries found",
|
||||||
"",
|
"",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(
|
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"},
|
||||||
`git stash list -z --pretty='%gs'`,
|
|
||||||
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
|
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ func TestStashPop(t *testing.T) {
|
|||||||
|
|
||||||
func TestStashSave(t *testing.T) {
|
func TestStashSave(t *testing.T) {
|
||||||
runner := oscommands.NewFakeRunner(t).
|
runner := oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"stash", "save", "A stash message"}, "", nil)
|
ExpectGitArgs([]string{"stash", "push", "-m", "A stash message"}, "", nil)
|
||||||
instance := buildStashCommands(commonDeps{runner: runner})
|
instance := buildStashCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
assert.NoError(t, instance.Save("A stash message"))
|
assert.NoError(t, instance.Push("A stash message"))
|
||||||
runner.CheckForMissingCalls()
|
runner.CheckForMissingCalls()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ func TestStashStore(t *testing.T) {
|
|||||||
testName: "Non-empty message",
|
testName: "Non-empty message",
|
||||||
sha: "0123456789abcdef",
|
sha: "0123456789abcdef",
|
||||||
message: "New stash name",
|
message: "New stash name",
|
||||||
expected: []string{"stash", "store", "0123456789abcdef", "-m", "New stash name"},
|
expected: []string{"stash", "store", "-m", "New stash name", "0123456789abcdef"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Empty message",
|
testName: "Empty message",
|
||||||
@@ -99,24 +99,34 @@ func TestStashSha(t *testing.T) {
|
|||||||
|
|
||||||
func TestStashStashEntryCmdObj(t *testing.T) {
|
func TestStashStashEntryCmdObj(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
index int
|
index int
|
||||||
contextSize int
|
contextSize int
|
||||||
expected string
|
ignoreWhitespace bool
|
||||||
|
expected []string
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
testName: "Default case",
|
testName: "Default case",
|
||||||
index: 5,
|
index: 5,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
expected: "git stash show -p --stat --color=always --unified=3 stash@{5}",
|
ignoreWhitespace: false,
|
||||||
|
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "stash@{5}"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff with custom context size",
|
testName: "Show diff with custom context size",
|
||||||
index: 5,
|
index: 5,
|
||||||
contextSize: 77,
|
contextSize: 77,
|
||||||
expected: "git stash show -p --stat --color=always --unified=77 stash@{5}",
|
ignoreWhitespace: false,
|
||||||
|
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "stash@{5}"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Default case",
|
||||||
|
index: 5,
|
||||||
|
contextSize: 3,
|
||||||
|
ignoreWhitespace: true,
|
||||||
|
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "stash@{5}"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +137,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
|||||||
userConfig.Git.DiffContextSize = s.contextSize
|
userConfig.Git.DiffContextSize = s.contextSize
|
||||||
instance := buildStashCommands(commonDeps{userConfig: userConfig})
|
instance := buildStashCommands(commonDeps{userConfig: userConfig})
|
||||||
|
|
||||||
cmdStr := instance.ShowStashEntryCmdObj(s.index).ToString()
|
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).Args()
|
||||||
assert.Equal(t, s.expected, cmdStr)
|
assert.Equal(t, s.expected, cmdStr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -152,7 +162,7 @@ func TestStashRename(t *testing.T) {
|
|||||||
expectedShaCmd: []string{"rev-parse", "refs/stash@{3}"},
|
expectedShaCmd: []string{"rev-parse", "refs/stash@{3}"},
|
||||||
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
|
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
|
||||||
expectedDropCmd: []string{"stash", "drop", "stash@{3}"},
|
expectedDropCmd: []string{"stash", "drop", "stash@{3}"},
|
||||||
expectedStoreCmd: []string{"stash", "store", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd", "-m", "New message"},
|
expectedStoreCmd: []string{"stash", "store", "-m", "New message", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Empty message",
|
testName: "Empty message",
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ func (self *StatusCommands) IsBareRepo() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
|
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
|
||||||
res, err := osCommand.Cmd.New("git rev-parse --is-bare-repository").DontLog().RunWithOutput()
|
res, err := osCommand.Cmd.New(
|
||||||
|
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToArgv(),
|
||||||
|
).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package git_commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -82,38 +81,60 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.cmd.New("git -C " + self.cmd.Quote(submodule.Path) + " stash --include-untracked").Run()
|
cmdArgs := NewGitCmd("stash").
|
||||||
|
RepoPath(submodule.Path).
|
||||||
|
Arg("--include-untracked").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
|
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
|
||||||
return self.cmd.New("git submodule update --init --force -- " + self.cmd.Quote(submodule.Path)).Run()
|
cmdArgs := NewGitCmd("submodule").
|
||||||
|
Arg("update", "--init", "--force", "--", submodule.Path).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) UpdateAll() error {
|
func (self *SubmoduleCommands) UpdateAll() error {
|
||||||
// not doing an --init here because the user probably doesn't want that
|
// not doing an --init here because the user probably doesn't want that
|
||||||
return self.cmd.New("git submodule update --force").Run()
|
cmdArgs := NewGitCmd("submodule").Arg("update", "--force").ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||||
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
||||||
|
|
||||||
if err := self.cmd.New("git submodule deinit --force -- " + self.cmd.Quote(submodule.Path)).Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
if strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
NewGitCmd("submodule").
|
||||||
if err := self.cmd.New("git config --file .gitmodules --remove-section submodule." + self.cmd.Quote(submodule.Name)).Run(); err != nil {
|
Arg("deinit", "--force", "--", submodule.Path).ToArgv(),
|
||||||
return err
|
).Run(); err != nil {
|
||||||
}
|
if !strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := self.cmd.New("git config --remove-section submodule." + self.cmd.Quote(submodule.Name)).Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
return err
|
NewGitCmd("config").
|
||||||
}
|
Arg("--file", ".gitmodules", "--remove-section", "submodule."+submodule.Path).
|
||||||
|
ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// if there's an error here about it not existing then we'll just continue to do `git rm`
|
if err := self.cmd.New(
|
||||||
} else {
|
NewGitCmd("config").
|
||||||
|
Arg("--remove-section", "submodule."+submodule.Path).
|
||||||
|
ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.cmd.New("git rm --force -r " + submodule.Path).Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("rm").Arg("--force", "-r", submodule.Path).ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
// if the directory isn't there then that's fine
|
// if the directory isn't there then that's fine
|
||||||
self.Log.Error(err)
|
self.Log.Error(err)
|
||||||
}
|
}
|
||||||
@@ -122,24 +143,35 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
||||||
return self.cmd.
|
cmdArgs := NewGitCmd("submodule").
|
||||||
New(
|
Arg("add").
|
||||||
fmt.Sprintf(
|
Arg("--force").
|
||||||
"git submodule add --force --name %s -- %s %s ",
|
Arg("--name").
|
||||||
self.cmd.Quote(name),
|
Arg(name).
|
||||||
self.cmd.Quote(url),
|
Arg("--").
|
||||||
self.cmd.Quote(path),
|
Arg(url).
|
||||||
)).
|
Arg(path).
|
||||||
Run()
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string) error {
|
func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string) error {
|
||||||
|
setUrlCmdStr := NewGitCmd("config").
|
||||||
|
Arg(
|
||||||
|
"--file", ".gitmodules", "submodule."+name+".url", newUrl,
|
||||||
|
).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
// the set-url command is only for later git versions so we're doing it manually here
|
// the set-url command is only for later git versions so we're doing it manually here
|
||||||
if err := self.cmd.New("git config --file .gitmodules submodule." + self.cmd.Quote(name) + ".url " + self.cmd.Quote(newUrl)).Run(); err != nil {
|
if err := self.cmd.New(setUrlCmdStr).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.cmd.New("git submodule sync -- " + self.cmd.Quote(path)).Run(); err != nil {
|
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", path).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
if err := self.cmd.New(syncCmdStr).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,27 +179,45 @@ func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) Init(path string) error {
|
func (self *SubmoduleCommands) Init(path string) error {
|
||||||
return self.cmd.New("git submodule init -- " + self.cmd.Quote(path)).Run()
|
cmdArgs := NewGitCmd("submodule").Arg("init", "--", path).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) Update(path string) error {
|
func (self *SubmoduleCommands) Update(path string) error {
|
||||||
return self.cmd.New("git submodule update --init -- " + self.cmd.Quote(path)).Run()
|
cmdArgs := NewGitCmd("submodule").Arg("update", "--init", "--", path).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) BulkInitCmdObj() oscommands.ICmdObj {
|
func (self *SubmoduleCommands) BulkInitCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New("git submodule init")
|
cmdArgs := NewGitCmd("submodule").Arg("init").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) BulkUpdateCmdObj() oscommands.ICmdObj {
|
func (self *SubmoduleCommands) BulkUpdateCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New("git submodule update")
|
cmdArgs := NewGitCmd("submodule").Arg("update").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) ForceBulkUpdateCmdObj() oscommands.ICmdObj {
|
func (self *SubmoduleCommands) ForceBulkUpdateCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New("git submodule update --force")
|
cmdArgs := NewGitCmd("submodule").Arg("update", "--force").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) BulkDeinitCmdObj() oscommands.ICmdObj {
|
func (self *SubmoduleCommands) BulkDeinitCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New("git submodule deinit --all --force")
|
cmdArgs := NewGitCmd("submodule").Arg("deinit", "--all", "--force").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubmoduleCommands) ResetSubmodules(submodules []*models.SubmoduleConfig) error {
|
func (self *SubmoduleCommands) ResetSubmodules(submodules []*models.SubmoduleConfig) error {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,34 +24,24 @@ type PushOpts struct {
|
|||||||
SetUpstream bool
|
SetUpstream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) PushCmdObj(opts PushOpts) (oscommands.ICmdObj, error) {
|
func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (oscommands.ICmdObj, error) {
|
||||||
cmdStr := "git push"
|
if opts.UpstreamBranch != "" && opts.UpstreamRemote == "" {
|
||||||
|
return nil, errors.New(self.Tr.MustSpecifyOriginError)
|
||||||
if opts.Force {
|
|
||||||
cmdStr += " --force-with-lease"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.SetUpstream {
|
cmdArgs := NewGitCmd("push").
|
||||||
cmdStr += " --set-upstream"
|
ArgIf(opts.Force, "--force-with-lease").
|
||||||
}
|
ArgIf(opts.SetUpstream, "--set-upstream").
|
||||||
|
ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote).
|
||||||
|
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
if opts.UpstreamRemote != "" {
|
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex)
|
||||||
cmdStr += " " + self.cmd.Quote(opts.UpstreamRemote)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.UpstreamBranch != "" {
|
|
||||||
if opts.UpstreamRemote == "" {
|
|
||||||
return nil, errors.New(self.Tr.MustSpecifyOriginError)
|
|
||||||
}
|
|
||||||
cmdStr += " " + self.cmd.Quote(opts.UpstreamBranch)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdObj := self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex)
|
|
||||||
return cmdObj, nil
|
return cmdObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) Push(opts PushOpts) error {
|
func (self *SyncCommands) Push(task gocui.Task, opts PushOpts) error {
|
||||||
cmdObj, err := self.PushCmdObj(opts)
|
cmdObj, err := self.PushCmdObj(task, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -60,30 +49,33 @@ func (self *SyncCommands) Push(opts PushOpts) error {
|
|||||||
return cmdObj.Run()
|
return cmdObj.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
type FetchOptions struct {
|
func (self *SyncCommands) FetchCmdObj(task gocui.Task) oscommands.ICmdObj {
|
||||||
Background bool
|
cmdArgs := NewGitCmd("fetch").
|
||||||
RemoteName string
|
ArgIf(self.UserConfig.Git.FetchAll, "--all").
|
||||||
BranchName string
|
ToArgv()
|
||||||
|
|
||||||
|
cmdObj := self.cmd.New(cmdArgs)
|
||||||
|
cmdObj.PromptOnCredentialRequest(task)
|
||||||
|
return cmdObj
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch fetch git repo
|
func (self *SyncCommands) Fetch(task gocui.Task) error {
|
||||||
func (self *SyncCommands) Fetch(opts FetchOptions) error {
|
return self.FetchCmdObj(task).Run()
|
||||||
cmdStr := "git fetch"
|
}
|
||||||
|
|
||||||
if opts.RemoteName != "" {
|
func (self *SyncCommands) FetchBackgroundCmdObj() oscommands.ICmdObj {
|
||||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.RemoteName))
|
cmdArgs := NewGitCmd("fetch").
|
||||||
}
|
ArgIf(self.UserConfig.Git.FetchAll, "--all").
|
||||||
if opts.BranchName != "" {
|
ToArgv()
|
||||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.BranchName))
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdObj := self.cmd.New(cmdStr)
|
cmdObj := self.cmd.New(cmdArgs)
|
||||||
if opts.Background {
|
cmdObj.DontLog().FailOnCredentialRequest()
|
||||||
cmdObj.DontLog().FailOnCredentialRequest()
|
cmdObj.WithMutex(self.syncMutex)
|
||||||
} else {
|
return cmdObj
|
||||||
cmdObj.PromptOnCredentialRequest()
|
}
|
||||||
}
|
|
||||||
return cmdObj.WithMutex(self.syncMutex).Run()
|
func (self *SyncCommands) FetchBackground() error {
|
||||||
|
return self.FetchBackgroundCmdObj().Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
type PullOptions struct {
|
type PullOptions struct {
|
||||||
@@ -92,31 +84,32 @@ type PullOptions struct {
|
|||||||
FastForwardOnly bool
|
FastForwardOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) Pull(opts PullOptions) error {
|
func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
||||||
cmdStr := "git pull --no-edit"
|
cmdArgs := NewGitCmd("pull").
|
||||||
|
Arg("--no-edit").
|
||||||
if opts.FastForwardOnly {
|
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||||
cmdStr += " --ff-only"
|
ArgIf(opts.RemoteName != "", opts.RemoteName).
|
||||||
}
|
ArgIf(opts.BranchName != "", opts.BranchName).
|
||||||
|
ToArgv()
|
||||||
if opts.RemoteName != "" {
|
|
||||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.RemoteName))
|
|
||||||
}
|
|
||||||
if opts.BranchName != "" {
|
|
||||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.BranchName))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
// has 'pull.rebase = interactive' configured.
|
// has 'pull.rebase = interactive' configured.
|
||||||
return self.cmd.New(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) FastForward(branchName string, remoteName string, remoteBranchName string) error {
|
func (self *SyncCommands) FastForward(task gocui.Task, branchName string, remoteName string, remoteBranchName string) error {
|
||||||
cmdStr := fmt.Sprintf("git fetch %s %s:%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName), self.cmd.Quote(branchName))
|
cmdArgs := NewGitCmd("fetch").
|
||||||
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
Arg(remoteName).
|
||||||
|
Arg(remoteBranchName + ":" + branchName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SyncCommands) FetchRemote(remoteName string) error {
|
func (self *SyncCommands) FetchRemote(task gocui.Task, remoteName string) error {
|
||||||
cmdStr := fmt.Sprintf("git fetch %s", self.cmd.Quote(remoteName))
|
cmdArgs := NewGitCmd("fetch").
|
||||||
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
Arg(remoteName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package git_commands
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -19,7 +20,7 @@ func TestSyncPush(t *testing.T) {
|
|||||||
testName: "Push with force disabled",
|
testName: "Push with force disabled",
|
||||||
opts: PushOpts{Force: false},
|
opts: PushOpts{Force: false},
|
||||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||||
assert.Equal(t, cmdObj.ToString(), "git push")
|
assert.Equal(t, cmdObj.Args(), []string{"git", "push"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -27,7 +28,7 @@ func TestSyncPush(t *testing.T) {
|
|||||||
testName: "Push with force enabled",
|
testName: "Push with force enabled",
|
||||||
opts: PushOpts{Force: true},
|
opts: PushOpts{Force: true},
|
||||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||||
assert.Equal(t, cmdObj.ToString(), "git push --force-with-lease")
|
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -39,7 +40,7 @@ func TestSyncPush(t *testing.T) {
|
|||||||
UpstreamBranch: "master",
|
UpstreamBranch: "master",
|
||||||
},
|
},
|
||||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||||
assert.Equal(t, cmdObj.ToString(), `git push "origin" "master"`)
|
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "master"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -52,7 +53,7 @@ func TestSyncPush(t *testing.T) {
|
|||||||
SetUpstream: true,
|
SetUpstream: true,
|
||||||
},
|
},
|
||||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||||
assert.Equal(t, cmdObj.ToString(), `git push --set-upstream "origin" "master"`)
|
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "master"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -65,7 +66,7 @@ func TestSyncPush(t *testing.T) {
|
|||||||
SetUpstream: true,
|
SetUpstream: true,
|
||||||
},
|
},
|
||||||
test: func(cmdObj oscommands.ICmdObj, err error) {
|
test: func(cmdObj oscommands.ICmdObj, err error) {
|
||||||
assert.Equal(t, cmdObj.ToString(), `git push --force-with-lease --set-upstream "origin" "master"`)
|
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -88,7 +89,85 @@ func TestSyncPush(t *testing.T) {
|
|||||||
s := s
|
s := s
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
instance := buildSyncCommands(commonDeps{})
|
instance := buildSyncCommands(commonDeps{})
|
||||||
s.test(instance.PushCmdObj(s.opts))
|
task := gocui.NewFakeTask()
|
||||||
|
s.test(instance.PushCmdObj(task, s.opts))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncFetch(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
fetchAllConfig bool
|
||||||
|
test func(oscommands.ICmdObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
testName: "Fetch in foreground (all=false)",
|
||||||
|
fetchAllConfig: false,
|
||||||
|
test: func(cmdObj oscommands.ICmdObj) {
|
||||||
|
assert.True(t, cmdObj.ShouldLog())
|
||||||
|
assert.Equal(t, cmdObj.GetCredentialStrategy(), oscommands.PROMPT)
|
||||||
|
assert.Equal(t, cmdObj.Args(), []string{"git", "fetch"})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Fetch in foreground (all=true)",
|
||||||
|
fetchAllConfig: true,
|
||||||
|
test: func(cmdObj oscommands.ICmdObj) {
|
||||||
|
assert.True(t, cmdObj.ShouldLog())
|
||||||
|
assert.Equal(t, cmdObj.GetCredentialStrategy(), oscommands.PROMPT)
|
||||||
|
assert.Equal(t, cmdObj.Args(), []string{"git", "fetch", "--all"})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
instance := buildSyncCommands(commonDeps{})
|
||||||
|
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
|
||||||
|
task := gocui.NewFakeTask()
|
||||||
|
s.test(instance.FetchCmdObj(task))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncFetchBackground(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
fetchAllConfig bool
|
||||||
|
test func(oscommands.ICmdObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
testName: "Fetch in background (all=false)",
|
||||||
|
fetchAllConfig: false,
|
||||||
|
test: func(cmdObj oscommands.ICmdObj) {
|
||||||
|
assert.False(t, cmdObj.ShouldLog())
|
||||||
|
assert.Equal(t, cmdObj.GetCredentialStrategy(), oscommands.FAIL)
|
||||||
|
assert.Equal(t, cmdObj.Args(), []string{"git", "fetch"})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Fetch in background (all=true)",
|
||||||
|
fetchAllConfig: true,
|
||||||
|
test: func(cmdObj oscommands.ICmdObj) {
|
||||||
|
assert.False(t, cmdObj.ShouldLog())
|
||||||
|
assert.Equal(t, cmdObj.GetCredentialStrategy(), oscommands.FAIL)
|
||||||
|
assert.Equal(t, cmdObj.Args(), []string{"git", "fetch", "--all"})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
instance := buildSyncCommands(commonDeps{})
|
||||||
|
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
|
||||||
|
s.test(instance.FetchBackgroundCmdObj())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import "github.com/jesseduffield/gocui"
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TagCommands struct {
|
type TagCommands struct {
|
||||||
*GitCommon
|
*GitCommon
|
||||||
@@ -15,25 +13,32 @@ func NewTagCommands(gitCommon *GitCommon) *TagCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *TagCommands) CreateLightweight(tagName string, ref string) error {
|
func (self *TagCommands) CreateLightweight(tagName string, ref string) error {
|
||||||
if len(ref) > 0 {
|
cmdArgs := NewGitCmd("tag").Arg("--", tagName).
|
||||||
return self.cmd.New(fmt.Sprintf("git tag -- %s %s", self.cmd.Quote(tagName), self.cmd.Quote(ref))).Run()
|
ArgIf(len(ref) > 0, ref).
|
||||||
} else {
|
ToArgv()
|
||||||
return self.cmd.New(fmt.Sprintf("git tag -- %s", self.cmd.Quote(tagName))).Run()
|
|
||||||
}
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
|
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
|
||||||
if len(ref) > 0 {
|
cmdArgs := NewGitCmd("tag").Arg(tagName).
|
||||||
return self.cmd.New(fmt.Sprintf("git tag %s %s -m %s", self.cmd.Quote(tagName), self.cmd.Quote(ref), self.cmd.Quote(msg))).Run()
|
ArgIf(len(ref) > 0, ref).
|
||||||
} else {
|
Arg("-m", msg).
|
||||||
return self.cmd.New(fmt.Sprintf("git tag %s -m %s", self.cmd.Quote(tagName), self.cmd.Quote(msg))).Run()
|
ToArgv()
|
||||||
}
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TagCommands) Delete(tagName string) error {
|
func (self *TagCommands) Delete(tagName string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git tag -d %s", self.cmd.Quote(tagName))).Run()
|
cmdArgs := NewGitCmd("tag").Arg("-d", tagName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TagCommands) Push(remoteName string, tagName string) error {
|
func (self *TagCommands) Push(task gocui.Task, remoteName string, tagName string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git push %s tag %s", self.cmd.Quote(remoteName), self.cmd.Quote(tagName))).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
cmdArgs := NewGitCmd("push").Arg(remoteName, "tag", tagName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ func NewTagLoader(
|
|||||||
func (self *TagLoader) GetTags() ([]*models.Tag, error) {
|
func (self *TagLoader) GetTags() ([]*models.Tag, error) {
|
||||||
// get remote branches, sorted by creation date (descending)
|
// get remote branches, sorted by creation date (descending)
|
||||||
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
|
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
|
||||||
tagsOutput, err := self.cmd.New(`git tag --list -n --sort=-creatordate`).DontLog().RunWithOutput()
|
cmdArgs := NewGitCmd("tag").Arg("--list", "-n", "--sort=-creatordate").ToArgv()
|
||||||
|
tagsOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ func TestGetTags(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "should return no tags if there are none",
|
testName: "should return no tags if there are none",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git tag --list -n --sort=-creatordate`, "", nil),
|
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, "", nil),
|
||||||
expectedTags: []*models.Tag{},
|
expectedTags: []*models.Tag{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "should return tags if present",
|
testName: "should return tags if present",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git tag --list -n --sort=-creatordate`, tagsOutput, nil),
|
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, tagsOutput, nil),
|
||||||
expectedTags: []*models.Tag{
|
expectedTags: []*models.Tag{
|
||||||
{Name: "tag1", Message: "this is my message"},
|
{Name: "tag1", Message: "this is my message"},
|
||||||
{Name: "tag2", Message: ""},
|
{Name: "tag2", Message: ""},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type GitVersion struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetGitVersion(osCommand *oscommands.OSCommand) (*GitVersion, error) {
|
func GetGitVersion(osCommand *oscommands.OSCommand) (*GitVersion, error) {
|
||||||
versionStr, _, err := osCommand.Cmd.New("git --version").RunWithOutputs()
|
versionStr, _, err := osCommand.Cmd.New(NewGitCmd("--version").ToArgv()).RunWithOutputs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,10 @@ package git_commands
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/generics/slices"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WorkingTreeCommands struct {
|
type WorkingTreeCommands struct {
|
||||||
@@ -33,7 +28,7 @@ func NewWorkingTreeCommands(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
|
func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
|
||||||
return self.cmd.New("git mergetool")
|
return self.cmd.New(NewGitCmd("mergetool").ToArgv())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) OpenMergeTool() error {
|
func (self *WorkingTreeCommands) OpenMergeTool() error {
|
||||||
@@ -46,33 +41,36 @@ func (self *WorkingTreeCommands) StageFile(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) StageFiles(paths []string) error {
|
func (self *WorkingTreeCommands) StageFiles(paths []string) error {
|
||||||
quotedPaths := slices.Map(paths, func(path string) string {
|
cmdArgs := NewGitCmd("add").Arg("--").Arg(paths...).ToArgv()
|
||||||
return self.cmd.Quote(path)
|
|
||||||
})
|
return self.cmd.New(cmdArgs).Run()
|
||||||
return self.cmd.New(fmt.Sprintf("git add -- %s", strings.Join(quotedPaths, " "))).Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StageAll stages all files
|
// StageAll stages all files
|
||||||
func (self *WorkingTreeCommands) StageAll() error {
|
func (self *WorkingTreeCommands) StageAll() error {
|
||||||
return self.cmd.New("git add -A").Run()
|
cmdArgs := NewGitCmd("add").Arg("-A").ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnstageAll unstages all files
|
// UnstageAll unstages all files
|
||||||
func (self *WorkingTreeCommands) UnstageAll() error {
|
func (self *WorkingTreeCommands) UnstageAll() error {
|
||||||
return self.cmd.New("git reset").Run()
|
return self.cmd.New(NewGitCmd("reset").ToArgv()).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnStageFile unstages a file
|
// UnStageFile unstages a file
|
||||||
// we accept an array of filenames for the cases where a file has been renamed i.e.
|
// we accept an array of filenames for the cases where a file has been renamed i.e.
|
||||||
// we accept the current name and the previous name
|
// we accept the current name and the previous name
|
||||||
func (self *WorkingTreeCommands) UnStageFile(fileNames []string, reset bool) error {
|
func (self *WorkingTreeCommands) UnStageFile(fileNames []string, reset bool) error {
|
||||||
command := "git rm --cached --force -- %s"
|
|
||||||
if reset {
|
|
||||||
command = "git reset HEAD -- %s"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, name := range fileNames {
|
for _, name := range fileNames {
|
||||||
err := self.cmd.New(fmt.Sprintf(command, self.cmd.Quote(name))).Run()
|
var cmdArgs []string
|
||||||
|
if reset {
|
||||||
|
cmdArgs = NewGitCmd("reset").Arg("HEAD", "--", name).ToArgv()
|
||||||
|
} else {
|
||||||
|
cmdArgs = NewGitCmd("rm").Arg("--cached", "--force", "--", name).ToArgv()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := self.cmd.New(cmdArgs).Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -134,25 +132,32 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
quotedFileName := self.cmd.Quote(file.Name)
|
|
||||||
|
|
||||||
if file.ShortStatus == "AA" {
|
if file.ShortStatus == "AA" {
|
||||||
if err := self.cmd.New("git checkout --ours -- " + quotedFileName).Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("checkout").Arg("--ours", "--", file.Name).ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := self.cmd.New("git add -- " + quotedFileName).Run(); err != nil {
|
|
||||||
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("add").Arg("--", file.Name).ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.ShortStatus == "DU" {
|
if file.ShortStatus == "DU" {
|
||||||
return self.cmd.New("git rm -- " + quotedFileName).Run()
|
return self.cmd.New(
|
||||||
|
NewGitCmd("rm").Arg("--", file.Name).ToArgv(),
|
||||||
|
).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the file isn't tracked, we assume you want to delete it
|
// if the file isn't tracked, we assume you want to delete it
|
||||||
if file.HasStagedChanges || file.HasMergeConflicts {
|
if file.HasStagedChanges || file.HasMergeConflicts {
|
||||||
if err := self.cmd.New("git reset -- " + quotedFileName).Run(); err != nil {
|
if err := self.cmd.New(
|
||||||
|
NewGitCmd("reset").Arg("--", file.Name).ToArgv(),
|
||||||
|
).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,8 +188,8 @@ func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node IFileNode) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
quotedPath := self.cmd.Quote(node.GetPath())
|
cmdArgs := NewGitCmd("checkout").Arg("--", node.GetPath()).ToArgv()
|
||||||
if err := self.cmd.New("git checkout -- " + quotedPath).Run(); err != nil {
|
if err := self.cmd.New(cmdArgs).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,8 +213,8 @@ func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node IFileNode) error {
|
|||||||
|
|
||||||
// DiscardUnstagedFileChanges directly
|
// DiscardUnstagedFileChanges directly
|
||||||
func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) error {
|
func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) error {
|
||||||
quotedFileName := self.cmd.Quote(file.Name)
|
cmdArgs := NewGitCmd("checkout").Arg("--", file.Name).ToArgv()
|
||||||
return self.cmd.New("git checkout -- " + quotedFileName).Run()
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore adds a file to the gitignore for the repo
|
// Ignore adds a file to the gitignore for the repo
|
||||||
@@ -230,59 +235,30 @@ func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
|
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||||
cachedArg := ""
|
|
||||||
trackedArg := "--"
|
|
||||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||||
quotedPath := self.cmd.Quote(node.GetPath())
|
|
||||||
quotedPrevPath := ""
|
|
||||||
ignoreWhitespaceArg := ""
|
|
||||||
contextSize := self.UserConfig.Git.DiffContextSize
|
|
||||||
if cached {
|
|
||||||
cachedArg = " --cached"
|
|
||||||
}
|
|
||||||
if !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile() {
|
|
||||||
trackedArg = "--no-index -- /dev/null"
|
|
||||||
}
|
|
||||||
if plain {
|
if plain {
|
||||||
colorArg = "never"
|
colorArg = "never"
|
||||||
}
|
}
|
||||||
if ignoreWhitespace {
|
|
||||||
ignoreWhitespaceArg = " --ignore-all-space"
|
|
||||||
}
|
|
||||||
if prevPath := node.GetPreviousPath(); prevPath != "" {
|
|
||||||
quotedPrevPath = " " + self.cmd.Quote(prevPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdStr := fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --color=%s%s%s %s %s%s", contextSize, colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, quotedPath, quotedPrevPath)
|
contextSize := self.UserConfig.Git.DiffContextSize
|
||||||
|
prevPath := node.GetPreviousPath()
|
||||||
|
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
|
||||||
|
|
||||||
return self.cmd.New(cmdStr).DontLog()
|
cmdArgs := NewGitCmd("diff").
|
||||||
}
|
Arg("--submodule").
|
||||||
|
Arg("--no-ext-diff").
|
||||||
|
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||||
|
Arg(fmt.Sprintf("--color=%s", colorArg)).
|
||||||
|
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||||
|
ArgIf(cached, "--cached").
|
||||||
|
ArgIf(noIndex, "--no-index").
|
||||||
|
Arg("--").
|
||||||
|
ArgIf(noIndex, "/dev/null").
|
||||||
|
Arg(node.GetPath()).
|
||||||
|
ArgIf(prevPath != "", prevPath).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) ApplyPatch(patch string, flags ...string) error {
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
filepath, err := self.SaveTemporaryPatch(patch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.ApplyPatchFile(filepath, flags...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) ApplyPatchFile(filepath string, flags ...string) error {
|
|
||||||
flagStr := ""
|
|
||||||
for _, flag := range flags {
|
|
||||||
flagStr += " --" + flag
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.cmd.New(fmt.Sprintf("git apply%s %s", flagStr, self.cmd.Quote(filepath))).Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) SaveTemporaryPatch(patch string) (string, error) {
|
|
||||||
filepath := filepath.Join(self.os.GetTempDir(), utils.GetCurrentRepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch")
|
|
||||||
self.Log.Infof("saving temporary patch to %s", filepath)
|
|
||||||
if err := self.os.CreateFileWithContent(filepath, patch); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return filepath, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
|
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
|
||||||
@@ -296,49 +272,59 @@ func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bo
|
|||||||
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool,
|
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool,
|
||||||
ignoreWhitespace bool,
|
ignoreWhitespace bool,
|
||||||
) oscommands.ICmdObj {
|
) oscommands.ICmdObj {
|
||||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
|
||||||
contextSize := self.UserConfig.Git.DiffContextSize
|
contextSize := self.UserConfig.Git.DiffContextSize
|
||||||
|
|
||||||
|
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||||
if plain {
|
if plain {
|
||||||
colorArg = "never"
|
colorArg = "never"
|
||||||
}
|
}
|
||||||
|
|
||||||
reverseFlag := ""
|
cmdArgs := NewGitCmd("diff").
|
||||||
if reverse {
|
Arg("--submodule").
|
||||||
reverseFlag = " -R"
|
Arg("--no-ext-diff").
|
||||||
}
|
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||||
|
Arg("--no-renames").
|
||||||
|
Arg(fmt.Sprintf("--color=%s", colorArg)).
|
||||||
|
Arg(from).
|
||||||
|
Arg(to).
|
||||||
|
ArgIf(reverse, "-R").
|
||||||
|
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||||
|
Arg("--").
|
||||||
|
Arg(fileName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
ignoreWhitespaceFlag := ""
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
if ignoreWhitespace {
|
|
||||||
ignoreWhitespaceFlag = " --ignore-all-space"
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.cmd.
|
|
||||||
New(
|
|
||||||
fmt.Sprintf(
|
|
||||||
"git diff --submodule --no-ext-diff --unified=%d --no-renames --color=%s%s%s%s%s -- %s",
|
|
||||||
contextSize, colorArg, pad(from), pad(to), reverseFlag, ignoreWhitespaceFlag, self.cmd.Quote(fileName)),
|
|
||||||
).
|
|
||||||
DontLog()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckoutFile checks out the file for the given commit
|
// CheckoutFile checks out the file for the given commit
|
||||||
func (self *WorkingTreeCommands) CheckoutFile(commitSha, fileName string) error {
|
func (self *WorkingTreeCommands) CheckoutFile(commitSha, fileName string) error {
|
||||||
return self.cmd.New(fmt.Sprintf("git checkout %s -- %s", commitSha, self.cmd.Quote(fileName))).Run()
|
cmdArgs := NewGitCmd("checkout").Arg(commitSha, "--", fileName).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .`
|
// DiscardAnyUnstagedFileChanges discards any unstaged file changes via `git checkout -- .`
|
||||||
func (self *WorkingTreeCommands) DiscardAnyUnstagedFileChanges() error {
|
func (self *WorkingTreeCommands) DiscardAnyUnstagedFileChanges() error {
|
||||||
return self.cmd.New("git checkout -- .").Run()
|
cmdArgs := NewGitCmd("checkout").Arg("--", ".").
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
|
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
|
||||||
func (self *WorkingTreeCommands) RemoveTrackedFiles(name string) error {
|
func (self *WorkingTreeCommands) RemoveTrackedFiles(name string) error {
|
||||||
return self.cmd.New("git rm -r --cached -- " + self.cmd.Quote(name)).Run()
|
cmdArgs := NewGitCmd("rm").Arg("-r", "--cached", "--", name).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUntrackedFiles runs `git clean -fd`
|
// RemoveUntrackedFiles runs `git clean -fd`
|
||||||
func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
|
func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
|
||||||
return self.cmd.New("git clean -fd").Run()
|
cmdArgs := NewGitCmd("clean").Arg("-fd").ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetAndClean removes all unstaged changes and removes all untracked files
|
// ResetAndClean removes all unstaged changes and removes all untracked files
|
||||||
@@ -363,23 +349,23 @@ func (self *WorkingTreeCommands) ResetAndClean() error {
|
|||||||
|
|
||||||
// ResetHardHead runs `git reset --hard`
|
// ResetHardHead runs `git reset --hard`
|
||||||
func (self *WorkingTreeCommands) ResetHard(ref string) error {
|
func (self *WorkingTreeCommands) ResetHard(ref string) error {
|
||||||
return self.cmd.New("git reset --hard " + self.cmd.Quote(ref)).Run()
|
cmdArgs := NewGitCmd("reset").Arg("--hard", ref).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetSoft runs `git reset --soft HEAD`
|
// ResetSoft runs `git reset --soft HEAD`
|
||||||
func (self *WorkingTreeCommands) ResetSoft(ref string) error {
|
func (self *WorkingTreeCommands) ResetSoft(ref string) error {
|
||||||
return self.cmd.New("git reset --soft " + self.cmd.Quote(ref)).Run()
|
cmdArgs := NewGitCmd("reset").Arg("--soft", ref).
|
||||||
|
ToArgv()
|
||||||
|
|
||||||
|
return self.cmd.New(cmdArgs).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeCommands) ResetMixed(ref string) error {
|
func (self *WorkingTreeCommands) ResetMixed(ref string) error {
|
||||||
return self.cmd.New("git reset --mixed " + self.cmd.Quote(ref)).Run()
|
cmdArgs := NewGitCmd("reset").Arg("--mixed", ref).
|
||||||
}
|
ToArgv()
|
||||||
|
|
||||||
// so that we don't have unnecessary space in our commands we use this helper function to prepend spaces to args so that in the format string we can go '%s%s%s' and if any args are missing we won't have gaps.
|
return self.cmd.New(cmdArgs).Run()
|
||||||
func pad(str string) string {
|
|
||||||
if str == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return " " + str
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package git_commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
@@ -15,7 +13,7 @@ import (
|
|||||||
|
|
||||||
func TestWorkingTreeStageFile(t *testing.T) {
|
func TestWorkingTreeStageFile(t *testing.T) {
|
||||||
runner := oscommands.NewFakeRunner(t).
|
runner := oscommands.NewFakeRunner(t).
|
||||||
Expect(`git add -- "test.txt"`, "", nil)
|
ExpectGitArgs([]string{"add", "--", "test.txt"}, "", nil)
|
||||||
|
|
||||||
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
|
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
@@ -25,7 +23,7 @@ func TestWorkingTreeStageFile(t *testing.T) {
|
|||||||
|
|
||||||
func TestWorkingTreeStageFiles(t *testing.T) {
|
func TestWorkingTreeStageFiles(t *testing.T) {
|
||||||
runner := oscommands.NewFakeRunner(t).
|
runner := oscommands.NewFakeRunner(t).
|
||||||
Expect(`git add -- "test.txt" "test2.txt"`, "", nil)
|
ExpectGitArgs([]string{"add", "--", "test.txt", "test2.txt"}, "", nil)
|
||||||
|
|
||||||
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
|
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
@@ -46,7 +44,7 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
|
|||||||
testName: "Remove an untracked file from staging",
|
testName: "Remove an untracked file from staging",
|
||||||
reset: false,
|
reset: false,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git rm --cached --force -- "test.txt"`, "", nil),
|
ExpectGitArgs([]string{"rm", "--cached", "--force", "--", "test.txt"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -55,7 +53,7 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
|
|||||||
testName: "Remove a tracked file from staging",
|
testName: "Remove a tracked file from staging",
|
||||||
reset: true,
|
reset: true,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git reset HEAD -- "test.txt"`, "", nil),
|
ExpectGitArgs([]string{"reset", "HEAD", "--", "test.txt"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -92,7 +90,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|||||||
},
|
},
|
||||||
removeFile: func(string) error { return nil },
|
removeFile: func(string) error { return nil },
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git reset -- "test"`, "", errors.New("error")),
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", errors.New("error")),
|
||||||
expectedError: "error",
|
expectedError: "error",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -117,7 +115,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|||||||
},
|
},
|
||||||
removeFile: func(string) error { return nil },
|
removeFile: func(string) error { return nil },
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git checkout -- "test"`, "", errors.New("error")),
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", errors.New("error")),
|
||||||
expectedError: "error",
|
expectedError: "error",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -129,7 +127,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|||||||
},
|
},
|
||||||
removeFile: func(string) error { return nil },
|
removeFile: func(string) error { return nil },
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git checkout -- "test"`, "", nil),
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
||||||
expectedError: "",
|
expectedError: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -141,8 +139,8 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|||||||
},
|
},
|
||||||
removeFile: func(string) error { return nil },
|
removeFile: func(string) error { return nil },
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git reset -- "test"`, "", nil).
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
|
||||||
Expect(`git checkout -- "test"`, "", nil),
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
||||||
expectedError: "",
|
expectedError: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -154,8 +152,8 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|||||||
},
|
},
|
||||||
removeFile: func(string) error { return nil },
|
removeFile: func(string) error { return nil },
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git reset -- "test"`, "", nil).
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
|
||||||
Expect(`git checkout -- "test"`, "", nil),
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
||||||
expectedError: "",
|
expectedError: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -171,7 +169,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git reset -- "test"`, "", nil),
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil),
|
||||||
expectedError: "",
|
expectedError: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -233,7 +231,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "cached",
|
testName: "cached",
|
||||||
@@ -247,7 +245,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --cached -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "plain",
|
testName: "plain",
|
||||||
@@ -261,7 +259,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=never -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "File not tracked and file has no staged changes",
|
testName: "File not tracked and file has no staged changes",
|
||||||
@@ -275,7 +273,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --no-index -- /dev/null "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Default case (ignore whitespace)",
|
testName: "Default case (ignore whitespace)",
|
||||||
@@ -289,7 +287,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: true,
|
ignoreWhitespace: true,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --ignore-all-space -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff with custom context size",
|
testName: "Show diff with custom context size",
|
||||||
@@ -303,7 +301,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
contextSize: 17,
|
contextSize: 17,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=17 --color=always -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +343,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=3 --no-renames --color=always 1234567890 0987654321 -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff with custom context size",
|
testName: "Show diff with custom context size",
|
||||||
@@ -356,7 +354,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
contextSize: 123,
|
contextSize: 123,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=123 --no-renames --color=always 1234567890 0987654321 -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Default case (ignore whitespace)",
|
testName: "Default case (ignore whitespace)",
|
||||||
@@ -367,7 +365,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
|||||||
ignoreWhitespace: true,
|
ignoreWhitespace: true,
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git diff --submodule --no-ext-diff --unified=3 --no-renames --color=always 1234567890 0987654321 --ignore-all-space -- "test.txt"`, expectedResult, nil),
|
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +400,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
|
|||||||
commitSha: "11af912",
|
commitSha: "11af912",
|
||||||
fileName: "test999.txt",
|
fileName: "test999.txt",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git checkout 11af912 -- "test999.txt"`, "", nil),
|
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -412,7 +410,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
|
|||||||
commitSha: "11af912",
|
commitSha: "11af912",
|
||||||
fileName: "test999.txt",
|
fileName: "test999.txt",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git checkout 11af912 -- "test999.txt"`, "", errors.New("error")),
|
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
},
|
},
|
||||||
@@ -430,60 +428,6 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorkingTreeApplyPatch(t *testing.T) {
|
|
||||||
type scenario struct {
|
|
||||||
testName string
|
|
||||||
runner *oscommands.FakeCmdObjRunner
|
|
||||||
test func(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectFn := func(regexStr string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) {
|
|
||||||
return func(cmdObj oscommands.ICmdObj) (string, error) {
|
|
||||||
re := regexp.MustCompile(regexStr)
|
|
||||||
cmdStr := cmdObj.ToString()
|
|
||||||
matches := re.FindStringSubmatch(cmdStr)
|
|
||||||
assert.Equal(t, 2, len(matches), fmt.Sprintf("unexpected command: %s", cmdStr))
|
|
||||||
|
|
||||||
filename := matches[1]
|
|
||||||
|
|
||||||
content, err := os.ReadFile(filename)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "test", string(content))
|
|
||||||
|
|
||||||
return "", errToReturn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios := []scenario{
|
|
||||||
{
|
|
||||||
testName: "valid case",
|
|
||||||
runner: oscommands.NewFakeRunner(t).
|
|
||||||
ExpectFunc(expectFn(`git apply --cached "(.*)"`, nil)),
|
|
||||||
test: func(err error) {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "command returns error",
|
|
||||||
runner: oscommands.NewFakeRunner(t).
|
|
||||||
ExpectFunc(expectFn(`git apply --cached "(.*)"`, errors.New("error"))),
|
|
||||||
test: func(err error) {
|
|
||||||
assert.Error(t, err)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range scenarios {
|
|
||||||
s := s
|
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
|
||||||
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
||||||
s.test(instance.ApplyPatch("test", "cached"))
|
|
||||||
s.runner.CheckForMissingCalls()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
|
func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
@@ -497,7 +441,7 @@ func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
|
|||||||
testName: "valid case",
|
testName: "valid case",
|
||||||
file: &models.File{Name: "test.txt"},
|
file: &models.File{Name: "test.txt"},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git checkout -- "test.txt"`, "", nil),
|
ExpectGitArgs([]string{"checkout", "--", "test.txt"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -525,7 +469,7 @@ func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "valid case",
|
testName: "valid case",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git checkout -- .`, "", nil),
|
ExpectGitArgs([]string{"checkout", "--", "."}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -553,7 +497,7 @@ func TestWorkingTreeRemoveUntrackedFiles(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "valid case",
|
testName: "valid case",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git clean -fd`, "", nil),
|
ExpectGitArgs([]string{"clean", "-fd"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@@ -583,7 +527,7 @@ func TestWorkingTreeResetHard(t *testing.T) {
|
|||||||
"valid case",
|
"valid case",
|
||||||
"HEAD",
|
"HEAD",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git reset --hard "HEAD"`, "", nil),
|
ExpectGitArgs([]string{"reset", "--hard", "HEAD"}, "", nil),
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package git_config
|
|||||||
import (
|
import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -20,6 +21,7 @@ type CachedGitConfig struct {
|
|||||||
cache map[string]string
|
cache map[string]string
|
||||||
runGitConfigCmd func(*exec.Cmd) (string, error)
|
runGitConfigCmd func(*exec.Cmd) (string, error)
|
||||||
log *logrus.Entry
|
log *logrus.Entry
|
||||||
|
mutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStdCachedGitConfig(log *logrus.Entry) *CachedGitConfig {
|
func NewStdCachedGitConfig(log *logrus.Entry) *CachedGitConfig {
|
||||||
@@ -31,10 +33,14 @@ func NewCachedGitConfig(runGitConfigCmd func(*exec.Cmd) (string, error), log *lo
|
|||||||
cache: make(map[string]string),
|
cache: make(map[string]string),
|
||||||
runGitConfigCmd: runGitConfigCmd,
|
runGitConfigCmd: runGitConfigCmd,
|
||||||
log: log,
|
log: log,
|
||||||
|
mutex: sync.Mutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CachedGitConfig) Get(key string) string {
|
func (self *CachedGitConfig) Get(key string) string {
|
||||||
|
self.mutex.Lock()
|
||||||
|
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.Debugf("using cache for key " + key)
|
||||||
return value
|
return value
|
||||||
@@ -46,6 +52,9 @@ func (self *CachedGitConfig) Get(key string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CachedGitConfig) GetGeneral(args string) string {
|
func (self *CachedGitConfig) GetGeneral(args string) string {
|
||||||
|
self.mutex.Lock()
|
||||||
|
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.Debugf("using cache for args " + args)
|
||||||
return value
|
return value
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ var bitbucketServiceDef = ServiceDefinition{
|
|||||||
|
|
||||||
var gitLabServiceDef = ServiceDefinition{
|
var gitLabServiceDef = ServiceDefinition{
|
||||||
provider: "gitlab",
|
provider: "gitlab",
|
||||||
pullRequestURLIntoDefaultBranch: "/merge_requests/new?merge_request[source_branch]={{.From}}",
|
pullRequestURLIntoDefaultBranch: "/-/merge_requests/new?merge_request[source_branch]={{.From}}",
|
||||||
pullRequestURLIntoTargetBranch: "/merge_requests/new?merge_request[source_branch]={{.From}}&merge_request[target_branch]={{.To}}",
|
pullRequestURLIntoTargetBranch: "/-/merge_requests/new?merge_request[source_branch]={{.From}}&merge_request[target_branch]={{.To}}",
|
||||||
commitURL: "/commit/{{.CommitSha}}",
|
commitURL: "/-/commit/{{.CommitSha}}",
|
||||||
regexStrings: defaultUrlRegexStrings,
|
regexStrings: defaultUrlRegexStrings,
|
||||||
repoURLTemplate: defaultRepoURLTemplate,
|
repoURLTemplate: defaultRepoURLTemplate,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "git@gitlab.com:peter/calculator.git",
|
remoteUrl: "git@gitlab.com:peter/calculator.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/peter/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
|
assert.Equal(t, "https://gitlab.com/peter/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -121,7 +121,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
|
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
|
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -130,7 +130,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "https://gitlab.com/peter/public/calculator.git",
|
remoteUrl: "https://gitlab.com/peter/public/calculator.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
|
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -140,7 +140,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "git@gitlab.com:peter/calculator.git",
|
remoteUrl: "git@gitlab.com:peter/calculator.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/peter/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
|
assert.Equal(t, "https://gitlab.com/peter/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -150,7 +150,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
|
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
|
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -160,7 +160,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "https://gitlab.com/peter/public/calculator.git",
|
remoteUrl: "https://gitlab.com/peter/public/calculator.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
|
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -373,7 +373,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "git@gitlab.com:me/public/repo-with-issues.git",
|
remoteUrl: "git@gitlab.com:me/public/repo-with-issues.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/merge_requests/new?merge_request[source_branch]=feature%2FsomeIssue%23123&merge_request[target_branch]=master", url)
|
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/-/merge_requests/new?merge_request[source_branch]=feature%2FsomeIssue%23123&merge_request[target_branch]=master", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -383,7 +383,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
|||||||
remoteUrl: "git@gitlab.com:me/public/repo-with-issues.git",
|
remoteUrl: "git@gitlab.com:me/public/repo-with-issues.git",
|
||||||
test: func(url string, err error) {
|
test: func(url string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/merge_requests/new?merge_request[source_branch]=yolo&merge_request[target_branch]=archive%2Fnever-ending-feature%23666", url)
|
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/-/merge_requests/new?merge_request[source_branch]=yolo&merge_request[target_branch]=archive%2Fnever-ending-feature%23666", url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ package models
|
|||||||
type Branch struct {
|
type Branch struct {
|
||||||
Name string
|
Name string
|
||||||
// the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf'
|
// the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf'
|
||||||
DisplayName string
|
DisplayName string
|
||||||
Recency string
|
// indicator of when the branch was last checked out e.g. '2d', '3m'
|
||||||
Pushables string
|
Recency string
|
||||||
Pullables string
|
// how many commits ahead we are from the remote branch (how many commits we can push)
|
||||||
|
Pushables string
|
||||||
|
// how many commits behind we are from the remote branch (how many commits we can pull)
|
||||||
|
Pullables string
|
||||||
|
// whether the remote branch is 'gone' i.e. we're tracking a remote branch that has been deleted
|
||||||
UpstreamGone bool
|
UpstreamGone bool
|
||||||
|
// whether this is the current branch. Exactly one branch should have this be true
|
||||||
Head bool
|
Head bool
|
||||||
DetachedHead bool
|
DetachedHead bool
|
||||||
// if we have a named remote locally this will be the name of that remote e.g.
|
// if we have a named remote locally this will be the name of that remote e.g.
|
||||||
@@ -17,6 +22,10 @@ type Branch struct {
|
|||||||
// 'git@github.com:tiwood/lazygit.git'
|
// 'git@github.com:tiwood/lazygit.git'
|
||||||
UpstreamRemote string
|
UpstreamRemote string
|
||||||
UpstreamBranch string
|
UpstreamBranch string
|
||||||
|
// subject line in commit message
|
||||||
|
Subject string
|
||||||
|
// commit hash
|
||||||
|
CommitHash string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Branch) FullRefName() string {
|
func (b *Branch) FullRefName() string {
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const (
|
|||||||
// Conveniently for us, the todo package starts the enum at 1, and given
|
// Conveniently for us, the todo package starts the enum at 1, and given
|
||||||
// that it doesn't have a "none" value, we're setting ours to 0
|
// that it doesn't have a "none" value, we're setting ours to 0
|
||||||
ActionNone todo.TodoCommand = 0
|
ActionNone todo.TodoCommand = 0
|
||||||
|
// "Comment" is the last one of the todo package's enum entries
|
||||||
|
ActionConflict = todo.Comment + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// Commit : A git commit
|
// Commit : A git commit
|
||||||
|
|||||||
@@ -15,3 +15,15 @@ func (f *CommitFile) ID() string {
|
|||||||
func (f *CommitFile) Description() string {
|
func (f *CommitFile) Description() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *CommitFile) Added() bool {
|
||||||
|
return f.ChangeStatus == "A"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *CommitFile) Deleted() bool {
|
||||||
|
return f.ChangeStatus == "D"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *CommitFile) GetPath() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package oscommands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/samber/lo"
|
||||||
"github.com/sasha-s/go-deadlock"
|
"github.com/sasha-s/go-deadlock"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,6 +18,9 @@ type ICmdObj interface {
|
|||||||
// into a terminal e.g. 'sh -c git commit' as opposed to 'sh -c "git commit"'
|
// into a terminal e.g. 'sh -c git commit' as opposed to 'sh -c "git commit"'
|
||||||
ToString() string
|
ToString() string
|
||||||
|
|
||||||
|
// outputs args vector e.g. ["git", "commit", "-m", "my message"]
|
||||||
|
Args() []string
|
||||||
|
|
||||||
AddEnvVars(...string) ICmdObj
|
AddEnvVars(...string) ICmdObj
|
||||||
GetEnvVars() []string
|
GetEnvVars() []string
|
||||||
|
|
||||||
@@ -51,18 +57,24 @@ type ICmdObj interface {
|
|||||||
// returns true if IgnoreEmptyError() was called
|
// returns true if IgnoreEmptyError() was called
|
||||||
ShouldIgnoreEmptyError() bool
|
ShouldIgnoreEmptyError() bool
|
||||||
|
|
||||||
PromptOnCredentialRequest() ICmdObj
|
PromptOnCredentialRequest(task gocui.Task) ICmdObj
|
||||||
FailOnCredentialRequest() ICmdObj
|
FailOnCredentialRequest() ICmdObj
|
||||||
|
|
||||||
WithMutex(mutex *deadlock.Mutex) ICmdObj
|
WithMutex(mutex *deadlock.Mutex) ICmdObj
|
||||||
Mutex() *deadlock.Mutex
|
Mutex() *deadlock.Mutex
|
||||||
|
|
||||||
GetCredentialStrategy() CredentialStrategy
|
GetCredentialStrategy() CredentialStrategy
|
||||||
|
GetTask() gocui.Task
|
||||||
|
|
||||||
|
Clone() ICmdObj
|
||||||
}
|
}
|
||||||
|
|
||||||
type CmdObj struct {
|
type CmdObj struct {
|
||||||
cmdStr string
|
// the secureexec package will swap out the first arg with the full path to the binary,
|
||||||
cmd *exec.Cmd
|
// so we store these args separately so that ToString() will output the original
|
||||||
|
args []string
|
||||||
|
|
||||||
|
cmd *exec.Cmd
|
||||||
|
|
||||||
runner ICmdObjRunner
|
runner ICmdObjRunner
|
||||||
|
|
||||||
@@ -77,6 +89,7 @@ type CmdObj struct {
|
|||||||
|
|
||||||
// if set to true, it means we might be asked to enter a username/password by this command.
|
// if set to true, it means we might be asked to enter a username/password by this command.
|
||||||
credentialStrategy CredentialStrategy
|
credentialStrategy CredentialStrategy
|
||||||
|
task gocui.Task
|
||||||
|
|
||||||
// can be set so that we don't run certain commands simultaneously
|
// can be set so that we don't run certain commands simultaneously
|
||||||
mutex *deadlock.Mutex
|
mutex *deadlock.Mutex
|
||||||
@@ -104,7 +117,19 @@ func (self *CmdObj) GetCmd() *exec.Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CmdObj) ToString() string {
|
func (self *CmdObj) ToString() string {
|
||||||
return self.cmdStr
|
// if a given arg contains a space, we need to wrap it in quotes
|
||||||
|
quotedArgs := lo.Map(self.args, func(arg string, _ int) string {
|
||||||
|
if strings.Contains(arg, " ") {
|
||||||
|
return `"` + arg + `"`
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
})
|
||||||
|
|
||||||
|
return strings.Join(quotedArgs, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CmdObj) Args() []string {
|
||||||
|
return self.args
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
|
func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
|
||||||
@@ -172,8 +197,9 @@ func (self *CmdObj) RunAndProcessLines(onLine func(line string) (bool, error)) e
|
|||||||
return self.runner.RunAndProcessLines(self, onLine)
|
return self.runner.RunAndProcessLines(self, onLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CmdObj) PromptOnCredentialRequest() ICmdObj {
|
func (self *CmdObj) PromptOnCredentialRequest(task gocui.Task) ICmdObj {
|
||||||
self.credentialStrategy = PROMPT
|
self.credentialStrategy = PROMPT
|
||||||
|
self.task = task
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
@@ -187,3 +213,21 @@ func (self *CmdObj) FailOnCredentialRequest() ICmdObj {
|
|||||||
func (self *CmdObj) GetCredentialStrategy() CredentialStrategy {
|
func (self *CmdObj) GetCredentialStrategy() CredentialStrategy {
|
||||||
return self.credentialStrategy
|
return self.credentialStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CmdObj) GetTask() gocui.Task {
|
||||||
|
return self.task
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CmdObj) Clone() ICmdObj {
|
||||||
|
clone := &CmdObj{}
|
||||||
|
*clone = *self
|
||||||
|
clone.cmd = cloneCmd(self.cmd)
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneCmd(cmd *exec.Cmd) *exec.Cmd {
|
||||||
|
clone := &exec.Cmd{}
|
||||||
|
*clone = *cmd
|
||||||
|
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ICmdObjBuilder interface {
|
type ICmdObjBuilder interface {
|
||||||
// New returns a new command object based on the string provided
|
// NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object.
|
||||||
New(cmdStr 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
|
||||||
// NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object. This can be useful when you don't want to worry about whitespace and quoting and stuff.
|
|
||||||
NewFromArgs(args []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
|
||||||
}
|
}
|
||||||
@@ -28,24 +26,12 @@ type CmdObjBuilder struct {
|
|||||||
// poor man's version of explicitly saying that struct X implements interface Y
|
// poor man's version of explicitly saying that struct X implements interface Y
|
||||||
var _ ICmdObjBuilder = &CmdObjBuilder{}
|
var _ ICmdObjBuilder = &CmdObjBuilder{}
|
||||||
|
|
||||||
func (self *CmdObjBuilder) New(cmdStr string) ICmdObj {
|
func (self *CmdObjBuilder) New(args []string) ICmdObj {
|
||||||
args := str.ToArgv(cmdStr)
|
|
||||||
cmd := secureexec.Command(args[0], args[1:]...)
|
cmd := secureexec.Command(args[0], args[1:]...)
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
|
|
||||||
return &CmdObj{
|
return &CmdObj{
|
||||||
cmdStr: cmdStr,
|
args: args,
|
||||||
cmd: cmd,
|
|
||||||
runner: self.runner,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CmdObjBuilder) NewFromArgs(args []string) ICmdObj {
|
|
||||||
cmd := secureexec.Command(args[0], args[1:]...)
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
|
|
||||||
return &CmdObj{
|
|
||||||
cmdStr: strings.Join(args, " "),
|
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
runner: self.runner,
|
runner: self.runner,
|
||||||
}
|
}
|
||||||
@@ -67,8 +53,9 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
|
|||||||
quotedCommand = self.Quote(commandStr)
|
quotedCommand = self.Quote(commandStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
shellCommand := fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand)
|
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
|
||||||
return self.New(shellCommand)
|
|
||||||
|
return self.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {
|
func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {
|
||||||
@@ -80,6 +67,9 @@ func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CHARS_REQUIRING_QUOTES = "\"\\$` "
|
||||||
|
|
||||||
|
// If you update this method, be sure to update CHARS_REQUIRING_QUOTES
|
||||||
func (self *CmdObjBuilder) Quote(message string) string {
|
func (self *CmdObjBuilder) Quote(message string) string {
|
||||||
var quote string
|
var quote string
|
||||||
if self.platform.OS == "windows" {
|
if self.platform.OS == "windows" {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -19,15 +20,6 @@ type ICmdObjRunner interface {
|
|||||||
RunAndProcessLines(cmdObj ICmdObj, onLine func(line string) (bool, error)) error
|
RunAndProcessLines(cmdObj ICmdObj, onLine func(line string) (bool, error)) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type CredentialType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
Password CredentialType = iota
|
|
||||||
Username
|
|
||||||
Passphrase
|
|
||||||
PIN
|
|
||||||
)
|
|
||||||
|
|
||||||
type cmdObjRunner struct {
|
type cmdObjRunner struct {
|
||||||
log *logrus.Entry
|
log *logrus.Entry
|
||||||
guiIO *guiIO
|
guiIO *guiIO
|
||||||
@@ -182,26 +174,6 @@ func (self *cmdObjRunner) RunAndProcessLines(cmdObj ICmdObj, onLine func(line st
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whenever we're asked for a password we just enter a newline, which will
|
|
||||||
// eventually cause the command to fail.
|
|
||||||
var failPromptFn = func(CredentialType) string { return "\n" }
|
|
||||||
|
|
||||||
func (self *cmdObjRunner) runWithCredentialHandling(cmdObj ICmdObj) error {
|
|
||||||
var promptFn func(CredentialType) string
|
|
||||||
|
|
||||||
switch cmdObj.GetCredentialStrategy() {
|
|
||||||
case PROMPT:
|
|
||||||
promptFn = self.guiIO.promptForCredentialFn
|
|
||||||
case FAIL:
|
|
||||||
promptFn = failPromptFn
|
|
||||||
case NONE:
|
|
||||||
// we should never land here
|
|
||||||
return errors.New("runWithCredentialHandling called but cmdObj does not have a credential strategy")
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.runAndDetectCredentialRequest(cmdObj, promptFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *cmdObjRunner) logCmdObj(cmdObj ICmdObj) {
|
func (self *cmdObjRunner) logCmdObj(cmdObj ICmdObj) {
|
||||||
self.guiIO.logCommandFn(cmdObj.ToString(), true)
|
self.guiIO.logCommandFn(cmdObj.ToString(), true)
|
||||||
}
|
}
|
||||||
@@ -233,25 +205,6 @@ func (self *cmdObjRunner) runAndStream(cmdObj ICmdObj) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
|
|
||||||
func (self *cmdObjRunner) runAndDetectCredentialRequest(
|
|
||||||
cmdObj ICmdObj,
|
|
||||||
promptUserForCredential func(CredentialType) string,
|
|
||||||
) error {
|
|
||||||
// setting the output to english so we can parse it for a username/password request
|
|
||||||
cmdObj.AddEnvVars("LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")
|
|
||||||
|
|
||||||
return self.runAndStreamAux(cmdObj, func(handler *cmdHandler, cmdWriter io.Writer) {
|
|
||||||
tr := io.TeeReader(handler.stdoutPipe, cmdWriter)
|
|
||||||
|
|
||||||
go utils.Safe(func() {
|
|
||||||
self.processOutput(tr, handler.stdinPipe, promptUserForCredential)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *cmdObjRunner) runAndStreamAux(
|
func (self *cmdObjRunner) runAndStreamAux(
|
||||||
cmdObj ICmdObj,
|
cmdObj ICmdObj,
|
||||||
onRun func(*cmdHandler, io.Writer),
|
onRun func(*cmdHandler, io.Writer),
|
||||||
@@ -296,13 +249,79 @@ func (self *cmdObjRunner) runAndStreamAux(
|
|||||||
if cmdObj.ShouldIgnoreEmptyError() {
|
if cmdObj.ShouldIgnoreEmptyError() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New(stdout.String())
|
stdoutStr := stdout.String()
|
||||||
|
if stdoutStr != "" {
|
||||||
|
return errors.New(stdoutStr)
|
||||||
|
}
|
||||||
|
return errors.New("Command exited with non-zero exit code, but no output")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *cmdObjRunner) processOutput(reader io.Reader, writer io.Writer, promptUserForCredential func(CredentialType) string) {
|
type CredentialType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Password CredentialType = iota
|
||||||
|
Username
|
||||||
|
Passphrase
|
||||||
|
PIN
|
||||||
|
)
|
||||||
|
|
||||||
|
// Whenever we're asked for a password we just enter a newline, which will
|
||||||
|
// eventually cause the command to fail.
|
||||||
|
var failPromptFn = func(CredentialType) <-chan string {
|
||||||
|
ch := make(chan string)
|
||||||
|
go func() {
|
||||||
|
ch <- "\n"
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *cmdObjRunner) runWithCredentialHandling(cmdObj ICmdObj) error {
|
||||||
|
promptFn, err := self.getCredentialPromptFn(cmdObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.runAndDetectCredentialRequest(cmdObj, promptFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *cmdObjRunner) getCredentialPromptFn(cmdObj ICmdObj) (func(CredentialType) <-chan string, error) {
|
||||||
|
switch cmdObj.GetCredentialStrategy() {
|
||||||
|
case PROMPT:
|
||||||
|
return self.guiIO.promptForCredentialFn, nil
|
||||||
|
case FAIL:
|
||||||
|
return failPromptFn, nil
|
||||||
|
default:
|
||||||
|
// we should never land here
|
||||||
|
return nil, errors.New("runWithCredentialHandling called but cmdObj does not have a credential strategy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
|
||||||
|
func (self *cmdObjRunner) runAndDetectCredentialRequest(
|
||||||
|
cmdObj ICmdObj,
|
||||||
|
promptUserForCredential func(CredentialType) <-chan string,
|
||||||
|
) error {
|
||||||
|
// setting the output to english so we can parse it for a username/password request
|
||||||
|
cmdObj.AddEnvVars("LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")
|
||||||
|
|
||||||
|
return self.runAndStreamAux(cmdObj, func(handler *cmdHandler, cmdWriter io.Writer) {
|
||||||
|
tr := io.TeeReader(handler.stdoutPipe, cmdWriter)
|
||||||
|
|
||||||
|
self.processOutput(tr, handler.stdinPipe, promptUserForCredential, cmdObj.GetTask())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *cmdObjRunner) processOutput(
|
||||||
|
reader io.Reader,
|
||||||
|
writer io.Writer,
|
||||||
|
promptUserForCredential func(CredentialType) <-chan string,
|
||||||
|
task gocui.Task,
|
||||||
|
) {
|
||||||
checkForCredentialRequest := self.getCheckForCredentialRequestFunc()
|
checkForCredentialRequest := self.getCheckForCredentialRequestFunc()
|
||||||
|
|
||||||
scanner := bufio.NewScanner(reader)
|
scanner := bufio.NewScanner(reader)
|
||||||
@@ -311,7 +330,14 @@ func (self *cmdObjRunner) processOutput(reader io.Reader, writer io.Writer, prom
|
|||||||
newBytes := scanner.Bytes()
|
newBytes := scanner.Bytes()
|
||||||
askFor, ok := checkForCredentialRequest(newBytes)
|
askFor, ok := checkForCredentialRequest(newBytes)
|
||||||
if ok {
|
if ok {
|
||||||
toInput := promptUserForCredential(askFor)
|
responseChan := promptUserForCredential(askFor)
|
||||||
|
if task != nil {
|
||||||
|
task.Pause()
|
||||||
|
}
|
||||||
|
toInput := <-responseChan
|
||||||
|
if task != nil {
|
||||||
|
task.Continue()
|
||||||
|
}
|
||||||
// If the return data is empty we don't write anything to stdin
|
// If the return data is empty we don't write anything to stdin
|
||||||
if toInput != "" {
|
if toInput != "" {
|
||||||
_, _ = writer.Write([]byte(toInput))
|
_, _ = writer.Write([]byte(toInput))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,6 +16,18 @@ func getRunner() *cmdObjRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toChanFn(f func(ct CredentialType) string) func(CredentialType) <-chan string {
|
||||||
|
return func(ct CredentialType) <-chan string {
|
||||||
|
ch := make(chan string)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ch <- f(ct)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProcessOutput(t *testing.T) {
|
func TestProcessOutput(t *testing.T) {
|
||||||
defaultPromptUserForCredential := func(ct CredentialType) string {
|
defaultPromptUserForCredential := func(ct CredentialType) string {
|
||||||
switch ct {
|
switch ct {
|
||||||
@@ -99,7 +112,8 @@ func TestProcessOutput(t *testing.T) {
|
|||||||
reader := strings.NewReader(scenario.output)
|
reader := strings.NewReader(scenario.output)
|
||||||
writer := &strings.Builder{}
|
writer := &strings.Builder{}
|
||||||
|
|
||||||
runner.processOutput(reader, writer, scenario.promptUserForCredential)
|
task := gocui.NewFakeTask()
|
||||||
|
runner.processOutput(reader, writer, toChanFn(scenario.promptUserForCredential), task)
|
||||||
|
|
||||||
if writer.String() != scenario.expectedToWrite {
|
if writer.String() != scenario.expectedToWrite {
|
||||||
t.Errorf("expected to write '%s' but got '%s'", scenario.expectedToWrite, writer.String())
|
t.Errorf("expected to write '%s' but got '%s'", scenario.expectedToWrite, writer.String())
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package oscommands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCmdObjToString(t *testing.T) {
|
||||||
|
quote := func(s string) string {
|
||||||
|
return "\"" + s + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []struct {
|
||||||
|
cmdArgs []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cmdArgs: []string{"git", "push", "myfile.txt"},
|
||||||
|
expected: "git push myfile.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cmdArgs: []string{"git", "push", "my file.txt"},
|
||||||
|
expected: "git push \"my file.txt\"",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
cmdObj := &CmdObj{args: scenario.cmdArgs}
|
||||||
|
actual := cmdObj.ToString()
|
||||||
|
if actual != scenario.expected {
|
||||||
|
t.Errorf("Expected %s, got %s", quote(scenario.expected), quote(actual))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClone(t *testing.T) {
|
||||||
|
task := gocui.NewFakeTask()
|
||||||
|
cmdObj := &CmdObj{task: task, cmd: &exec.Cmd{}}
|
||||||
|
clone := cmdObj.Clone()
|
||||||
|
if clone == cmdObj {
|
||||||
|
t.Errorf("Clone should not return the same object")
|
||||||
|
}
|
||||||
|
|
||||||
|
if clone.GetTask() == nil {
|
||||||
|
t.Errorf("Clone task should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if clone.GetTask() != task {
|
||||||
|
t.Errorf("Clone should have the same task")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,13 +39,13 @@ import (
|
|||||||
func CopyFile(src, dst string) (err error) {
|
func CopyFile(src, dst string) (err error) {
|
||||||
in, err := os.Open(src)
|
in, err := os.Open(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
defer in.Close()
|
defer in.Close()
|
||||||
|
|
||||||
out, err := os.Create(dst)
|
out, err := os.Create(dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := out.Close(); e != nil {
|
if e := out.Close(); e != nil {
|
||||||
@@ -55,21 +55,21 @@ func CopyFile(src, dst string) (err error) {
|
|||||||
|
|
||||||
_, err = io.Copy(out, in)
|
_, err = io.Copy(out, in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
|
|
||||||
err = out.Sync()
|
err = out.Sync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
|
|
||||||
si, err := os.Stat(src)
|
si, err := os.Stat(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
err = os.Chmod(dst, si.Mode())
|
err = os.Chmod(dst, si.Mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
|
|
||||||
return //nolint: nakedret
|
return //nolint: nakedret
|
||||||
@@ -92,7 +92,7 @@ func CopyDir(src string, dst string) (err error) {
|
|||||||
|
|
||||||
_, err = os.Stat(dst)
|
_, err = os.Stat(dst)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// it exists so let's remove it
|
// it exists so let's remove it
|
||||||
@@ -103,12 +103,12 @@ func CopyDir(src string, dst string) (err error) {
|
|||||||
|
|
||||||
err = os.MkdirAll(dst, si.Mode())
|
err = os.MkdirAll(dst, si.Mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := ioutil.ReadDir(src)
|
entries, err := ioutil.ReadDir(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
@@ -118,7 +118,7 @@ func CopyDir(src string, dst string) (err error) {
|
|||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
err = CopyDir(srcPath, dstPath)
|
err = CopyDir(srcPath, dstPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Skip symlinks.
|
// Skip symlinks.
|
||||||
@@ -128,7 +128,7 @@ func CopyDir(src string, dst string) (err error) {
|
|||||||
|
|
||||||
err = CopyFile(srcPath, dstPath)
|
err = CopyFile(srcPath, dstPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return //nolint: nakedret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -91,7 +92,18 @@ func (self *FakeCmdObjRunner) Expect(expectedCmdStr string, output string, err e
|
|||||||
func (self *FakeCmdObjRunner) ExpectArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner {
|
func (self *FakeCmdObjRunner) ExpectArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner {
|
||||||
self.ExpectFunc(func(cmdObj ICmdObj) (string, error) {
|
self.ExpectFunc(func(cmdObj ICmdObj) (string, error) {
|
||||||
args := cmdObj.GetCmd().Args
|
args := cmdObj.GetCmd().Args
|
||||||
assert.EqualValues(self.t, expectedArgs, args, fmt.Sprintf("command %d did not match expectation", self.expectedCmdIndex+1))
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// thanks to the secureexec package, the first arg is something like
|
||||||
|
// '"C:\\Program Files\\Git\\mingw64\\bin\\<command>.exe"
|
||||||
|
// on windows so we'll just ensure it contains our program
|
||||||
|
assert.Contains(self.t, args[0], expectedArgs[0])
|
||||||
|
} else {
|
||||||
|
// first arg is the program name
|
||||||
|
assert.Equal(self.t, expectedArgs[0], args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.EqualValues(self.t, expectedArgs[1:], args[1:], fmt.Sprintf("command %d did not match expectation", self.expectedCmdIndex+1))
|
||||||
|
|
||||||
return output, err
|
return output, err
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -26,10 +26,15 @@ type guiIO struct {
|
|||||||
// this allows us to request info from the user like username/password, in the event
|
// this allows us to request info from the user like username/password, in the event
|
||||||
// that a command requests it.
|
// that a command requests it.
|
||||||
// the 'credential' arg is something like 'username' or 'password'
|
// the 'credential' arg is something like 'username' or 'password'
|
||||||
promptForCredentialFn func(credential CredentialType) string
|
promptForCredentialFn func(credential CredentialType) <-chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGuiIO(log *logrus.Entry, logCommandFn func(string, bool), newCmdWriterFn func() io.Writer, promptForCredentialFn func(CredentialType) string) *guiIO {
|
func NewGuiIO(
|
||||||
|
log *logrus.Entry,
|
||||||
|
logCommandFn func(string, bool),
|
||||||
|
newCmdWriterFn func() io.Writer,
|
||||||
|
promptForCredentialFn func(CredentialType) <-chan string,
|
||||||
|
) *guiIO {
|
||||||
return &guiIO{
|
return &guiIO{
|
||||||
log: log,
|
log: log,
|
||||||
logCommandFn: logCommandFn,
|
logCommandFn: logCommandFn,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
"github.com/atotto/clipboard"
|
"github.com/atotto/clipboard"
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
@@ -187,12 +188,18 @@ func (c *OSCommand) FileExists(path string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
|
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
|
||||||
func (c *OSCommand) PipeCommands(commandStrings ...string) error {
|
func (c *OSCommand) PipeCommands(cmdObjs ...ICmdObj) error {
|
||||||
cmds := slices.Map(commandStrings, func(cmdString string) *exec.Cmd {
|
cmds := slices.Map(cmdObjs, func(cmdObj ICmdObj) *exec.Cmd {
|
||||||
return c.Cmd.New(cmdString).GetCmd()
|
return cmdObj.GetCmd()
|
||||||
})
|
})
|
||||||
|
|
||||||
logCmdStr := strings.Join(commandStrings, " | ")
|
logCmdStr := strings.Join(
|
||||||
|
lo.Map(cmdObjs, func(cmdObj ICmdObj, _ int) string {
|
||||||
|
return cmdObj.ToString()
|
||||||
|
}),
|
||||||
|
" | ",
|
||||||
|
)
|
||||||
|
|
||||||
c.LogCommand(logCmdStr, true)
|
c.LogCommand(logCmdStr, true)
|
||||||
|
|
||||||
for i := 0; i < len(cmds)-1; i++ {
|
for i := 0; i < len(cmds)-1; i++ {
|
||||||
|
|||||||
@@ -12,20 +12,20 @@ import (
|
|||||||
|
|
||||||
func TestOSCommandRunWithOutput(t *testing.T) {
|
func TestOSCommandRunWithOutput(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
command string
|
args []string
|
||||||
test func(string, error)
|
test func(string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"echo -n '123'",
|
[]string{"echo", "-n", "123"},
|
||||||
func(output string, err error) {
|
func(output string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, "123", output)
|
assert.EqualValues(t, "123", output)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"rmdir unexisting-folder",
|
[]string{"rmdir", "unexisting-folder"},
|
||||||
func(output string, err error) {
|
func(output string, err error) {
|
||||||
assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
|
assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
|
||||||
},
|
},
|
||||||
@@ -34,7 +34,7 @@ func TestOSCommandRunWithOutput(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
c := NewDummyOSCommand()
|
c := NewDummyOSCommand()
|
||||||
s.test(c.Cmd.New(s.command).RunWithOutput())
|
s.test(c.Cmd.New(s.args).RunWithOutput())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import (
|
|||||||
|
|
||||||
func TestOSCommandRun(t *testing.T) {
|
func TestOSCommandRun(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
command string
|
args []string
|
||||||
test func(error)
|
test func(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"rmdir unexisting-folder",
|
[]string{"rmdir", "unexisting-folder"},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
|
assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
|
||||||
},
|
},
|
||||||
@@ -25,7 +25,7 @@ func TestOSCommandRun(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
c := NewDummyOSCommand()
|
c := NewDummyOSCommand()
|
||||||
s.test(c.Cmd.New(s.command).Run())
|
s.test(c.Cmd.New(s.args).Run())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ type fileInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
applyPatchFunc func(patch string, flags ...string) error
|
|
||||||
loadFileDiffFunc func(from string, to string, reverse bool, filename string, plain bool) (string, error)
|
loadFileDiffFunc func(from string, to string, reverse bool, filename string, plain bool) (string, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,17 +46,14 @@ type PatchBuilder struct {
|
|||||||
// fileInfoMap starts empty but you add files to it as you go along
|
// fileInfoMap starts empty but you add files to it as you go along
|
||||||
fileInfoMap map[string]*fileInfo
|
fileInfoMap map[string]*fileInfo
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
applyPatch applyPatchFunc
|
|
||||||
|
|
||||||
// loadFileDiff loads the diff of a file, for a given to (typically a commit SHA)
|
// loadFileDiff loads the diff of a file, for a given to (typically a commit SHA)
|
||||||
loadFileDiff loadFileDiffFunc
|
loadFileDiff loadFileDiffFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPatchBuilder returns a new PatchBuilder
|
func NewPatchBuilder(log *logrus.Entry, loadFileDiff loadFileDiffFunc) *PatchBuilder {
|
||||||
func NewPatchBuilder(log *logrus.Entry, applyPatch applyPatchFunc, loadFileDiff loadFileDiffFunc) *PatchBuilder {
|
|
||||||
return &PatchBuilder{
|
return &PatchBuilder{
|
||||||
Log: log,
|
Log: log,
|
||||||
applyPatch: applyPatch,
|
|
||||||
loadFileDiff: loadFileDiff,
|
loadFileDiff: loadFileDiff,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +66,20 @@ func (p *PatchBuilder) Start(from, to string, reverse bool, canRebase bool) {
|
|||||||
p.fileInfoMap = map[string]*fileInfo{}
|
p.fileInfoMap = map[string]*fileInfo{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PatchBuilder) PatchToApply(reverse bool) string {
|
||||||
|
patch := ""
|
||||||
|
|
||||||
|
for filename, info := range p.fileInfoMap {
|
||||||
|
if info.mode == UNSELECTED {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
patch += p.RenderPatchForFile(filename, true, reverse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return patch
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PatchBuilder) addFileWhole(info *fileInfo) {
|
func (p *PatchBuilder) addFileWhole(info *fileInfo) {
|
||||||
info.mode = WHOLE
|
info.mode = WHOLE
|
||||||
lineCount := len(strings.Split(info.diff, "\n"))
|
lineCount := len(strings.Split(info.diff, "\n"))
|
||||||
@@ -234,25 +244,6 @@ func (p *PatchBuilder) GetFileIncLineIndices(filename string) ([]int, error) {
|
|||||||
return info.includedLineIndices, nil
|
return info.includedLineIndices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PatchBuilder) ApplyPatches(reverse bool) error {
|
|
||||||
patch := ""
|
|
||||||
|
|
||||||
applyFlags := []string{"index", "3way"}
|
|
||||||
if reverse {
|
|
||||||
applyFlags = append(applyFlags, "reverse")
|
|
||||||
}
|
|
||||||
|
|
||||||
for filename, info := range p.fileInfoMap {
|
|
||||||
if info.mode == UNSELECTED {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
patch += p.RenderPatchForFile(filename, true, reverse)
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.applyPatch(patch, applyFlags...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// clears the patch
|
// clears the patch
|
||||||
func (p *PatchBuilder) Reset() {
|
func (p *PatchBuilder) Reset() {
|
||||||
p.To = ""
|
p.To = ""
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/OpenPeeDeeP/xdg"
|
"github.com/OpenPeeDeeP/xdg"
|
||||||
yaml "github.com/jesseduffield/yaml"
|
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppConfig contains the base configuration fields required for lazygit.
|
// AppConfig contains the base configuration fields required for lazygit.
|
||||||
@@ -156,6 +157,11 @@ func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content, err = migrateUserConfig(path, content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -164,6 +170,30 @@ func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error)
|
|||||||
return base, nil
|
return base, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do any backward-compatibility migrations of things that have changed in the
|
||||||
|
// config over time; examples are renaming a key to a better name, moving a key
|
||||||
|
// from one container to another, or changing the type of a key (e.g. from bool
|
||||||
|
// to an enum).
|
||||||
|
func migrateUserConfig(path string, content []byte) ([]byte, error) {
|
||||||
|
changedContent, err := yaml_utils.RenameYamlKey(content, []string{"gui", "skipUnstageLineWarning"},
|
||||||
|
"skipDiscardChangeWarning")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more migrations here...
|
||||||
|
|
||||||
|
// Write config back if changed
|
||||||
|
if string(changedContent) != string(content) {
|
||||||
|
if err := os.WriteFile(path, changedContent, 0o644); err != nil {
|
||||||
|
return nil, fmt.Errorf("Couldn't write migrated config back to `%s`: %s", path, err)
|
||||||
|
}
|
||||||
|
return changedContent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *AppConfig) GetDebug() bool {
|
func (c *AppConfig) GetDebug() bool {
|
||||||
return c.Debug
|
return c.Debug
|
||||||
}
|
}
|
||||||
@@ -301,5 +331,9 @@ func getDefaultAppState() *AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func LogPath() (string, error) {
|
func LogPath() (string, error) {
|
||||||
|
if os.Getenv("LAZYGIT_LOG_PATH") != "" {
|
||||||
|
return os.Getenv("LAZYGIT_LOG_PATH"), nil
|
||||||
|
}
|
||||||
|
|
||||||
return configFilePath("development.log")
|
return configFilePath("development.log")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
yaml "github.com/jesseduffield/yaml"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDummyAppConfig creates a new dummy AppConfig for testing
|
// NewDummyAppConfig creates a new dummy AppConfig for testing
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user