Compare commits

..
39 Commits
Author SHA1 Message Date
Jesse Duffield e57f6ff9c5 Better logic for knowing which repo we're in 2023-07-17 14:38:08 +10:00
Jesse Duffield a748294bc1 Only show worktree in status panel if not the main worktree and worktrees are supported 2023-07-17 14:10:32 +10:00
Jesse Duffield eb099c13c7 Hide worktree functionality on old git versions 2023-07-17 14:10:32 +10:00
Jesse Duffield 2ff12e6820 Associate branches with worktrees even when mid-rebase 2023-07-17 13:43:10 +10:00
Jesse Duffield a8aafbc6af Assume that the base of a worktree can be checked out 2023-07-17 09:48:37 +10:00
Jesse Duffield 67e6e4e8fd i18n for worktrees 2023-07-17 09:46:14 +10:00
Jesse Duffield a8d99a8ee7 Don't quit on error 2023-07-17 09:13:16 +10:00
Jesse Duffield 223143c834 Allow opening worktree in editor
This does the job but I think we need yet another editor command for opening a directory in a new window.
2023-07-16 20:44:35 +10:00
Jesse Duffield f6973cf7e4 Show base ref suggestions when creating worktree 2023-07-16 20:38:22 +10:00
Jesse Duffield e1e7e9185e Refresh work trees when discarding file changes
We do this because we may be deleting a worktree folder so we'll need to show that in the worktrees view
2023-07-16 20:35:04 +10:00
Jesse Duffield 48d161dd1c Checkout worktree when creating from worktree view 2023-07-16 20:35:04 +10:00
Jesse Duffield 8ce1ed23ce Use 'M' for months in branches panel 2023-07-16 20:09:07 +10:00
Jesse Duffield 4d1352e3d0 Fix filtering logic in worktrees view 2023-07-16 20:07:04 +10:00
Jesse Duffield a7367ffcc1 Support creating worktrees from refs 2023-07-16 20:07:04 +10:00
Jesse Duffield cda40a7b75 Fix wording 2023-07-16 18:23:47 +10:00
Jesse Duffield 76c62eba7c Log when directory is changed 2023-07-16 18:23:47 +10:00
Jesse Duffield 835ca6389e Handle deleting branch attached to worktree 2023-07-16 17:59:41 +10:00
Jesse Duffield bdaf0c99ba Update wording 2023-07-16 17:31:52 +10:00
Jesse Duffield c996c29df0 Don't touch repo stack when switching worktrees
We shouldn't touch this cos we're doing a lateral move
2023-07-16 17:26:27 +10:00
Jesse Duffield dc9a6e0ea5 Move status panel presentation logic into presentation package 2023-07-16 17:15:19 +10:00
Jesse Duffield eaba9dd62d Land in the same panel when switching to a worktree 2023-07-16 14:37:49 +10:00
Jesse Duffield 3cb13f14bf Move current worktree to top of list 2023-07-16 14:23:31 +10:00
Jesse Duffield 5d52852df3 Prompt to switch to worktree when branch is checked out by other worktree 2023-07-16 14:14:09 +10:00
Jesse Duffield 7d4432c4b5 Use git lingo 2023-07-16 13:53:59 +10:00
Jesse Duffield a1235fa468 Improve name handling 2023-07-16 13:43:20 +10:00
Jesse Duffield 8b90811c65 Use sentence case 2023-07-16 12:23:35 +10:00
Jesse Duffield 2dd2b9f5e3 Refactor 2023-07-16 12:21:43 +10:00
Jesse Duffield e7484808e5 Update worktree model 2023-07-16 11:36:50 +10:00
Jesse Duffield 3245350bab Alert when attempting to enter the current worktree 2023-07-16 11:20:22 +10:00
Jesse Duffield 6e51dd1c85 Remove comment 2023-07-16 11:16:10 +10:00
Joel Baranick 77db982774 Address PR comments 2023-07-16 11:04:39 +10:00
Joel Baranick e16f56e492 Basic support for adding a worktree 2023-07-16 10:55:56 +10:00
Joel Baranick 3055944b5d Put all worktree i18n strings together
Use tabwriter to align worktree panel contents
2023-07-16 10:53:00 +10:00
Joel Baranick 6194b17ebb Improve worktree panel 2023-07-16 10:50:25 +10:00
Joel Baranick 15ffb34474 Style missing worktree as red and display better error when trying to switch to them
Use a broken link icon for missing worktrees
2023-07-16 10:48:22 +10:00
Joel Baranick 32409dbb2f Hide worktrees in the worktree panel if they point at a non-existing filesystem location.
Remove unneeded check when filtering out branches from non-current worktrees from the branch panel.
Add link icon for linked worktrees
2023-07-16 10:32:36 +10:00
Joel Baranick 4ec960f07d Update status to differentiate the main vs linked worktrees 2023-07-16 10:29:08 +10:00
Joel Baranick a7bdc6be01 Support for deleting a worktree 2023-07-16 10:23:17 +10:00
Joel Baranick 271f894106 Initial addition of support for worktrees 2023-07-16 10:20:36 +10:00
2223 changed files with 67933 additions and 227446 deletions
-7
View File
@@ -1,7 +0,0 @@
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = .git*,go.sum,*.lock,.codespellrc,vendor,translations,Keybindings_*.md
check-hidden = true
# camel-cased
ignore-regex = (\b[A-Za-z][a-z]*[A-Z]\S+\b|\.edn\b|\S+…|\\nd\b)
ignore-words-list = fomrat,inbetween
+2
View File
@@ -5,6 +5,8 @@ ARG VARIANT=1-bullseye
FROM golang:${VARIANT}
RUN go install mvdan.cc/gofumpt@latest
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.0
RUN golangci-lint --version
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
-4
View File
@@ -1,4 +0,0 @@
root = true
[*.go]
indent_style = tab
-3
View File
@@ -1,3 +0,0 @@
*.go text
*.md text eol=lf
*.json text eol=lf
+9 -11
View File
@@ -6,10 +6,10 @@ labels: bug
assignees: ''
---
### Describe the bug
**Describe the bug**
A clear and concise description of what the bug is.
### To Reproduce
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
@@ -17,22 +17,20 @@ Steps to reproduce the behavior:
3. Scroll down to '....'
4. See error
### Expected behavior
**Expected behavior**
A clear and concise description of what you expected to happen.
### Screenshots
**Screenshots**
If applicable, add screenshots to help explain your problem.
### Version info:
**Version info:**
_Run `lazygit --version` and paste the result here_
_Run `git --version` and paste the result here_
* _Run `lazygit --version` and paste the result here_
* _Run `git --version` and paste the result here_
### Additional context
**Additional context**
Add any other context about the problem here.
> [!NOTE]
> Please try updating to the latest version or [manually building](https://github.com/jesseduffield/lazygit/#manual) the latest `master` to see if the issue still occurs.
**Note:** please try updating to the latest version or [manually building](https://github.com/jesseduffield/lazygit/#manual) the latest `master` to see if the issue still occurs.
<!--
If you want to try and debug this issue yourself, you can run `lazygit --debug` in one terminal panel and `lazygit --logs` in another to view the logs.
+4 -4
View File
@@ -7,8 +7,8 @@ assignees: ''
---
### Topic
A clear and concise description of what you want to discuss.
**Topic**
A clear and concise description of what you want to discuss
### Your thoughts
What you have to say about the topic.
**Your thoughts**
What you have to say about the topic
+4 -4
View File
@@ -6,16 +6,16 @@ labels: enhancement
assignees: ''
---
### Is your feature request related to a problem? Please describe.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
### Describe the solution you'd like
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
### Describe alternatives you've considered
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
### Additional context
**Additional context**
Add any other context or screenshots about the feature request here.
<!--
+4 -12
View File
@@ -1,18 +1,10 @@
### PR Description
- **PR Description**
### Please check if the PR fulfills these requirements
- **Please check if the PR fulfills these requirements**
* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go generate`)
* [ ] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide)
* [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be hot-reloaded (see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [ ] Docs (specifically `docs/Config.md`) have been updated if necessary
* [ ] You've read through your own file changes for silly mistakes etc
<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view', and make sure the title
is suitable to be included as a bullet point in release notes (i.e. phrased from a user's point
of view).
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for examples
-->
-29
View File
@@ -1,29 +0,0 @@
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: Performance Improvements 📊
labels:
- performance
- title: Other Changes
labels:
- "*"
+31
View File
@@ -0,0 +1,31 @@
name: Continuous Delivery
on:
push:
tags:
- 'v*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Unshallow repo
run: git fetch --prune --unshallow
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.18.x
- name: Run goreleaser
uses: goreleaser/goreleaser-action@v1
env:
GITHUB_TOKEN: ${{secrets.GITHUB_API_TOKEN}}
homebrew:
runs-on: ubuntu-latest
steps:
- name: Bump Homebrew formula
uses: dawidd6/action-homebrew-bump-formula@v3
with:
token: ${{secrets.GITHUB_API_TOKEN}}
formula: lazygit
@@ -1,15 +0,0 @@
name: Check Required Labels
on:
pull_request:
types: [opened, labeled, unlabeled, synchronize]
jobs:
check-required-label:
runs-on: ubuntu-latest
steps:
- uses: mheap/github-action-required-labels@v5
with:
mode: exactly
count: 1
labels: "ignore-for-release, feature, enhancement, bug, maintenance, docs, i18n, performance"
+77 -101
View File
@@ -1,7 +1,7 @@
name: Continuous Integration
env:
GO_VERSION: 1.25
GO_VERSION: 1.18
on:
push:
@@ -28,30 +28,34 @@ jobs:
GOFLAGS: -mod=vendor
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v1
with:
go-version: 1.25.x
go-version: 1.18.x
- name: Cache build
uses: actions/cache@v3
with:
path: |
${{matrix.cache_path}}
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
- name: Test code
# we're passing -short so that we skip the integration tests, which will be run in parallel below
run: |
mkdir -p /tmp/code_coverage
go test ./... -short -cover -args "-test.gocoverdir=/tmp/code_coverage"
- name: Upload code coverage artifacts
uses: actions/upload-artifact@v4
with:
name: coverage-unit-${{ matrix.os }}-${{ github.run_id }}
path: /tmp/code_coverage
go test ./... -short
integration-tests:
strategy:
fail-fast: false
matrix:
git-version:
- 2.32.0 # oldest supported version
- 2.38.2 # first version that supports the rebase.updateRefs config
- 2.44.0
- 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
name: "Integration Tests - git ${{matrix.git-version}}"
@@ -59,11 +63,11 @@ jobs:
GOFLAGS: -mod=vendor
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Restore Git cache
if: matrix.git-version != 'latest'
id: cache-git-restore
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
with:
path: ~/git-${{matrix.git-version}}
key: ${{runner.os}}-git-${{matrix.git-version}}
@@ -80,28 +84,28 @@ jobs:
run: sudo make -C "$HOME/git-${{matrix.git-version}}" -j install
- name: Save Git cache
if: steps.cache-git-restore.outputs.cache-hit != 'true' && matrix.git-version != 'latest'
uses: actions/cache/save@v4
uses: actions/cache/save@v3
with:
path: ~/git-${{matrix.git-version}}
key: ${{runner.os}}-git-${{matrix.git-version}}
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v1
with:
go-version: 1.25.x
go-version: 1.18.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
- name: Print git version
run: git --version
- name: Test code
env:
# See https://go.dev/blog/integration-test-coverage
LAZYGIT_GOCOVERDIR: /tmp/code_coverage
run: |
mkdir -p /tmp/code_coverage
./scripts/run_integration_tests.sh
- name: Upload code coverage artifacts
uses: actions/upload-artifact@v4
with:
name: coverage-integration-${{ matrix.git-version }}-${{ github.run_id }}
path: /tmp/code_coverage
build:
runs-on: ubuntu-latest
env:
@@ -109,11 +113,20 @@ jobs:
GOARCH: amd64
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v1
with:
go-version: 1.25.x
go-version: 1.18.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
restore-keys: |
${{runner.os}}-go-
- name: Build linux binary
run: |
GOOS=linux go build
@@ -136,23 +149,31 @@ jobs:
GOARCH: amd64
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v1
with:
go-version: 1.25.x
go-version: 1.18.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
restore-keys: |
${{runner.os}}-go-
- name: Check Cheatsheet
run: |
go run scripts/cheatsheet/main.go check
- name: Check Vendor Directory
# ensure our vendor directory matches up with our go modules
run: |
go mod vendor && git diff --exit-code || (echo "Unexpected change to vendor directory. Run 'go mod vendor' locally and commit the changes" && exit 1)
- name: Check go.mod file
# ensure our go.mod file is clean
- name: Check Integration Test List
# ensure our integration test list is up to date
run: |
go mod tidy && git diff --exit-code || (echo "go.mod file is not clean. Run 'go mod tidy' locally and commit the changes" && exit 1)
- name: Check All Auto-Generated Files
# ensure all our auto-generated files are up to date
run: |
go generate ./... && git diff --quiet || (git status -s; echo "Auto-generated files not up to date. Run 'go generate ./...' locally and commit the changes" && exit 1)
go generate pkg/integration/tests/tests.go && git diff --exit-code || (echo "Integration test list not up to date. Run 'go generate pkg/integration/tests/tests.go' locally and commit the changes" && exit 1)
shell: bash # needed so that we get "-o pipefail"
- name: Check Filenames
run: scripts/check_filenames.sh
@@ -162,69 +183,24 @@ jobs:
GOFLAGS: -mod=vendor
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v1
with:
go-version: 1.25.x
go-version: 1.18.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
- name: Lint
uses: golangci/golangci-lint-action@v8
uses: golangci/golangci-lint-action@v3.1.0
with:
# If you change this, make sure to also update scripts/golangci-lint-shim.sh
version: v2.4.0
version: latest
- name: errors
run: golangci-lint run
if: ${{ failure() }}
upload-coverage:
# List all jobs that produce coverage files
needs: [unit-tests, integration-tests]
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.25.x
- name: Download all coverage artifacts
uses: actions/download-artifact@v4
with:
path: /tmp/code_coverage
- name: Combine coverage files
run: |
# Find all directories in /tmp/code_coverage and create a comma-separated list
COVERAGE_DIRS=$(find /tmp/code_coverage -mindepth 1 -maxdepth 1 -type d -printf '/tmp/code_coverage/%f,' | sed 's/,$//')
echo "Coverage directories: $COVERAGE_DIRS"
# Run the combine command with the generated list
go tool covdata textfmt -i=$COVERAGE_DIRS -o coverage.out
echo "Combined coverage:"
go tool cover -func coverage.out | tail -1 | awk '{print $3}'
- name: Upload to Codacy
run: |
CODACY_PROJECT_TOKEN=${{ secrets.CODACY_PROJECT_TOKEN }} \
bash <(curl -Ls https://coverage.codacy.com/get.sh) report \
--force-coverage-parser go -r coverage.out
check-for-fixups:
runs-on: ubuntu-latest
if: github.ref != 'refs/heads/master'
steps:
# See https://github.com/actions/checkout/issues/552#issuecomment-1167086216
- name: "PR commits"
run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} ))" >> "${GITHUB_ENV}"
- name: "Checkout PR branch and all PR commits"
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: ${{ env.PR_FETCH_DEPTH }}
- name: Check for fixups
run: |
./scripts/check_for_fixups.sh ${{ github.event.pull_request.base.ref }}
-39
View File
@@ -1,39 +0,0 @@
name: Close Issues
on:
issue_comment:
types: [created]
permissions:
issues: write
jobs:
close_issue:
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request == null && startsWith(github.event.comment.body, '/close') }}
steps:
- uses: actions/github-script@v7
with:
script: |
const trustedUsers = ['ChrisMcD1', 'jesseduffield', 'stefanhaller']
const commenter = context.payload.comment.user.login
console.log(`Commenter: ${commenter}`)
if (!trustedUsers.includes(commenter)) {
console.log(`User ${commenter} is not trusted. Ignoring.`)
return
}
const issueNumber = context.payload.issue.number
const owner = context.repo.owner
const repo = context.repo.repo
await github.rest.issues.update({
owner,
repo,
issue_number: issueNumber,
state: 'closed'
})
console.log(`Closed issue #${issueNumber} by request from ${commenter}.`)
-25
View File
@@ -1,25 +0,0 @@
# Codespell configuration is within .codespellrc
---
name: Codespell
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Annotate locations with typos
uses: codespell-project/codespell-problem-matcher@v1
- name: Codespell
uses: codespell-project/actions-codespell@v2
-151
View File
@@ -1,151 +0,0 @@
name: Release
on:
schedule:
# Runs at 8:00 AM UTC on every Saturday
# We'll check below if it's the first Saturday of the month, and fail if not
- cron: '0 8 * * 6'
# Allow manual triggering of the workflow
workflow_dispatch:
inputs:
version_bump:
description: 'Version bump type'
type: choice
required: true
default: 'patch'
options:
- minor
- patch
ignore_blocks:
description: 'Ignore blocking PRs/issues'
type: boolean
required: true
default: false
defaults:
run:
shell: bash
jobs:
check-and-release:
runs-on: ubuntu-latest
steps:
- name: Check for correct repository
if: ${{ github.event_name != 'workflow_dispatch' && github.repository != 'stefanhaller/lazygit' }}
run: |
echo "Should only run in the stefanhaller/lazygit repository"
exit 1
- name: Check for first Saturday of the month
if: ${{ github.event_name != 'workflow_dispatch' }}
run: |
if (( $(date +%e) > 7 )); then
echo "This is not the first Saturday of the month"
exit 1
fi
- name: Checkout Code
uses: actions/checkout@v4
with:
repository: jesseduffield/lazygit
token: ${{ secrets.LAZYGIT_RELEASE_PAT }}
fetch-depth: 0
- name: Get Latest Tag
run: |
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1) || echo "v0.0.0")
if ! [[ $latest_tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Tag format is invalid. Expected format: vX.X.X"
exit 1
fi
echo "Latest tag: $latest_tag"
echo "latest_tag=$latest_tag" >> $GITHUB_ENV
- name: Check for changes since last release
run: |
if [ -z "$(git diff --name-only ${{ env.latest_tag }})" ]; then
echo "No changes detected since last release"
exit 1
fi
- name: Check for Blocking Issues/PRs
if: ${{ !inputs.ignore_blocks }}
id: check_blocks
run: |
gh auth setup-git
gh auth status
echo "Checking for blocking issues and PRs..."
# Check for blocking issues
blocking_issues=$(gh issue list -l blocks-release --json number,title --jq '.[] | "- \(.title) (#\(.number))"')
# Check for blocking PRs
blocking_prs=$(gh pr list -l blocks-release --json number,title --jq '.[] | "- \(.title) (#\(.number)) (PR)"')
# Combine the results
blocking_items="$blocking_issues"$'\n'"$blocking_prs"
# Remove empty lines
blocking_items=$(echo "$blocking_items" | grep . || true)
if [ -n "$blocking_items" ]; then
echo "Blocking issues/PRs detected:"
echo "$blocking_items"
exit 1
fi
env:
GITHUB_TOKEN: ${{ secrets.LAZYGIT_RELEASE_PAT }}
- name: Calculate next version
run: |
echo "Latest tag: ${{ env.latest_tag }}"
IFS='.' read -r major minor patch <<< "${{ env.latest_tag }}"
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [[ "${{ inputs.version_bump }}" == "patch" ]]; then
patch=$((patch + 1))
else
minor=$((minor + 1))
patch=0
fi
else
# Default behavior for scheduled runs
minor=$((minor + 1))
patch=0
fi
new_tag="$major.$minor.$patch"
if ! [[ $new_tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: New tag's format is invalid. Expected format: vX.X.X"
exit 1
fi
echo "New tag: $new_tag"
echo "new_tag=$new_tag" >> $GITHUB_ENV
- name: Create and Push Tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag ${{ env.new_tag }} -a -m "Release ${{ env.new_tag }}"
git push origin ${{ env.new_tag }}
env:
GITHUB_TOKEN: ${{ secrets.LAZYGIT_RELEASE_PAT }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.25.x
- name: Run goreleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: v2
args: release --clean
env:
GITHUB_TOKEN: ${{secrets.LAZYGIT_RELEASE_PAT}}
+12 -13
View File
@@ -7,23 +7,22 @@ on:
jobs:
deploy:
runs-on: ubuntu-latest
if: ${{ github.repository == 'jesseduffield/lazygit' }}
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Generate Sponsors 💖
uses: JamesIves/github-sponsors-readme-action@v1.2.2
uses: JamesIves/github-sponsors-readme-action@v1.0.8
with:
token: ${{ secrets.SPONSORS_TOKEN }}
file: "README.md"
file: 'README.md'
if: ${{ github.repository == 'jesseduffield/lazygit' }}
- name: Create Pull Request 🚀
uses: peter-evans/create-pull-request@v7
with:
commit-message: "README.md: Update Sponsors"
title: "README.md: Update Sponsors"
author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
labels: "ignore-for-release"
delete-branch: true
token: ${{ secrets.SPONSORS_PR_TOKEN }}
- name: Commit and push if changed
run: |-
git diff
git config --global user.email "actions@users.noreply.github.com"
git config --global user.name "README-bot"
git add README.md
git commit -m "Updated README.md" || exit 0
git push
+22 -15
View File
@@ -3,6 +3,12 @@
# Logs
*.log
# Hidden
.*
# TODO
TODO.*
# Notes
*.notes
@@ -10,27 +16,28 @@
test/repos/repo
coverage.txt
# JetBrains stuff
.idea/
# Binaries
lazygit
lazygit.exe
# Exceptions
!.gitignore
!.goreleaser.yml
!.golangci.yml
!.circleci/
!.github/
!.vscode/
!.devcontainer/
# these are for our integration tests
!.git_keep
!.gitmodules_keep
test/git_server/data
test/_results/**
test/results/**
oryxBuildBinary
__debug_bin*
__debug_bin
.worktrees
demo/output/*
coverage.out
# Nix
result
result-*
.direnv
.envrc
.worktrees
+25 -106
View File
@@ -1,110 +1,29 @@
version: "2"
run:
go: "1.25"
linters:
enable:
- copyloopvar
- errorlint
- exhaustive
- intrange
- makezero
- nakedret
- nolintlint
- prealloc
- revive
- thelper
- tparallel
- unconvert
- unparam
- wastedassign
settings:
copyloopvar:
check-alias: true
exhaustive:
default-signifies-exhaustive: true
nakedret:
# the gods will judge me but I just don't like naked returns at all
max-func-lines: 0
staticcheck:
checks:
- all
# SA1019 is for checking that we're not using fields marked as
# deprecated in a comment. It decides this in a loose way so I'm
# silencing it. Also because it's tripping on our own structs.
- -SA1019
# ST1003 complains about names like remoteUrl or itemId (should be
# remoteURL and itemID). While I like these suggestions, it also
# complains about enum constants that are all caps, and we use these and
# I like them, and also about camelCase identifiers that contain an
# underscore, which we also use in a few places. Since it can't be
# configured to ignore specific cases, and I don't want to use nolint
# comments in the code, we have to disable it altogether.
- -ST1003 # Poorly chosen identifier
# Probably a good idea, but we first have to review our error reporting
# strategy to be able to use it everywhere.
- -ST1005 # Error strings should not be capitalized
# Many of our classes use self as a receiver name, and we think that's fine.
- -ST1006 # Use of self or this as receiver name
# De Morgan's law suggests to replace `!(a && b)` with `!a || !b`; but
# sometimes I find one more readable than the other, so I want to decide
# that myself.
- -QF1001 # De Morgan's law
# QF1003 is about using a tagged switch instead of an if-else chain. In
# many cases this is a useful suggestion; however, sometimes the change
# is only possible by adding a default case to the switch (when there
# was no `else` block in the original code), in which case I don't find
# it to be an improvement.
- -QF1003 # Could replace with tagged switch
# We need to review our use of embedded fields. I suspect that in some
# cases the fix is not to remove the selector for the embedded field,
# but to turn the embedded field into a named field.
- -QF1008 # Could remove embedded field from selector
# The following checks are all disabled by default in golangci-lint, but
# we disable them again explicitly here to make it easier to keep this
# list in sync with the gopls config in .vscode/settings.json.
- -ST1000, # At least one file in a package should have a package comment
- -ST1020, # The documentation of an exported function should start with the function's name
- -ST1021, # The documentation of an exported type should start with type's name
- -ST1022, # The documentation of an exported variable or constant should start with variable's name
dot-import-whitelist:
- github.com/jesseduffield/lazygit/pkg/integration/components
revive:
severity: warning
rules:
- name: atomic
- name: context-as-argument
- name: context-keys-type
- name: error-naming
- name: var-declaration
- name: package-comments
- name: range
- name: time-naming
- name: indent-error-flow
- name: errorf
- name: superfluous-else
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- vendor/
formatters:
disable:
- structcheck # gives false positives
enable:
- gofumpt
- thelper
- goimports
exclusions:
generated: lax
paths:
- vendor/
- tparallel
- wastedassign
- exportloopref
- unparam
- prealloc
- unconvert
- exhaustive
- makezero
- nakedret
# - goconst # TODO: enable and fix issues
fast: false
linters-settings:
exhaustive:
default-signifies-exhaustive: true
nakedret:
# the gods will judge me but I just don't like naked returns at all
max-func-lines: 0
run:
go: 1.18
+34 -13
View File
@@ -1,5 +1,5 @@
version: 2
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
builds:
- env:
- CGO_ENABLED=0
@@ -12,27 +12,48 @@ builds:
- amd64
- arm
- arm64
- '386'
- 386
# Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}`.
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.buildSource=binaryRelease
archives:
- name_template: >-
{{- .ProjectName }}_
{{- .Version }}_
{{- .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}32-bit
{{- else if eq .Arch "arm" }}armv6
{{- else }}{{ .Arch }}{{ end }}
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: 32-bit
amd64: x86_64
format_overrides:
- goos: windows
formats: [ zip ]
format: zip
checksum:
name_template: 'checksums.txt'
snapshot:
version_template: '{{ .Tag }}-next'
name_template: '{{ .Tag }}-next'
changelog:
use: github-native
sort: asc
brews:
-
# Repository to push the tap to.
tap:
owner: jesseduffield
name: homebrew-lazygit
# Your app's homepage.
# Default is empty.
homepage: 'https://github.com/jesseduffield/lazygit/'
# Your app's description.
# Default is empty.
description: 'A simple terminal UI for git commands, written in Go'
# # Packages your package depends on.
# dependencies:
# - git
# - zsh
# # Packages that conflict with your package.
# conflicts:
# - svn
# - bash
+10 -44
View File
@@ -7,12 +7,11 @@
"request": "launch",
"mode": "auto",
"program": "main.go",
"args": [
"--debug",
"--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
],
"hideSystemGoroutines": true,
"args": ["--debug", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
"console": "integratedTerminal",
"presentation": {
"hidden": true
}
},
{
"name": "Tail Lazygit logs",
@@ -20,50 +19,17 @@
"request": "launch",
"mode": "auto",
"program": "main.go",
"args": [
"--logs",
"--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
],
"args": ["--logs", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
"console": "integratedTerminal",
},
{
"name": "JSON Schema generator",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/pkg/jsonschema/generator.go",
"cwd": "${workspaceFolder}/pkg/jsonschema",
"console": "integratedTerminal",
},
{
"name": "Attach to a running Lazygit",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "lazygit",
"hideSystemGoroutines": true,
"console": "integratedTerminal",
},
{
// To use this, first start an integration test with the "cli" runner and
// use the -debug option; e.g.
// $ make integration-test-cli -- -debug tag/reset.go
"name": "Attach to integration test runner",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "test_lazygit",
"hideSystemGoroutines": true,
"console": "integratedTerminal",
},
"presentation": {
"hidden": true
}
}
],
"compounds": [
{
"name": "Run with logs",
"configurations": [
"Tail Lazygit logs",
"Debug Lazygit"
],
"configurations": ["Tail Lazygit logs", "Debug Lazygit"],
"stopAll": true
}
]
-29
View File
@@ -1,29 +0,0 @@
{
"gopls": {
"formatting.gofumpt": true,
"ui.diagnostic.staticcheck": true,
"ui.diagnostic.analyses": {
// This list must match the one in .golangci.yml
"SA1019": false,
"ST1003": false,
"ST1005": false,
"ST1006": false,
"QF1001": false,
"QF1003": false,
"QF1008": false,
"ST1000": false,
"ST1020": false,
"ST1021": false,
"ST1022": false,
// Dot imports; this warning is enabled in .golangci.yml, but with an
// extra dot-import-whitelist config. Because I couldn't figure out how to
// specify that extra config for gopls, I'm disabling the check altogether
// here.
"ST1001": false,
},
},
"go.alternateTools": {
"golangci-lint-v2": "${workspaceFolder}/scripts/golangci-lint-shim.sh",
},
"go.lintTool": "golangci-lint-v2",
}
+3 -6
View File
@@ -24,24 +24,22 @@
{
"label": "Run current file integration test",
"type": "shell",
"command": "go run cmd/integration_test/main.go cli ${relativeFile}",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",
"isDefault": true
},
"presentation": {
"clear": true,
"focus": true
}
},
{
"label": "Run current file integration test (slow)",
"type": "shell",
"command": "go run cmd/integration_test/main.go cli --slow ${relativeFile}",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli --slow ${relativeFile}",
"problemMatcher": [],
"group": {
"clear": true,
"kind": "test",
},
"presentation": {
@@ -51,13 +49,12 @@
{
"label": "Run current file integration test (sandbox)",
"type": "shell",
"command": "go run cmd/integration_test/main.go cli --sandbox ${relativeFile}",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli --sandbox ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",
},
"presentation": {
"clear": true,
"focus": true
}
},
+26 -60
View File
@@ -10,19 +10,6 @@ before making a change.
[This video](https://www.youtube.com/watch?v=kNavnhzZHtk) walks through the process of adding a small feature to lazygit. If you have no idea where to start, watching that video is a good first step.
## Design principles
See [here](./VISION.md) for a set of design principles that we want to consider when building a feature or making a change.
## Codebase guide
[This doc](./docs/dev/Codebase_Guide.md) explains:
* what the different packages in the codebase are for
* where important files live
* important concepts in the code
* how the event loop works
* other useful information
## All code changes happen through Pull Requests
Pull requests are the best way to propose changes to the codebase. We actively
@@ -34,8 +21,6 @@ welcome your pull requests:
4. Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
5. Issue that pull request!
Please do not raise pull request from your fork's master branch: make a feature branch instead. Lazygit maintainers will sometimes push changes to your branch when reviewing a PR and we often can't do this if you use your master branch.
If you've never written Go in your life, then join the club! Lazygit was the maintainer's first Go program, and most contributors have never used Go before. Go is widely considered an easy-to-learn language, so if you're looking for an open source project to gain dev experience, you've come to the right place.
## Running in a VSCode dev container
@@ -60,31 +45,6 @@ To run lazygit from within the integrated terminal just go `go run main.go`
This allows you to contribute to Lazygit without needing to install anything on your local machine. The Codespace has all the necessary tools and extensions pre-installed.
## Using Nix for development
If you use Nix, you can leverage the included flake to set up a complete development environment with all necessary dependencies:
```sh
nix develop
```
This will drop you into a development shell that includes:
* Latest Go toolchain
* golangci-lint for code linting
* git and make
You can also build and run lazygit using nix:
```sh
# Build lazygit
nix build
# Run lazygit directly
nix run
```
The nix flake supports multiple architectures (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin) and provides a consistent development environment across different systems.
## Code of conduct
Please note by participating in this project, you agree to abide by the [code of conduct].
@@ -137,30 +97,12 @@ To run gofumpt from your terminal go:
go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
```
## Programming Font
Lazygit supports [Nerd Fonts](https://www.nerdfonts.com) to render certain icons. Sometimes we use some of these icons verbatim in string literals in the code (mainly in tests), so you need to set your development environment to use a nerd font to see these.
## Internationalisation
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package.
### For developers adding new text
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).
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')
### For translators
Lazygit translations are managed through [Crowdin](https://crowdin.com/project/lazygit/). If you'd like to contribute translations:
1. Join the Crowdin project at https://crowdin.com/project/lazygit/
2. Select your target language and help translate missing strings
3. The translation files in `pkg/i18n/translations/` are managed by the maintainers - please don't edit them directly
For detailed information about the translation process, including how maintainers sync translations, see `pkg/i18n/translations/README.md`.
## 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`).
@@ -197,7 +139,31 @@ If you want to trigger a debug session from VSCode, you can use the following sn
## Profiling
If you want to investigate what's contributing to CPU or memory usage, see [this separate document](docs/dev/Profiling.md).
If you want to investigate what's contributing to CPU usage you can add the following to the top of the `main()` function in `main.go`
```go
import "runtime/pprof"
func main() {
f, err := os.Create("cpu.prof")
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
...
```
Then run lazygit, and afterwards, from your terminal, run:
```sh
go tool pprof --web cpu.prof
```
That should open an application which allows you to view the breakdown of CPU usage.
## Testing
+2 -2
View File
@@ -2,14 +2,14 @@
# docker build -t lazygit .
# docker run -it lazygit:latest /bin/sh
FROM golang:1.25 as build
FROM golang:1.18 as build
WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build
FROM alpine:3.19
FROM alpine:3.15
RUN apk add --no-cache -U git xdg-utils
WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY --from=build /go/src/github.com/jesseduffield/lazygit ./
+1
View File
@@ -0,0 +1 @@
eUTlaNqeTB
+1
View File
@@ -0,0 +1 @@
qtuVMihtEl
-72
View File
@@ -1,72 +0,0 @@
.PHONY: all
all: build
.PHONY: build
build:
go build -gcflags='all=-N -l'
.PHONY: install
install:
go install
.PHONY: run
run: build
./lazygit
# Run `make run-debug` in one terminal tab and `make print-log` in another to view the program and its log output side by side
.PHONY: run-debug
run-debug:
go run main.go -debug
.PHONY: print-log
print-log:
go run main.go --logs
.PHONY: unit-test
unit-test:
go test ./... -short
.PHONY: test
test: unit-test integration-test-all
# Generate all our auto-generated files (test list, cheatsheets, maybe other things in the future)
.PHONY: generate
generate:
go generate ./...
.PHONY: format
format:
gofumpt -l -w .
.PHONY: lint
lint:
./scripts/golangci-lint-shim.sh run
# For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md.
.PHONY: integration-test-tui
integration-test-tui:
go run cmd/integration_test/main.go tui $(filter-out $@,$(MAKECMDGOALS))
.PHONY: integration-test-cli
integration-test-cli:
go run cmd/integration_test/main.go cli $(filter-out $@,$(MAKECMDGOALS))
.PHONY: integration-test-all
integration-test-all:
go test pkg/integration/clients/*.go
.PHONY: bump-gocui
bump-gocui:
scripts/bump_gocui.sh
.PHONY: bump-lazycore
bump-lazycore:
scripts/bump_lazycore.sh
.PHONY: record-demo
record-demo:
demo/record_demo.sh $(filter-out $@,$(MAKECMDGOALS))
.PHONY: vendor
vendor:
go mod vendor && go mod tidy
+57 -335
View File
File diff suppressed because one or more lines are too long
-104
View File
@@ -1,104 +0,0 @@
# Vision and Design Principles
## Vision
Lazygit's vision is to be the most enjoyable UI for git.
## Design Principles
There are seven (sometimes contradictory) design principles we follow:
- Discoverability
- Simplicity
- Safety
- Power
- Speed
- Conformity with git
- Think of the codebase
### Discoverability
TUI's are notoriously hard to learn, thanks to limited screen real-estate to provide contextual help and a general lack of effort on the part of developers to make things obvious. We want Lazygit to buck the trend and be easy for a new user to grok.
Examples:
- Clearly document all the features/configuration options
- e.g. gifs in the README
- Document how to solve various git problems with Lazygit
- This is something we don't have yet but should: a section in the docs explaining how Lazygit can help you in various scenarios
- Use tooltips to explain what actions will do
- Make it easy for users to ask questions and get answers from the community
- Make it easy to find entities and actions from within Lazygit
- Use visual elements to make things obvious
- e.g. '<-- YOU ARE HERE' label when rebasing
- Don't require the user to memorise keybindings
- e.g. when the user is mid-rebase, we prominently show that the keybinding for viewing rebase options is 'm'
- When the user performs an action in Lazygit, make the impact obvious
- If the affected entity isn't visible, show a toast notification
- If a keybinding is disabled, give a reason why
### Simplicity
The git CLI is very complex but most git use cases are simple. Lazygit needs to ensure that simple use cases are easy to satisfy.
- Make the most common use cases dead-simple (staging files, committing, pulling/pushing)
- Don't overwhelm the user with options
- Use sensible defaults
- We already have too many configuration options: think hard before adding any new ones
### Safety
It's easy to screw things up in git so Lazygit should try to protect the user from screwing things up.
- Prompt for a confirmation before doing anything that's hard to reverse
- Make it easy to correct mistakes
- e.g. undo action
- the escape key should get you out of most transient situations (rebasing, diffing, etc)
## Power
Users shouldn't have to drop down the CLI _too_ often. Lazygit should be able to handle some complex use cases.
- Make complex (but common) CLI flows simple
- e.g. interactive rebasing
- Use the custom commands system to handle the really rare complex edge-cases
### Speed
Pro users should be able to move at lightning speed with Lazygit.
- Always think about the number of keypresses involved in a given UX flow
- Make lazygit performant and responsive
- Think about the individual commands being run and how fast they are
- Startup should be FAST. If you want to run something at startup that is slow, make it non-blocking.
- Support muscle-memory
- Prefer disabling menu items instead of hiding them so that muscle memory can be used to select the desired menu item
- Try to make keybinding intuitions to transfer across contexts (e.g. 'd' for destroy)
- When changing keybindings in a new release, always consider what will happen if a user does not read the release notes and relies on muscle memory.
### Conformity with git
Satisfying the use-cases of git users is more important than perfectly conforming to git's API, but even obscure parts of git's API were motivated by real use-cases.
- Users should only have to drop down to the git CLI in rare circumstances
- Honour the git config
- Don't override anything set in the git config without the user's permission
- Work with git, not against it.
- Too much magic will get us into trouble
- Avoid storing Lazygit-specific session state that could instead be stored in git
- Ensure that Lazygit can represent the state of any repo
- Sometimes git's default behaviour is just silly and we'll make the call to override but it should be a well-considered decision.
### Think of the codebase
Will somebody PLEASE think of the codebase!
Some features are not worth the added complexity in the codebase. The more this codebase grows, the harder it will be to make the changes that everybody wants.
## Resolving conflicts
Many of the above objectives are directly antithetical to one another. If you add an extra confirmation prompt for the sake of _safety_, you're sacrificing _speed_. If you support toggling various git flags in the name of _power_, you're sacrificing _simplicity_. There are a few things to say here.
When there are conflicts, we need to make a judgement call. In general we should err on the side of safety and simplicity as the default, with the ability for users to make things faster / more powerful either through configuration or separate keybindings.
This does not mean for example that force pushes should be impossible without being manually enabled: force pushes are table stakes for anybody who rebases. But it does mean that a confirmation popup should appear when force pushing.
-26
View File
@@ -1,26 +0,0 @@
package main
import (
"encoding/json"
"log"
"os"
"github.com/jesseduffield/lazygit/pkg/i18n"
)
func saveLanguageFileToJson(tr *i18n.TranslationSet, filepath string) error {
jsonData, err := json.MarshalIndent(tr, "", " ")
if err != nil {
return err
}
jsonData = append(jsonData, '\n')
return os.WriteFile(filepath, jsonData, 0o644)
}
func main() {
err := saveLanguageFileToJson(i18n.EnglishTranslationSet(), "en.json")
if err != nil {
log.Fatal(err)
}
}
+15 -41
View File
@@ -16,7 +16,7 @@ Usage:
> go run cmd/integration_test/main.go cli [--slow] [--sandbox] <test1> <test2> ...
If you pass no test names, it runs all tests
Accepted environment variables:
INPUT_DELAY (e.g. 200): the number of milliseconds to wait between keypresses or mouse clicks
KEY_PRESS_DELAY (e.g. 200): the number of milliseconds to wait between keypresses
TUI mode:
> go run cmd/integration_test/main.go tui
@@ -26,29 +26,6 @@ Usage:
> go run cmd/integration_test/main.go help
`
type flagInfo struct {
name string // name of the flag; can be used with "-" or "--"
flag *bool // a pointer to the variable that should be set to true when this flag is passed
}
// Takes the args that you want to parse (excluding the program name and any
// subcommands), and returns the remaining args with the flags removed
func parseFlags(args []string, flags []flagInfo) []string {
outer:
for len(args) > 0 {
for _, f := range flags {
if args[0] == "-"+f.name || args[0] == "--"+f.name {
*f.flag = true
args = args[1:]
continue outer
}
}
break
}
return args
}
func main() {
if len(os.Args) < 2 {
log.Fatal(usage)
@@ -58,26 +35,23 @@ func main() {
case "help":
fmt.Println(usage)
case "cli":
testNames := os.Args[2:]
slow := false
sandbox := false
waitForDebugger := false
raceDetector := false
testNames := parseFlags(os.Args[2:], []flagInfo{
{"slow", &slow},
{"sandbox", &sandbox},
{"debug", &waitForDebugger},
{"race", &raceDetector},
})
clients.RunCLI(testNames, slow, sandbox, waitForDebugger, raceDetector)
case "tui":
raceDetector := false
remainingArgs := parseFlags(os.Args[2:], []flagInfo{
{"race", &raceDetector},
})
if len(remainingArgs) > 0 {
log.Fatal("tui only supports the -race argument.")
// get the next arg if it's --slow
if len(os.Args) > 2 {
if os.Args[2] == "--slow" || os.Args[2] == "-slow" {
testNames = os.Args[3:]
slow = true
} else if os.Args[2] == "--sandbox" || os.Args[2] == "-sandbox" {
testNames = os.Args[3:]
sandbox = true
}
}
clients.RunTUI(raceDetector)
clients.RunCLI(testNames, slow, sandbox)
case "tui":
clients.RunTUI()
default:
log.Fatal(usage)
}
-12
View File
@@ -1,12 +0,0 @@
(import (
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
nodeName = lock.nodes.root.inputs.flake-compat;
in
fetchTarball {
url =
lock.nodes.${nodeName}.locked.url
or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
sha256 = lock.nodes.${nodeName}.locked.narHash;
}
) { src = ./.; }).defaultNix
-2
View File
@@ -1,2 +0,0 @@
This directory contains stuff for recording lazygit demos.
-112
View File
@@ -1,112 +0,0 @@
# Specify a command to be executed
# like `/bin/bash -l`, `ls`, or any other commands
# the default is bash for Linux
# or powershell.exe for Windows
command: echo "YOU NEED TO SPECIFY YOUR OWN COMMAND WITH THE -d ARG"
# Specify the current working directory path
# the default is the current working directory path
cwd: null
# Export additional ENV variables
env:
recording: true
# Explicitly set the number of columns
# or use `auto` to take the current
# number of columns of your shell
cols: 120 # 100
# Explicitly set the number of rows
# or use `auto` to take the current
# number of rows of your shell
rows: 35 # 30
# Amount of times to repeat GIF
# If value is -1, play once
# If value is 0, loop indefinitely
# If value is a positive number, loop n times
repeat: 0
# Quality
# 1 - 100
# Higher quality seems to make no difference, but running it through
# gifsicle ends up with a much better compressed version.
quality: 100
# Delay between frames in ms
# If the value is `auto` use the actual recording delays
frameDelay: auto
# Maximum delay between frames in ms
# Ignored if the `frameDelay` isn't set to `auto`
# Set to `auto` to prevent limiting the max idle time
maxIdleTime: 2000
# The surrounding frame box
# The `type` can be null, window, floating, or solid`
# To hide the title use the value null
# Don't forget to add a backgroundColor style with a null as type
frameBox:
type: floating
title: Lazygit
style:
border: 0px black solid
backgroundColor: "#1d1d1d"
margin: -5px
# Add a watermark image to the rendered gif
# You need to specify an absolute path for
# the image on your machine or a URL, and you can also
# add your own CSS styles
watermark:
imagePath: null
style:
position: absolute
right: 15px
bottom: 15px
width: 100px
opacity: 0.9
# Cursor style can be one of
# `block`, `underline`, or `bar`
cursorStyle: block
# Font family
# You can use any font that is installed on your machine
# in CSS-like syntax
# Download from:
# https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/DejaVuSansMono.zip
# Not using the mono font because it makes icons too small.
fontFamily: "DejaVuSansM Nerd Font"
# The size of the font
fontSize: 8
# The height of lines
lineHeight: 1
# The spacing between letters
letterSpacing: 0
# Theme
theme:
background: "transparent"
foreground: "#dddad6"
cursor: "#c7c7c7"
black: "#7a7a7a"
red: "#fc4384"
green: "#b3e33b"
yellow: "#ffa727"
blue: "#102895"
magenta: "#c930c7"
cyan: "#00c5c7"
white: "#c7c7c7"
brightBlack: "#676767"
brightRed: "#ff7fac"
brightGreen: "#c8ed71"
brightYellow: "#ebdf86"
brightBlue: "#6871ff"
brightMagenta: "#ff76ff"
brightCyan: "#5ffdff"
brightWhite: "#fffefe"
-81
View File
@@ -1,81 +0,0 @@
#!/bin/sh
set -e
TYPE=$1
TEST=$2
usage() {
echo "Usage: $0 [gif|mp4] <test path>"
echo "e.g. using full path: $0 gif pkg/integration/tests/demo/nuke_working_tree.go"
exit 1
}
if [ "$#" -ne 2 ]
then
usage
fi
if [ "$TYPE" != "gif" ] && [ "$TYPE" != "mp4" ]
then
usage
exit 1
fi
if [ -z "$TEST" ]
then
usage
fi
WORKTREE_PATH=$(git worktree list | grep assets | awk '{print $1}')
if [ -z "$WORKTREE_PATH" ]
then
echo "Could not find assets worktree. You'll need to create a worktree for the assets branch using the following command:"
echo "git worktree add .worktrees/assets assets"
echo "The assets branch has no shared history with the main branch: it exists to store assets which are too large to store in the main branch."
exit 1
fi
OUTPUT_DIR="$WORKTREE_PATH/demo"
if ! command -v terminalizer &> /dev/null
then
echo "terminalizer could not be found"
echo "Install it with: npm install -g terminalizer"
exit 1
fi
if ! command -v "gifsicle" &> /dev/null
then
echo "gifsicle could not be found"
echo "Install it with: npm install -g gifsicle"
exit 1
fi
# Get last part of the test path and set that as the output name
# example test path: pkg/integration/tests/01_basic_test.go
# For that we want: NAME=01_basic_test
NAME=$(echo "$TEST" | sed -e 's/.*\///' | sed -e 's/\..*//')
# Add the demo to the tests list (if missing) so that it can be run
go generate pkg/integration/tests/tests.go
mkdir -p "$OUTPUT_DIR"
# First we record the demo into a yaml representation
terminalizer -c demo/config.yml record --skip-sharing -d "go run cmd/integration_test/main.go cli --slow $TEST" "$OUTPUT_DIR/$NAME"
# Then we render it into a gif
terminalizer render "$OUTPUT_DIR/$NAME" -o "$OUTPUT_DIR/$NAME.gif"
# Then we convert it to either an mp4 or gif based on the command line argument
if [ "$TYPE" = "mp4" ]
then
COMPRESSED_PATH="$OUTPUT_DIR/$NAME.mp4"
ffmpeg -y -i "$OUTPUT_DIR/$NAME.gif" -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" "$COMPRESSED_PATH"
else
COMPRESSED_PATH="$OUTPUT_DIR/$NAME-compressed.gif"
gifsicle --colors 256 --use-col=web -O3 < "$OUTPUT_DIR/$NAME.gif" > "$COMPRESSED_PATH"
fi
echo "Demo recorded to $COMPRESSED_PATH"
+229 -871
View File
File diff suppressed because it is too large Load Diff
+12 -64
View File
@@ -1,12 +1,12 @@
# Custom Command Keybindings
You can add custom command keybindings in your config.yml (accessible by pressing 'e' on the status panel from within lazygit) like so:
You can add custom command keybindings in your config.yml (accessible by pressing 'o' on the status panel from within lazygit) like so:
```yml
customCommands:
- key: '<c-r>'
context: 'commits'
command: 'hub browse -- "commit/{{.SelectedLocalCommit.Hash}}"'
command: 'hub browse -- "commit/{{.SelectedLocalCommit.Sha}}"'
- key: 'a'
context: 'files'
command: "git {{if .SelectedFile.HasUnstagedChanges}} add {{else}} reset {{end}} {{.SelectedFile.Name | quote}}"
@@ -14,7 +14,7 @@ customCommands:
- key: 'C'
context: 'global'
command: "git commit"
output: terminal
subprocess: true
- key: 'n'
context: 'localBranches'
prompts:
@@ -50,14 +50,15 @@ Custom command keybindings will appear alongside inbuilt keybindings when you vi
For a given custom command, here are the allowed fields:
| _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). Custom commands without a key specified can be triggered by selecting them from the keybindings (`?`) menu | no |
| 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 (using Go template syntax for placeholder values) | yes |
| context | The context in which to listen for the key (see [below](#contexts)) | yes |
| subprocess | Whether you want the command to run in a subprocess (e.g. if the command requires user input) | no |
| 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 |
| description | Label for the custom command when displayed in the keybindings menu | no |
| output | Where the output of the command should go. 'none' discards it, 'terminal' suspends lazygit and runs the command in the terminal (useful for commands that require user input), 'log' streams it to the command log, 'logWithPty' is like 'log' but runs the command in a pseudo terminal (can be useful for commands that produce colored output when the output is a terminal), and 'popup' shows it in a popup. | no |
| outputTitle | The title to display in the popup panel if output is set to 'popup'. If left unset, the command will be used as the title. | no |
| stream | Whether you want to stream the command's output to the Command Log panel | no |
| showOutput | Whether you want to show the command's output in a popup within Lazygit | no |
| after | Actions to take after the command has completed | no |
Here are the options for the `after` key:
@@ -73,7 +74,6 @@ The permitted contexts are:
| -------------- | -------------------------------------------------------------------------------------------------------- |
| status | The 'Status' tab |
| files | The 'Files' tab |
| worktrees | The 'Worktrees' tab |
| localBranches | The 'Local Branches' tab |
| remotes | The 'Remotes' tab |
| remoteBranches | The context you get when pressing enter on a remote in the remotes tab |
@@ -85,11 +85,6 @@ The permitted contexts are:
| stash | The 'Stash' tab |
| global | This keybinding will take affect everywhere |
> **Bonus**
>
> You can use a comma-separated string, such as `context: 'commits, subCommits'`, to make it effective in multiple contexts.
## Prompts
### Common fields
@@ -294,8 +289,9 @@ Here's an example using a command but not specifying anything else: so each line
Your commands can contain placeholder strings using Go's [template syntax](https://jan.newmarch.name/golang/template/chapter-template.html). The template syntax is pretty powerful, letting you do things like conditionals if you want, but for the most part you'll simply want to be accessing the fields on the following objects:
```
SelectedCommit
SelectedCommitRange
SelectedLocalCommit
SelectedReflogCommit
SelectedSubCommit
SelectedFile
SelectedPath
SelectedLocalBranch
@@ -304,66 +300,18 @@ SelectedRemote
SelectedTag
SelectedStashEntry
SelectedCommitFile
SelectedWorktree
CheckedOutBranch
```
(For legacy reasons, `SelectedLocalCommit`, `SelectedReflogCommit`, and `SelectedSubCommit` are also available, but they are deprecated.)
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/services/custom_commands/models.go) (all the modelling lives in the same file).
We don't support accessing all elements of a range selection yet. We might add this in the future, but as a special case you can access the range of selected commits by using `SelectedCommitRange`, which has two properties `.To` and `.From` which are the hashes of the bottom and top selected commits, respectively. This is useful for passing them to a git command that operates on a range of commits. For example, to create patches for all selected commits, you might use
```yml
command: "git format-patch {{.SelectedCommitRange.From}}^..{{.SelectedCommitRange.To}}"
```
We support the following functions:
### Quoting
Quote wraps a string in quotes with necessary escaping for the current platform.
```
git {{.SelectedFile.Name | quote}}
```
### Running a command
Runs a command and returns the output. If the command outputs more than a single line, it will produce an error.
```
initialValue: "username/{{ runCommand "date +\"%Y/%-m\"" }}/"
```
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
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)
## Menus of custom commands
For custom commands that are not used very frequently it may be preferable to hide them in a menu; you can assign a key to open the menu, and the commands will appear inside. This has the advantage that you don't have to come up with individual unique keybindings for all those commands that you don't use often; the keybindings for the commands in the menu only need to be unique within the menu. Here is an example:
```yml
customCommands:
- key: X
description: "Copy/paste commits across repos"
commandMenu:
- key: c
command: 'git format-patch --stdout {{.SelectedCommitRange.From}}^..{{.SelectedCommitRange.To}} | pbcopy'
context: commits, subCommits
description: "Copy selected commits to clipboard"
- key: v
command: 'pbpaste | git am'
context: "commits"
description: "Paste selected commits from clipboard"
```
If you use the commandMenu property, none of the other properties except key and description can be used.
## Debugging
If you want to verify that your command actually does what you expect, you can wrap it in an 'echo' call and set `output: popup` so that it doesn't actually execute the command but you can see how the placeholders were resolved.
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
+21 -73
View File
@@ -1,42 +1,38 @@
# Custom Pagers
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `e` in the Status panel).
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `o` in the Status panel).
Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support. However, see [below](#emulating-custom-pagers-on-windows) for a workaround.
Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support.
Multiple pagers are supported; you can cycle through them with the `|` key. This can be useful if you usually prefer a particular pager, but want to use a different one for certain kinds of diffs.
Pagers are configured with the `pagers` array in the git section; here's an example for a multi-pager setup:
## Default:
```yaml
git:
pagers:
- pager: delta --dark --paging=never
- pager: ydiff -p cat -s --wrap --width={{columnWidth}}
colorArg: never
- externalDiffCommand: difft --color=always
paging:
colorArg: always
useConfig: false
```
The `colorArg` key is for whether you want the `--color=always` arg in your `git diff` command. Some pagers want it set to `always`, others want it set to `never`. The default is `always`, since that's what most pagers need.
the `colorArg` key is for whether you want the `--color=always` arg in your `git diff` command. Some pagers want it set to `always`, others want it set to `never`.
## Delta:
```yaml
git:
pagers:
- pager: delta --dark --paging=never
paging:
colorArg: always
pager: delta --dark --paging=never
```
![](https://i.imgur.com/QJpQkF3.png)
A cool feature of delta is --hyperlinks, which renders clickable links for the line numbers in the left margin, and lazygit supports these. To use them, set the `pager:` config to `delta --dark --paging=never --line-numbers --hyperlinks --hyperlinks-file-link-format="lazygit-edit://{path}:{line}"`; this allows you to click on an underlined line number in the diff to jump right to that same line in your editor.
## Diff-so-fancy
```yaml
git:
pagers:
- pager: diff-so-fancy
paging:
colorArg: always
pager: diff-so-fancy
```
![](https://i.imgur.com/rjH1TpT.png)
@@ -47,70 +43,22 @@ git:
gui:
sidePanelWidth: 0.2 # gives you more space to show things side-by-side
git:
pagers:
- colorArg: never
pager: ydiff -p cat -s --wrap --width={{columnWidth}}
paging:
colorArg: never
pager: ydiff -p cat -s --wrap --width={{columnWidth}}
```
![](https://i.imgur.com/vaa8z0H.png)
Be careful with this one, I think the homebrew and pip versions are behind master. I needed to directly download the ydiff script to get the no-pager functionality working.
## Using external diff commands
Some diff tools can't work as a simple pager like the ones above do, because they need access to the entire diff, so just post-processing git's diff is not enough for them. The most notable example is probably [difftastic](https://difftastic.wilfred.me.uk).
These can be used in lazygit by using the `externalDiffCommand` config; in the case of difftastic, that could be
## Using git config
```yaml
git:
pagers:
- externalDiffCommand: difft --color=always
paging:
colorArg: always
useConfig: true
```
The `colorArg` and `pager` options are not used in this case.
You can add whatever extra arguments you prefer for your difftool; for instance
```yaml
git:
pagers:
- externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off
```
Instead of setting this command in lazygit's `externalDiffCommand` config, you can also tell lazygit to use the external diff command that is configured in git itself (`diff.external`), by using
```yaml
git:
pagers:
- useExternalDiffGitConfig: true
```
This can be useful if you also want to use it for diffs on the command line, and it also has the advantage that you can configure it per file type in `.gitattributes`; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
## Emulating custom pagers on Windows
There is a trick to emulate custom pagers on Windows using a Powershell script configured as an external diff command. It's not perfect, but certainly better than nothing. To do this, save the following script as `lazygit-pager.ps1` at a convenient place on your disk:
```pwsh
#!/usr/bin/env pwsh
$old = $args[1].Replace('\', '/')
$new = $args[4].Replace('\', '/')
$path = $args[0]
git diff --no-index --no-ext-diff $old $new
| %{ $_.Replace($old, $path).Replace($new, $path) }
| delta --width=$env:LAZYGIT_COLUMNS
```
Use the pager of your choice with the arguments you like in the last line of the script. Personally I wouldn't want to use lazygit anymore without delta's `--hyperlinks --hyperlinks-file-link-format="lazygit-edit://{path}:{line}"` args, see [above](#delta).
In your lazygit config, use
```yml
git:
pagers:
- externalDiffCommand: "C:/wherever/lazygit-pager.ps1"
```
The main limitation of this approach compared to a "real" pager is that renames are not displayed correctly; they are shown as if they were modifications of the old file. (This affects only the hunk headers; the diff itself is always correct.)
If you set `useConfig: true`, lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
-65
View File
@@ -1,65 +0,0 @@
# Fixup Commits
## Background
There's this common scenario that you have a PR in review, the reviewer is
requesting some changes, and you make those changes and would normally simply
squash them into the original commit that they came from. If you do that,
however, there's no way for the reviewer to see what you changed. You could just
make a separate commit with those changes at the end of the branch, but this is
not ideal because it results in a git history that is not very clean.
To help with this, git has a concept of fixup commits: you do make a separate
commit, but the subject of this commit is the string "fixup! " followed by the
original commit subject. This both tells the reviewer what's going on (you are
making a change that you later will squash into the designated commit), and it
provides an easy way to actually perform this squash operation when you are
ready to do that (before merging).
## Creating fixup commits
You could of course create fixup commits manually by typing in the commit
message with the prefix yourself. But lazygit has an easier way to do that:
in the Commits view, select the commit that you want to create a fixup for, and
press shift-F (for "Create fixup commit for this commit"). This automatically
creates a commit with the appropriate subject line.
Don't confuse this with the lowercase "f" command ("Fixup commit"); that one
squashes the selected commit into its parent, this is not what we want here.
## Creating amend commits
There's a special type of fixup commit that uses "amend!" instead of "fixup!" in
the commit message subject; in addition to fixing up the original commit with
changes it allows you to also (or only) change the commit message of the
original commit. The menu that appears when pressing shift-F has options for
both of these; they bring up a commit message panel similar to when you reword a
commit, but then create the "amend!" commit containing the new message. Note
that in that panel you only type the new message as you want it to be
eventually; lazygit then takes care of formatting the "amend!" commit
appropriately for you (with the subject of your new message moving into the body
of the "amend!" commit).
## Squashing fixup commits
When you're ready to merge the branch and want to squash all these fixup commits
that you created, that's very easy to do: select the first commit of your branch
and hit shift-S (for "Squash all 'fixup!' commits above selected commit
(autosquash)"). Boom, done.
## Finding the commit to create a fixup for
When you are making changes to code that you changed earlier in a long branch,
it can be tedious to find the commit to squash it into. Lazygit has a command to
help you with this, too: in the Files view, press ctrl-f to select the right
base commit in the Commits view automatically. From there, you can either press
shift-F to create a fixup commit for it, or shift-A to amend your changes into
the commit if you haven't published your branch yet.
If you have many modifications in your working copy, it is a good idea to stage
related changes that are meant to go into the same fixup commit; if no changes
are staged, ctrl-f works on all unstaged modifications, and then it might show
an error if it finds multiple different base commits. If you are interested in
what the command does to do its magic, and how you can help it work better, you
may want to read the [design document](dev/Find_Base_Commit_For_Fixup_Design.md)
that describes this.
+9 -11
View File
@@ -1,11 +1,9 @@
# Documentation Overview
* [Configuration](./Config.md).
* [Custom Commands](./Custom_Command_Keybindings.md)
* [Custom Pagers](./Custom_Pagers.md)
* [Dev docs](./dev)
* [Keybindings](./keybindings)
* [Undo/Redo](./Undoing.md)
* [Range Select](./Range_Select.md)
* [Searching/Filtering](./Searching.md)
* [Stacked Branches](./Stacked_Branches.md)
# Documentation Overview
* [Configuration](./Config.md).
* [Custom Commands](./Custom_Command_Keybindings.md)
* [Custom Pagers](./Custom_Pagers.md)
* [Keybindings](./keybindings)
* [Undo/Redo](./Undoing.md)
* [Searching/Filtering](./Searching.md)
* [Dev docs](./dev)
-14
View File
@@ -1,14 +0,0 @@
# Range Select
Some actions can be performed on a range of contiguous items. For example:
* staging multiple files at once
* squashing multiple commits at once
* copying (for cherry-pick) multiple commits at once
There are two ways to select a range of items:
1. Sticky range select: Press 'v' to toggle range select, then expand the selection using the up/down arrow key. To reset the selection, press 'v' again.
2. Non-sticky range select: Press shift+up or shift+down to expand the selection. To reset the selection, press up/down without shift.
The sticky option will be more familiar to vim users, and the second option will feel more natural to users who aren't used to doing things in a modal way.
In order to perform an action on a range of items, simply press the normal key for that action. If the action only works on individual items, it will raise an error. This is a new feature and the plan is to incrementally support range select for more and more actions. If there is an action you would like to support range select which currently does not, please raise an issue in the repo.
-18
View File
@@ -1,18 +0,0 @@
# Working with stacked branches
When working on a large branch it can often be useful to break it down into
smaller pieces, and it can help to create separate branches for each independent
chunk of changes. For example, you could have one branch for preparatory
refactorings, one for backend changes, and one for frontend changes. Those
branches would then all be stacked onto each other.
Git has support for rebasing such a stack as a whole; you can enable it by
setting the git config `rebase.updateRefs` to true. If you then rebase the
topmost branch of the stack, the other ones in the stack will follow. This
includes interactive rebases, so for example amending a commit in the first
branch of the stack will "just work" in the sense that it keeps the other
branches properly stacked onto it.
Lazygit visualizes the individual branch heads in the stack by marking them with a
cyan asterisk (or a cyan branch symbol if you are using [nerd
fonts](Config.md#display-nerd-fonts-icons)).
+3 -3
View File
@@ -1,9 +1,9 @@
# Undo/Redo in lazygit
You can undo the last action by pressing 'z' and redo with `ctrl+z`. Here we drop a couple of commits and then undo the actions.
Undo uses the reflog which is specific to commits and branches so we can't undo changes to the working tree or stash.
![Gif](../../assets/undo2.gif)
![undo](../../assets/demo/undo-compressed.gif)
## Keybindings:
'z' to undo, 'ctrl+z' to redo
## How it works
+2 -2
View File
@@ -2,7 +2,7 @@
## The use-case
This topic deserves its own doc because there are a few touch points for it. We have a use-case for knowing when Lazygit is idle or busy because integration tests follow the following process:
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
@@ -57,7 +57,7 @@ go utils.Safe(f)
Where `utils.Safe` is a helper function that ensures we clean up the gui if the goroutine panics.
### Programmatically enqueueing a UI event
### 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()`.
-100
View File
@@ -1,100 +0,0 @@
# Lazygit Codebase Guide
## Packages
* `pkg/app`: Contains startup code, initialises a bunch of stuff like logging, the user config, etc, before starting the gui. Catches and handles some errors that the gui raises.
* `pkg/app/daemon`: Contains code relating to the lazygit daemon. This could be better named: it's is not a daemon in the sense that it's a long-running background process; rather it's a short-lived background process that we pass to git for certain tasks, like GIT_EDITOR for when we want to set the TODO file for an interactive rebase.
* `pkg/cheatsheet`: Generates the keybinding cheatsheets in `docs/keybindings`.
* `pkg/commands/git_commands`: All communication to the git binary happens here. So for example there's a `Checkout` method which calls `git checkout`.
* `pkg/commands/oscommands`: Contains code for talking to the OS, and for invoking commands in general
* `pkg/commands/git_config`: Reading of the git config all happens here.
* `pkg/commands/hosting_service`: Contains code that is specific to git hosting services (aka forges).
* `pkg/commands/models`: Contains model structs that represent commits, branches, files, etc.
* `pkg/commands/patch`: Contains code for parsing and working with git patches
* `pkg/common`: Contains the `Common` struct which holds common dependencies like the logger, i18n, and the user config. Most structs in the code will have a field named `c` which holds a common struct (or a derivative of the common struct).
* `pkg/config`: Contains code relating to the Lazygit user config. Specifically `pkg/config/user_config/go` defines the user config struct and its default values. See [below](#using-userconfig) for some important information about using it.
* `pkg/constants`: Contains some constant strings (e.g. links to docs)
* `pkg/env`: Contains code relating to setting/getting environment variables
* `pkg/i18n`: Contains internationalised strings
* `pkg/integration`: Contains end-to-end tests
* `pkg/jsonschema`: Contains generator for user config JSON schema.
* `pkg/logs`: Contains code for instantiating the logger and for tailing the logs via `lazygit --logs`
* `pkg/tasks`: Contains code for running asynchronous tasks: mostly related to efficiently rendering command output to the main window.
* `pkg/theme`: Contains code related to colour themes.
* `pkg/updates`: Contains code related to Lazygit updates (checking for update, download and installing the update)
* `pkg/utils`: Contains lots of low-level helper functions
* `pkg/gui`: Contains code related to the gui. We've still got a God Struct in the form of our Gui struct, but over time code has been moved out into contexts, controllers, and helpers, and we intend to continue moving more code out over time.
* `pkg/gui/context`: Contains code relating to contexts. There is a context for each view e.g. a branches context, a tags context, etc. Contexts manage state related to the view and receive keypresses.
* `pkg/gui/controllers`: Contains code relating to controllers. Controllers define a list of keybindings and their associated handlers. One controller can be assigned to multiple contexts, and one context can contain multiple controllers.
* `pkg/gui/controllers/helpers`: Contains code that is shared between multiple controllers.
* `pkg/gui/filetree`: Contains code relating to the representation of filetrees.
* `pkg/gui/keybindings`: Contains code for mapping between keybindings and their labels
* `pkg/gui/mergeconflicts`: Contains code relating to the handling of merge conflicts
* `pkg/gui/modes`: Contains code relating to the state of different modes e.g. cherry picking mode, rebase mode.
* `pkg/gui/patch_exploring`: Contains code relating to the state of patch-oriented views like the staging view.
* `pkg/gui/popup`: Contains code that lets you easily raise popups
* `pkg/gui/presentation`: Contains presentation code i.e. code concerned with rendering content inside views
* `pkg/gui/services/custom_commands`: Contains code related to user-defined custom commands.
* `pkg/gui/status`: Contains code for invoking loaders and toasts
* `pkg/gui/style`: Contains code for specifying text styles (colour, bold, etc)
* `pkg/gui/types`: Contains various gui-specific types and interfaces. Lots of code lives here to avoid circular dependencies
* `vendor/github.com/jesseduffield/gocui`: Gocui is the underlying library used for handling the gui event loop, handling keypresses, and rendering the UI. It defines the View struct which our own context structs build upon.
## Important files
* `pkg/config/user_config.go`: defines the user config and default values
* `pkg/gui/keybindings.go`: defines keybindings which have not yet been moved into a controller (originally all keybindings were defined here)
* `pkg/gui/controllers.go`: links up controllers with contexts
* `pkg/gui/controllers/helpers/helpers.go`: defines all the different helper structs
* `pkg/commands/git.go`: defines all the different git command structs
* `pkg/gui/gui.go`: defines the top-level gui state and gui initialisation/run code
* `pkg/gui/layout.go`: defines what happens on each render
* `pkg/gui/controllers/helpers/window_arrangement_helper.go`: defines the layout of the UI and the size/position of each window
* `pkg/gui/context/context.go`: defines the different contexts
* `pkg/gui/context/setup.go`: defines initialisation code for all contexts
* `pkg/gui/context.go`: manages the lifecycle of contexts, the context stack, and focus changes.
* `pkg/gui/types/views.go`: defines views
* `pkg/gui/views.go`: defines the ordering of views (front to back) and their initialisation code
* `pkg/gui/gui_common.go`: defines gui-specific methods that all controllers and helpers have access to
* `pkg/i18n/english.go`: defines the set of i18n strings and their English values
* `pkg/gui/controllers/helpers/refresh_helper.go`: manages refreshing of models. The refresh helper is typically invoked at the end of an action to re-load affected models from git (e.g. re-load branches after doing a git pull)
* `pkg/gui/controllers/quit_actions.go`: contains code that runs when you hit 'escape' on a view (assuming the view doesn't define its own escape handler)
* `vendor/github.com/jesseduffield/gocui/gui.go`: defines the gocui gui struct
* `vendor/github.com/jesseduffield/gocui/view.go`: defines the gocui view struct
## Concepts
* **View**: Views are defined in the gocui package, and they maintain an internal buffer of content which is rendered each time the screen is drawn.
* **Context**: A context is tied to a view and contains some additional state and logic specific to that view e.g. the branches context has code relating specifically to branches, and writes the list of branches to the branches view. Views and contexts share some responsibilities for historical reasons.
* **Controller**: A controller defined keybindings with associated handlers. One controller can be assigned to multiple contexts and one context can have multiple controllers. For example the list controller handles keybindings relating to navigating a list, and is assigned to all list contexts (e.g. the branches context).
* **Helper**: A helper defines shared code used by controllers, or used by some other parts of the application. Often a controller will have a method that ends up needing to be used by another controller, so in that case we move the method out into a helper so that both controllers can use it. We need to do this because controllers cannot refer to other controllers' methods.
In terms of dependencies, controllers sit at the highest level, so they can refer to helpers, contexts, and views (although it's preferable for view-specific code to live in contexts). Helpers can refer to contexts and views, and contexts can only refer to views. Views can't refer to contexts, controllers, or helpers.
* **Window**: A window is a section of the screen which will render a view. Windows are named after the default view that appears there, so for example there is a 'stash' window that is so named because by default the stash view appears there. But if you press enter on a stash entry, the stash entry's files will be shown in a different view, but in the same window.
* **Panel**: The term 'panel' is still used in a few places to refer to either a view or a window, and it's a term that is now deprecated in favour of 'view' and 'window'.
* **Tab**: Each tab in a window (e.g. Files, Worktrees, Submodules) actually has a corresponding view which we bring to the front upon changing tabs.
* **Model**: Representation of a git object e.g. commits, branches, files.
* **ViewModel**: Used by a context to maintain state related to the view.
* **Keybinding**: A keybinding associates a _key_ with an _action_. For example if you press the 'down' arrow, the action performed will be your cursor moving down a list by one.
* **Action**: An action is the thing that happens when you press a key. Often an action will invoke a git command, but not always: for example, navigation actions don't involve git.
* **Common structs**: Most structs have a field named `c` which contains a 'common' struct: a struct containing a bag of dependencies that most structs of the same layer require. For example if you want to access a helper from a controller you can do so with `self.c.Helpers.MyHelper`.
## Event loop and threads
The event loop is managed in the `MainLoop` function of `vendor/github.com/jesseduffield/gocui/gui.go`. Any time there is an event like a key press or a window resize, the event will be processed and then the screen will be redrawn. This involves calling the `layout` function defined in `pkg/gui/layout.go`, which lays out the windows and invokes some on-render hooks.
Often, as part of handling a keypress, we'll want to run some code asynchronously so that it doesn't block the UI thread. For this we'll typically run `self.c.OnWorker(myFunc)`. If the worker wants to then do something on the UI thread again it can call `self.c.OnUIThread(myOtherFunc)`.
## Using UserConfig
The UserConfig struct is loaded from lazygit's global config file (and possibly repo-specific config files). It can be re-loaded while lazygit is running, e.g. when the user edits one of the config files. In this case we should make sure that any new or changed config values take effect immediately. The easiest way to achieve this is what we do in most controllers or helpers: these have a pointer to the `common.Common` struct, which contains the UserConfig, and access it from there. Since the UserConfig instance in `common.Common` is updated whenever we reload the config, the code can be sure that it always uses an up-to-date value, and there's nothing else to do.
If that's not possible for some reason, see if you can add code to `Gui.onUserConfigLoaded` to update things from the new config; there are some examples in that function to use as a guide. If that's too hard to do too, add the config to the list in `Gui.checkForChangedConfigsThatDontAutoReload` so that the user is asked to quit and restart lazygit.
## Legacy code structure
Before we had controllers and contexts, all the code lived directly in the gui package under a gui God Struct. This was fairly bloated and so we split things out to have a better separation of concerns. Nonetheless, it's a big effort to migrate all the code so we still have some logic in the gui struct that ought to live somewhere else. Likewise, we have some keybindings defined in `pkg/gui/keybindings.go` that ought to live on a controller (all keybindings used to be defined in that one file).
The new structure has its own problems: we don't have a clear guide on whether code should live in a controller or helper. The current approach is to put code in a controller until it's needed by another controller, and to then extract it out into a helper. We may be better off just putting code in helpers to start with and leaving controllers super-thin, with the responsibility of just pairing keys with corresponding helper functions. But it's not clear to me if that would be better than the current approach.
-82
View File
@@ -1,82 +0,0 @@
# Demo Recordings
We want our demo recordings to be consistent and easy to update if we make changes to Lazygit's UI. Luckily for us, we have an existing recording system for the sake of our integration tests, so we can piggyback on that.
You'll want to familiarise yourself with how integration tests are written: see [here](../../pkg/integration/README.md).
## Prerequisites
Ideally we'd run this whole thing through docker but we haven't got that working. So you will need:
```
# for recording
npm i -g terminalizer
# for gif compression
npm i -g gifsicle
# for mp4 conversion
brew install ffmpeg
# font with icons
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/DejaVuSansMono.tar.xz && \
tar -xf DejaVuSansMono.tar.xz -C /usr/local/share/fonts && \
rm DejaVuSansMono.tar.xz
```
## Creating a demo
Demos are found in `pkg/integration/tests/demo/`. They are like regular integration tests but have `IsDemo: true` which has a few effects:
* The bottom row of the UI is quieter so that we can render captions
* Fetch/Push/Pull have artificial latency to mimic a network request
* The loader at the bottom-right does not appear
In demos, we don't need to be as strict in our assertions as we are in tests. But it's still good to have some basic assertions so that if we automate the process of updating demos we'll know if one of them has broken.
You can use the same flow as we use with integration tests when you're writing a demo:
* Setup the repo
* Run the demo in sandbox mode to get a feel of what needs to happen
* Come back and write the code to make it happen
### Adding captions
It's good to add captions explaining what task if being performed. Use the existing demos as a guide.
### Setting up the assets worktree
We store assets (which includes demo recordings) in the `assets` branch, which is a branch that shares no history with the main branch and exists purely for storing assets. Storing them separately means we don't clog up the code branches with large binaries.
The scripts and demo definitions live in the code branches but the output lives in the assets branch so to be able to create a video from a demo you'll need to create a linked worktree for the assets branch which you can do with:
```sh
git worktree add .worktrees/assets assets
```
Outputs will be stored in `.worktrees/assets/demos/`. We'll store three separate things:
* the yaml of the recording
* the original gif
* either the compressed gif or the mp4 depending on the output you chose (see below)
### Recording the demo
Once you're happy with your demo you can record it using:
```sh
scripts/record_demo.sh [gif|mp4] <path>
# e.g.
scripts/record_demo.sh gif pkg/integration/tests/demo/interactive_rebase.go
```
~~The gif format is for use in the first video of the readme (it has a larger size but has auto-play and looping)~~
~~The mp4 format is for everything else (no looping, requires clicking, but smaller size).~~
Turns out that you can't store mp4s in a repo and link them from a README so we're gonna just use gifs across the board for now.
### Including demos in README/docs
If you've followed the above steps you'll end up with your output in your assets worktree.
Within that worktree, stage all three output files and raise a PR against the assets branch.
Then back in the code branch, in the doc, you can embed the recording like so:
```md
![Nuke working tree](../assets/demo/interactive_rebase-compressed.gif)
```
This means we can update assets without needing to update the docs that embed them.
@@ -1,229 +0,0 @@
# About the mechanics of lazygit's "Find base commit for fixup" command
## Background
Lazygit has a command called "Find base commit for fixup" that helps with
creating fixup commits. (It is bound to "ctrl-f" by default, and I'll call it
simply "the ctrl-f command" throughout the rest of this text for brevity.)
It's a heuristic that needs to make a few assumptions; it tends to work well in
practice if users are aware of its limitations. The user-facing side of the
topic is explained [here](../Fixup_Commits.md). In this document we describe how
it works internally, and the design decisions behind it.
It is also interesting to compare it to the standalone tool
[git-absorb](https://github.com/tummychow/git-absorb) which does a very similar
thing, but made different decisions in some cases. We'll explore these
differences in this document.
## Design goals
I'll start with git-absorb's design goals (my interpretation, since I can't
speak for git-absorb's maintainer of course): its main goal seems to be minimum
user interaction required. The idea is that you have a PR in review, the
reviewer requested a bunch of changes, you make all these changes, so you have a
working copy with lots of modified files, and then you fire up git-absorb and it
creates all the necessary fixup commits automatically with no further user
intervention.
While this sounds attractive, it conflicts with ctrl-f's main design goal, which
is to support creating high-quality fixups. My philosophy is that fixup commits
should have the same high quality standards as normal commits; in particular:
- they should be atomic. This means that multiple diff hunks that belong
together to form one logical change should be in the same fixup commit. (Not
always possible if the logical change needs to be fixed up into several
different base commits.)
- they should be minimal. Every fixup commit should ideally contain only one
logical change, not several unrelated ones.
Why is this important? Because fixup commits are mainly a tool for reviewing (if
they weren't, you might as well squash the changes into their base commits right
away). And reviewing fixup commits is easier if they are well-structured, just
like normal commits.
The only way to achieve this with git-absorb is to set the `oneFixupPerCommit`
config option (for the first goal), and then manually stage the changes that
belong together (for the second). This is close to what you have to do with
ctrl-f, with one exception that we'll get to below.
But ctrl-f enforces this by refusing to do the job if the staged hunks belong to
more than one base commit. Git-absorb will happily create multiple fixup commits
in this case; ctrl-f doesn't, to enforce that you pay attention to how you group
the changes. There's another reason for this behavior: ctrl-f doesn't create
fixup commits itself (unlike git-absorb), instead it just selects the found base
commit so that the user can decide whether to amend the changes right in, or
create a fixup commit from there (both are single-key commands in lazygit). And
lazygit doesn't support non-contiguous multiselections of commits, but even if
it did, it wouldn't help much in this case.
## The mechanics
### General approach
Git-absorb uses a relatively simple approach, and the benefit is of course that
it is easy to understand: it looks at every diff hunk separately, and for every
hunk it looks at all commits (starting from the newest one backwards) to find
the earliest commit that the change can be amended to without conflicts.
It is important to realize that "diff hunk" doesn't necessarily mean what you
see in the diff view. Git-absorb and ctrl-f both use a context of 0 when diffing
your code, so they often see more and smaller hunks than users do. For example,
moving a line of code down by one line is a single hunk for users, but it's two
separate hunks for git-absorb and ctrl-f; one for deleting the line at the old
place, and another one for adding the line at the new place, even if it's only
one line further down.
From this, it follows that there's one big problem with git-absorb's approach:
when moving code, it doesn't realize that the two related hunks of deleting the
code from the old place and inserting it at the new place belong together, and
often it will manage to create a fixup commit for the first hunk, but leave the
other hunk in your working copy as "don't know what to do with this". As an
example, suppose your PR is adding a line of code to an existing function, maybe
one that declares a new variable, and a reviewer suggests to move this line down
a bit, closer to where some other related variables are declared. Moving the
line down results in two diff hunks (from the perspective of git-absorb and
ctrl-f, as they both use a context of 0 when diffing), and when looking at the
second diff hunk in isolation there's no way to find a base commit in your PR
for it, because the surrounding code is already on main.
To solve this, the ctrl-f command makes a distinction between hunks that have
deleted lines and hunks that have only added lines. If the whole diff contains
any hunks that have deleted lines, it uses only those hunks to determine the
base commit, and then assumes that all the hunks that have only added lines
belong into the same commit. This nicely solves the above example of moving
code, but also other examples such as the following:
<details>
<summary>Click to show example</summary>
Suppose you have a PR in which you added the following function:
```go
func findCommit(hash string) (*models.Commit, int, bool) {
for i, commit := range self.c.Model().Commits {
if commit.Hash == hash {
return commit, i, true
}
}
return nil, -1, false
}
```
A reviewer suggests to replace the manual `for` loop with a call to
`lo.FindIndexOf` since that's less code and more idiomatic. So your modification
is this:
```diff
--- a/my_file.go
+++ b/my_file.go
@@ -12,2 +12,3 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
+ "github.com/samber/lo"
"golang.org/x/sync/errgroup"
@@ -308,9 +309,5 @@ func (self *FixupHelper) blameAddedLines(addedLineHunks []*hunk) ([]string, error
func findCommit(hash string) (*models.Commit, int, bool) {
- for i, commit := range self.c.Model().Commits {
- if commit.Hash == hash {
- return commit, i, true
- }
- }
-
- return nil, -1, false
+ return lo.FindIndexOf(self.c.Model().Commits, func(commit *models.Commit) bool {
+ return commit.Hash == hash
+ })
}
```
If we were to look at these two hunks separately, we'd easily find the base
commit for the second one, but we wouldn't find the one for the first hunk
because the imports around the added import have been on main for a long time.
In fact, git-absorb leaves this hunk in the working copy because it doesn't know
what to do with it.
</details>
Only if there are no hunks with deleted lines does ctrl-f look at the hunks with
only added lines and determines the base commit for them. This solves cases like
adding a comment above a function that you added in your PR.
The downside of this more complicated approach is that it relies on the user
staging related hunks correctly. However, in my experience this is easy to do
and not very error-prone, as long as users are aware of this behavior. Lazygit
tries to help making them aware of it by showing a warning whenever there are
hunks with only added lines in addition to hunks with deleted lines.
### Finding the base commit for a given hunk
As explained above, git-absorb finds the base commit by walking the commits
backwards until it finds one that conflicts with the hunk, and then the found
base commit is the one just before that one. This works reliably, but it is
slow.
Ctrl-f uses a different approach that is usually much faster, but should always
yield the same result. Again, it makes a distinction between hunks with deleted
lines and hunks with only added lines. For hunks with deleted lines it performs
a line range blame for all the deleted lines (e.g. `git blame -L42,+3 --
filename`), and if the result is the same for all deleted lines, then that's the
base commit; otherwise it returns an error.
For hunks with only added lines, it gets a little more complicated. We blame the
single lines just before and just after the hunk (I'll ignore the edge cases of
either of those not existing because the hunk is at the beginning or end of the
file; read the code to see how we handle these cases). If the blame result is
the same for both, then that's the base commit. This is the case of adding a
line in the middle of a block of code that was added in the PR. Otherwise, the
base commit is the more recent of the two (and in this case it doesn't matter if
the other one is an earlier commit in the current branch, or a possibly very old
commit that's already on main). This covers the common case of adding a comment
to a function that was added in the PR, but also adding another line at the end
of a block of code that was added in the base commit.
It's interesting to discuss what "more recent" means here. You could say if
commit A is an ancestor of commit B (or in other words, A is reachable from B)
then B is the more recent one. And if none of the two commits is reachable from
the other, you have an error case because it's unclear which of the two should
be considered the base commit. The scenario in which this happens is a commit
history like this:
```
C---D
/ \
A---B---E---F---G
```
where, for instance, D and E are the two blame results.
Unfortunately, determining the ancestry relationship between two commits using
git commands is a bit expensive and not totally straightforward. Fortunately,
it's not necessary in lazygit because lazygit has the most recent 300 commits
cached in memory, and can simply search its linear list of commits to see which
one is closer to the beginning of the list. If only one of the two commits is
found within those 300 commits, then that's the more recent one; if neither is
found, we assume that both commits are on main and error out. In the merge
scenario pictured above, we arbitrarily return one of the two commits (this will
depend on the log order), but that's probably fine as this scenario should be
extremely rare in practice; in most cases, feature branches are simply linear.
### Knowing where to stop searching
Git-absorb needs to know when to stop walking backwards searching for commits,
since it doesn't make sense to create fixups for commits that are already on
main. However, it doesn't know where the current branch ends and main starts, so
it needs to rely on user input for this. By default it searches the most recent
10 commits, but this can be overridden with a config setting. In longer branches
this is often not enough for finding the base commit; but setting it to a higher
value causes the command to take longer to complete when the base commit can't
be found.
Lazygit doesn't have this problem. For a given blame result it needs to
determine whether that commit is already on main, and if it can find the commit
in its cached list of the first 300 commits it can get that information from
there, because lazygit knows what the user's configured main branches are
(`master` and `main` by default, but it could also include branches like `devel`
or `1.0-hotfixes`), and so it can tell for each commit whether it's contained in
one of those main branches. And if it can't find it among the first 300 commits,
it assumes the commit already on main, on the assumption that no feature branch
has more than 300 commits.
-69
View File
@@ -1,69 +0,0 @@
# Profiling Lazygit
If you want to investigate what's contributing to CPU or memory usage, start
lazygit with the `-profile` command line flag. This tells it to start an
integrated web server that listens for profiling requests.
## Save profile data
### CPU
While lazygit is running with the `-profile` flag, perform a CPU profile and
save it to a file by running this command in another terminal window:
```sh
curl -o cpu.out http://127.0.0.1:6060/debug/pprof/profile
```
By default, it profiles for 30 seconds. To change the duration, use
```sh
curl -o cpu.out 'http://127.0.0.1:6060/debug/pprof/profile?seconds=60'
```
### Memory
To save a heap profile (containing information about all memory allocated so
far since startup), use
```sh
curl -o mem.out http://127.0.0.1:6060/debug/pprof/heap
```
Sometimes it can be useful to get a delta log, i.e. to see how memory usage
developed from one point in time to another. For that, use
```sh
curl -o mem.out 'http://127.0.0.1:6060/debug/pprof/heap?seconds=20'
```
This will log the memory usage difference between now and 20 seconds later, so
it gives you 20 seconds to perform the action in lazygit that you are interested
in measuring.
## View profile data
To display the profile data, you can either use speedscope.app, or the pprof
tool that comes with go. I prefer the former because it has a nicer UI and is a
little more powerful; however, I have seen cases where it wasn't able to load a
profile for some reason, in which case it's good to have the pprof tool as a
fallback.
### Speedscope.app
Go to https://www.speedscope.app/ in your browser, and drag the saved profile
onto the browser window. Refer to [the
documentation](https://github.com/jlfwong/speedscope?tab=readme-ov-file#usage)
for how to navigate the data.
### Pprof tool
To view a profile that you saved as `cpu.out`, use
```sh
go tool pprof -http=:8080 cpu.out
```
By default this shows the graph view, which I don't find very useful myself.
Choose "Flame Graph" from the View menu to show a much more useful
representation of the data.
+1 -5
View File
@@ -1,8 +1,4 @@
# Dev Documentation Overview
* [Codebase Guide](./Codebase_Guide.md)
* [Busy/Idle Tracking](./Busy.md)
* [Busy/Idle tracking](./Busy.md).
* [Integration Tests](../../pkg/integration/README.md)
* [Demo Recordings](./Demo_Recordings.md)
* [Find base commit for fixup design](Find_Base_Commit_For_Fixup_Design.md)
* [Profiling](Profiling.md)
-4
View File
@@ -20,15 +20,11 @@
| `<pgup>` | Pgup |
| `<pgdown>` | Pgdn |
| `<up>` | ArrowUp |
| `<s-up>` | ShiftArrowUp |
| `<down>` | ArrowDown |
| `<s-down>` | ShiftArrowDown |
| `<left>` | ArrowLeft |
| `<right>` | ArrowRight |
| `<tab>` | Tab |
| `<backtab>` | Backtab |
| `<enter>` | Enter |
| `<a-enter>` | AltEnter |
| `<esc>` | Esc |
| `<backspace>` | Backspace |
| `<c-space>` | CtrlSpace |
+266 -346
View File
@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit Keybindings
@@ -6,408 +6,328 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Global keybindings
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Switch to a recent repo | |
| `` <pgup> (fn+up/shift+k) `` | Scroll up main window | |
| `` <pgdown> (fn+down/shift+j) `` | Scroll down main window | |
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | View custom patch options | |
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | Refresh | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Next screen mode (normal/half/fullscreen) | |
| `` _ `` | Prev screen mode | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | Cancel | |
| `` ? `` | Open keybindings menu | |
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | Quit | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | Undo | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
| `` Z `` | Redo | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
<pre>
<kbd>&lt;c-r&gt;</kbd>: Switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: Scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<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>: Execute custom command
<kbd>&lt;c-p&gt;</kbd>: View custom patch options
<kbd>m</kbd>: View merge/rebase options
<kbd>R</kbd>: Refresh
<kbd>+</kbd>: Next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: Prev screen mode
<kbd>?</kbd>: Open menu
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: Undo
<kbd>&lt;c-z&gt;</kbd>: Redo
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## List panel navigation
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Previous page | |
| `` . `` | Next page | |
| `` < (<home>) `` | Scroll to top | |
| `` > (<end>) `` | Scroll to bottom | |
| `` v `` | Toggle range select | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | Search the current view by text | |
| `` H `` | Scroll left | |
| `` L `` | Scroll right | |
| `` ] `` | Next tab | |
| `` [ `` | Previous tab | |
<pre>
<kbd>,</kbd>: Previous page
<kbd>.</kbd>: Next page
<kbd>&lt;</kbd>: Scroll to top
<kbd>&gt;</kbd>: Scroll to bottom
<kbd>/</kbd>: Search the current view by text
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Next tab
<kbd>[</kbd>: Previous tab
</pre>
## Commit files
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy path to clipboard | |
| `` y `` | Copy to clipboard | |
| `` c `` | Checkout | Checkout file. This replaces the file in your working tree with the version from the selected commit. |
| `` d `` | Remove | Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file. |
| `` o `` | Open file | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Toggle file included in patch | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Toggle all files | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Enter file / Toggle directory collapsed | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | Toggle file tree view | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Search the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the committed file name to the clipboard
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: Toggle file tree view
<kbd>/</kbd>: Search the current view by text
</pre>
## Commit summary
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirm | |
| `` <esc> `` | Close | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Confirm
<kbd>&lt;esc&gt;</kbd>: Close
</pre>
## Commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` b `` | View bisect options | |
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | Reword | Reword the selected commit's message. |
| `` R `` | Reword with editor | |
| `` d `` | Drop | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | Edit the selected commit. Use this to start an interactive rebase from the selected commit. When already mid-rebase, this will mark the selected commit for editing, which means that upon continuing the rebase, the rebase will pause at the selected commit to allow you to make changes. |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.<br>If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Mark the selected commit to be picked (when mid-rebase). This means that the commit will be retained upon continuing the rebase. |
| `` F `` | Create fixup commit | Create 'fixup!' commit for the selected commit. Later on, you can press `S` on this same commit to apply all above fixup commits. |
| `` S `` | Apply fixup commits | Squash all 'fixup!' commits, either above the selected commit, or all in current branch (autosquash). |
| `` <c-j> `` | Move commit down one | |
| `` <c-k> `` | Move commit up one | |
| `` V `` | Paste (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Amend commit with staged changes. If the selected commit is the HEAD commit, this will perform `git commit --amend`. Otherwise the commit will be amended via a rebase. |
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | View log options | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Copy (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: Reword commit
<kbd>R</kbd>: Reword commit with editor
<kbd>d</kbd>: Delete commit
<kbd>e</kbd>: Edit commit
<kbd>p</kbd>: Pick commit (when mid-rebase)
<kbd>F</kbd>: Create fixup commit for this commit
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
<kbd>&lt;c-j&gt;</kbd>: Move commit down one
<kbd>&lt;c-k&gt;</kbd>: Move commit up one
<kbd>v</kbd>: Paste commits (cherry-pick)
<kbd>A</kbd>: Amend commit with staged changes
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Revert commit
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: Copy commit (cherry-pick)
<kbd>C</kbd>: Copy commit range (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Search the current view by text
</pre>
## Confirmation panel
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirm | |
| `` <esc> `` | Close/Cancel | |
| `` <c-o> `` | Copy to clipboard | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Confirm
<kbd>&lt;esc&gt;</kbd>: Close/Cancel
</pre>
## Files
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy path to clipboard | |
| `` <space> `` | Stage | Toggle staged for selected file. |
| `` <c-b> `` | Filter files by status | |
| `` y `` | Copy to clipboard | |
| `` c `` | Commit | Commit staged changes. |
| `` w `` | Commit changes without pre-commit hook | |
| `` A `` | Amend last commit | |
| `` C `` | Commit changes using git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | Open file | Open file in default application. |
| `` i `` | Ignore or exclude file | |
| `` r `` | Refresh files | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | View stash options | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | Stage all | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Stage lines / Collapse directory | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Discard | View options for discarding changes to the selected file. |
| `` g `` | View upstream reset options | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | Toggle file tree view | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | Fetch | Fetch changes from remote. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Search the current view by text | |
## Input prompt
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirm | |
| `` <esc> `` | Close/Cancel | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the file name to the clipboard
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: Commit changes
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: Amend last commit
<kbd>C</kbd>: Commit changes using git editor
<kbd>e</kbd>: Edit file
<kbd>o</kbd>: Open file
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Refresh files
<kbd>s</kbd>: Stash all changes
<kbd>S</kbd>: View stash options
<kbd>a</kbd>: Stage/unstage all
<kbd>&lt;enter&gt;</kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: View reset options
<kbd>`</kbd>: Toggle file tree view
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: Search the current view by text
</pre>
## Local branches
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy branch name to clipboard | |
| `` i `` | Show git-flow options | |
| `` <space> `` | Checkout | Checkout selected item. |
| `` n `` | New branch | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | Create pull request | |
| `` O `` | View create pull request options | |
| `` <c-y> `` | Copy pull request URL to clipboard | |
| `` c `` | Checkout by name | Checkout by name. In the input box you can enter '-' to switch to the previous branch. |
| `` - `` | Checkout previous branch | |
| `` F `` | Force checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
| `` T `` | New tag | |
| `` s `` | Sort order | |
| `` g `` | Reset | |
| `` R `` | Rename branch | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>n</kbd>: New branch
<kbd>o</kbd>: Create pull request
<kbd>O</kbd>: Create pull request options
<kbd>&lt;c-y&gt;</kbd>: Copy pull request URL to clipboard
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: Delete branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: Merge into currently checked out branch
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: Create tag
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: Rename branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Main panel (merging)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Pick hunk | |
| `` b `` | Pick all hunks | |
| `` <up> `` | Previous hunk | |
| `` <down> `` | Next hunk | |
| `` <left> `` | Previous conflict | |
| `` <right> `` | Next conflict | |
| `` z `` | Undo | Undo last merge conflict resolution. |
| `` e `` | Edit file | Open file in external editor. |
| `` o `` | Open file | Open file in default application. |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | Return to files panel | |
<pre>
<kbd>e</kbd>: Edit file
<kbd>o</kbd>: Open file
<kbd>&lt;left&gt;</kbd>: Select previous conflict
<kbd>&lt;right&gt;</kbd>: Select next conflict
<kbd>&lt;up&gt;</kbd>: Select previous hunk
<kbd>&lt;down&gt;</kbd>: Select next hunk
<kbd>z</kbd>: Undo
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: Return to files panel
</pre>
## Main panel (normal)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Scroll down | |
| `` mouse wheel up (fn+down) `` | Scroll up | |
| `` <tab> `` | Switch view | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Search the current view by text | |
<pre>
<kbd>mouse wheel down</kbd>: Scroll down (fn+up)
<kbd>mouse wheel up</kbd>: Scroll up (fn+down)
</pre>
## Main panel (patch building)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Go to previous hunk | |
| `` <right> `` | Go to next hunk | |
| `` v `` | Toggle range select | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` o `` | Open file | Open file in default application. |
| `` e `` | Edit file | Open file in external editor. |
| `` <space> `` | Toggle lines in patch | |
| `` <esc> `` | Exit custom patch builder | |
| `` / `` | Search the current view by text | |
<pre>
<kbd>&lt;left&gt;</kbd>: Select previous hunk
<kbd>&lt;right&gt;</kbd>: Select next hunk
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;space&gt;</kbd>: Add/Remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
<kbd>/</kbd>: Search the current view by text
</pre>
## Main panel (staging)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Go to previous hunk | |
| `` <right> `` | Go to next hunk | |
| `` v `` | Toggle range select | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` <space> `` | Stage | Toggle selection staged / unstaged. |
| `` d `` | Discard | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | Open file | Open file in default application. |
| `` e `` | Edit file | Open file in external editor. |
| `` <esc> `` | Return to files panel | |
| `` <tab> `` | Switch view | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | Commit | Commit staged changes. |
| `` w `` | Commit changes without pre-commit hook | |
| `` C `` | Commit changes using git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Search the current view by text | |
<pre>
<kbd>&lt;left&gt;</kbd>: Select previous hunk
<kbd>&lt;right&gt;</kbd>: Select next hunk
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;esc&gt;</kbd>: Return to files panel
<kbd>&lt;tab&gt;</kbd>: Switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: Toggle line staged / unstaged
<kbd>d</kbd>: Discard change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Commit changes
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>C</kbd>: Commit changes using git editor
<kbd>/</kbd>: Search the current view by text
</pre>
## Menu
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Execute | |
| `` <esc> `` | Close/Cancel | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Execute
<kbd>&lt;esc&gt;</kbd>: Close
<kbd>/</kbd>: Filter the current view by text
</pre>
## Reflog
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Copy (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: Copy commit (cherry-pick)
<kbd>C</kbd>: Copy commit range (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remote branches
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy branch name to clipboard | |
| `` <space> `` | Checkout | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | New branch | |
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | Sort order | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>n</kbd>: New branch
<kbd>M</kbd>: Merge into currently checked out branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>d</kbd>: Delete branch
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remotes
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | New remote | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Edit the selected remote's name or URL. |
| `` f `` | Fetch | Fetch updates from the remote repository. This retrieves new commits and branches without merging them into your local branches. |
| `` / `` | Filter the current view by text | |
## Secondary
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | Switch view | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Search the current view by text | |
<pre>
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Add new remote
<kbd>d</kbd>: Remove remote
<kbd>e</kbd>: Edit remote
<kbd>/</kbd>: Filter the current view by text
</pre>
## Stash
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Apply | Apply the stash entry to your working directory. |
| `` g `` | Pop | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Drop | Remove the stash entry from the stash list. |
| `` n `` | New branch | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Rename stash | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: Apply
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: New branch
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Filter the current view by text
</pre>
## Status
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Open config file | Open file in default application. |
| `` e `` | Edit config file | Open file in external editor. |
| `` u `` | Check for update | |
| `` <enter> `` | Switch to a recent repo | |
| `` a `` | Show/cycle all branch logs | |
| `` 0 `` | Focus main view | |
<pre>
<kbd>o</kbd>: Open config file
<kbd>e</kbd>: Edit config file
<kbd>u</kbd>: Check for update
<kbd>&lt;enter&gt;</kbd>: Switch to a recent repo
<kbd>a</kbd>: Show all branch logs
</pre>
## Sub-commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Copy (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: Copy commit (cherry-pick)
<kbd>C</kbd>: Copy commit range (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Search the current view by text
</pre>
## Submodules
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy submodule name to clipboard | |
| `` <enter> `` | Enter | Enter submodule. After entering the submodule, you can press `<esc>` to escape back to the parent repo. |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | Update selected submodule. |
| `` n `` | New submodule | |
| `` e `` | Update submodule URL | |
| `` i `` | Initialize | Initialize the selected submodule to prepare for fetching. You probably want to follow this up by invoking the 'update' action to fetch the submodule. |
| `` b `` | View bulk submodule options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Add new submodule
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialize submodule
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
## Tags
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy tag to clipboard | |
| `` <space> `` | Checkout | Checkout the selected tag as a detached HEAD. |
| `` n `` | New tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Worktrees
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>d</kbd>: Delete tag
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Create tag
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
+282 -362
View File
@@ -1,413 +1,333 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit キーバインディング
# Lazygit キーバインド
_凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味します_
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## グローバルキーバインド
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 最近のリポジトリをチェックアウト | |
| `` <pgup> (fn+up/shift+k) `` | メインウィンドウを上にスクロール | |
| `` <pgdown> (fn+down/shift+j) `` | メインウィンドウを下にスクロール | |
| `` @ `` | コマンドログオプションを表示 | コマンドログのオプションを表示します(例:コマンドログの表示/非表示、コマンドログへのフォーカスなど)。 |
| `` P `` | プッシュ | 現在のブランチを対応するアップストリームブランチにプッシュします。アップストリームが設定されていない場合、アップストリームブランチの設定を求められます。 |
| `` p `` | プル | 現在のブランチのリモートから変更をプルします。アップストリームが設定されていない場合、アップストリームブランチの設定を求められます。 |
| `` ) `` | リネーム検出の類似度しきい値を上げる | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | リネーム検出の類似度しきい値を下げる | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | 差分コンテキストサイズを増やす | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | 差分コンテキストサイズを減らす | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | シェルコマンドを実行 | 実行するシェルコマンドを入力するプロンプトを表示します。 |
| `` <c-p> `` | カスタムパッチオプションを表示 | |
| `` m `` | マージ/リベースオプションを表示 | 現在のマージ/リベースを中止/継続/スキップするオプションを表示します。 |
| `` R `` | 更新 | Gitの状態を更新します(`git status`、`git branch`などをバックグラウンドで実行してパネルの内容を更新します)。これは`git fetch`を実行しません。 |
| `` + `` | 次の画面モード(通常/半分/全画面) | |
| `` _ `` | 前の画面モード | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | キャンセル | |
| `` ? `` | キーバインディングメニューを開く | |
| `` <c-s> `` | フィルターオプションを表示 | コミットログのフィルタリングオプションを表示し、フィルタに一致するコミットのみを表示します。 |
| `` W `` | 差分オプションを表示 | 2つのrefの差分に関連するオプションを表示します(例:選択したrefとの差分表示、差分を取るrefの入力、差分方向の反転など)。 |
| `` <c-e> `` | 差分オプションを表示 | 2つのrefの差分に関連するオプションを表示します(例:選択したrefとの差分表示、差分を取るrefの入力、差分方向の反転など)。 |
| `` q `` | 終了 | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | 空白表示の切り替え | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | 元に戻す | 最後のgitコマンドを元に戻すために実行するgitコマンドを決定するためにreflogが使用されます。これにはワーキングツリーへの変更は含まれません。コミットのみが考慮されます。 |
| `` Z `` | やり直す | 最後のgitコマンドをやり直すために実行するgitコマンドを決定するためにreflogが使用されます。これにはワーキングツリーへの変更は含まれません。コミットのみが考慮されます。 |
<pre>
<kbd>&lt;c-r&gt;</kbd>: 最近使用したリポジトリに切り替え
<kbd>&lt;pgup&gt;</kbd>: メインパネルを上にスクロール (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: メインパネルを下にスクロール (fn+down/shift+j)
<kbd>@</kbd>: コマンドログメニューを開く
<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>: カスタムコマンドを実行
<kbd>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: 差分メニューを開く
<kbd>&lt;c-e&gt;</kbd>: 差分メニューを開く
<kbd>&lt;c-w&gt;</kbd>: 空白文字の差分の表示有無を切り替え
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
<kbd>&lt;c-z&gt;</kbd>: リドゥ (via reflog) (experimental)
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## リストパネルのナビゲーション
## 一覧パネルの操作
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 前のページ | |
| `` . `` | 次のページ | |
| `` < (<home>) `` | 先頭にスクロール | |
| `` > (<end>) `` | 末尾にスクロール | |
| `` v `` | 範囲選択を切り替え | |
| `` <s-down> `` | 範囲選択を下に | |
| `` <s-up> `` | 範囲選択を上に | |
| `` / `` | 現在のビューをテキストで検索 | |
| `` H `` | 左にスクロール | |
| `` L `` | 右にスクロール | |
| `` ] `` | 次のタブ | |
| `` [ `` | 前のタブ | |
<pre>
<kbd>,</kbd>: 前のページ
<kbd>.</kbd>: 次のページ
<kbd>&lt;</kbd>: 最上部までスクロール
<kbd>&gt;</kbd>: 最下部までスクロール
<kbd>/</kbd>: 検索を開始
<kbd>H</kbd>: 左スクロール
<kbd>L</kbd>: 右スクロール
<kbd>]</kbd>: 次のタブ
<kbd>[</kbd>: 前のタブ
</pre>
## Input prompt
## Stash
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 閉じる/キャンセル | |
<pre>
<kbd>&lt;space&gt;</kbd>: 適用
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: 新しいブランチを作成
<kbd>r</kbd>: Stashを変更
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Filter the current view by text
</pre>
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットのSHAをクリップボードにコピー
<kbd>&lt;space&gt;</kbd>: コミットをチェックアウト
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 検索を開始
</pre>
## コミット
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
| `` <c-r> `` | コピーされた(チェリーピックされた)コミットの選択をリセット | |
| `` b `` | bisectオプションを表示 | |
| `` s `` | スカッシュ | 選択したコミットをその下のコミットにスカッシュします。スカッシュとは複数のコミットを1つにまとめる操作です。選択したコミットのメッセージが下のコミットに追加されます。 |
| `` f `` | フィックスアップ | 選択したコミットをその下のコミットにマージします。フィックスアップはスカッシュと似ていますが、選択したコミットのメッセージは破棄され、下のコミットのメッセージのみが保持されます。 |
| `` r `` | メッセージ変更 | 選択したコミットのメッセージを変更します。 |
| `` R `` | エディタでメッセージ変更 | |
| `` d `` | 削除 | 選択したコミットを削除します。これはリベースを通じてブランチからコミットを削除します。コミットが後続のコミットが依存する変更を行っている場合、マージコンフリクトを解決する必要があるかもしれません。 |
| `` e `` | 編集(対話型リベースを開始) | 選択したコミットを編集します。これを使用して、選択したコミットから対話型リベースを開始します。すでにリベース中の場合、これは選択したコミットを編集用にマークし、リベースを続行すると、リベースは選択したコミットで一時停止して変更を行えるようにします。 |
| `` i `` | 対話的リベースを開始 | ブランチ上のコミットの対話的リベースを開始します。これには、HEADコミットから最初のマージコミットまたはメインブランチのコミットまでのすべてのコミットが含まれます。<br>選択したコミットから対話的リベースを開始したい場合は、代わりに `e` を押してください。 |
| `` p `` | ピック | 選択したコミットをピックするようにマークします(リベース中)。これは、リベースを続行すると、コミットが保持されることを意味します。 |
| `` F `` | fixupコミットを作成 | 選択したコミットに対する「fixup!」コミットを作成します。fixupコミットは、選択したコミットの修正用コミットです。後で、同じコミットで `S` を押すと、上記のすべてのfixupコミットが適用されます。 |
| `` S `` | fixupコミットを適用 | すべての「fixup!」コミットを、選択したコミットの上部または現在のブランチ内のすべてをスカッシュします(autosquash)。 |
| `` <c-j> `` | コミットを1つ下に移動 | |
| `` <c-k> `` | コミットを1つ上に移動 | |
| `` V `` | ペースト(チェリーピック) | |
| `` B `` | リベース用のベースコミットとしてマーク | 次のリベース用のベースコミットを選択します。ブランチにリベースするとき、ベースコミットより上のコミットのみが持ち込まれます。これは `git rebase --onto` コマンドを使用します。 |
| `` A `` | 修正 | ステージされた変更でコミットを修正します。選択したコミットがHEADコミットの場合、これは `git commit --amend` を実行します。それ以外の場合、コミットはリベースを通じて修正されます。 |
| `` a `` | コミット属性を修正 | コミット作者の設定/リセットまたは共同作者の設定を行います。 |
| `` t `` | リバート | 選択したコミットの変更を逆に適用する、リバートコミットを作成します。 |
| `` T `` | コミットにタグを付ける | 選択したコミットを指すタグを新規作成します。タグ名とオプションの説明を入力するよう促されます。 |
| `` <c-l> `` | ログオプションを表示 | コミットログのオプションを表示します(例:並び順の変更、Gitグラフの非表示、Gitグラフ全体の表示)。 |
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したコミットをデタッチドヘッド(特定のブランチに属さない状態)としてチェックアウトします。 |
| `` y `` | コミット属性をクリップボードにコピー | コミット属性をクリップボードにコピーします(例:ハッシュ、URL、差分、メッセージ、作者)。 |
| `` o `` | ブラウザでコミットを開く | |
| `` n `` | コミットから新しいブランチを作成 | |
| `` N `` | コミットを新しいブランチに移動 | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | リセット | 選択した項目へのリセットオプション(ソフト/ミックス/ハード)を表示します。各リセットタイプの詳細は次の通りです:<br>- ソフトリセット:変更を保持し、ステージされた状態にします<br>- ミックスリセット:変更を保持し、ステージされていない状態にします<br>- ハードリセット:すべての変更を破棄します |
| `` C `` | コピー(チェリーピック) | コミットをコピーとしてマークします。ローカルコミットビューで `V` を押すと、コピーしたコミットをチェックアウトしたブランチにペースト(チェリーピック)できます。いつでも `<esc>` を押して選択をキャンセルできます。 |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` * `` | 現在のブランチのコミットを選択 | |
| `` 0 `` | メインビューにフォーカス | |
| `` <enter> `` | ファイルを表示 | |
| `` w `` | ワークツリーオプションを表示 | |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットのSHAをクリップボードにコピー
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: コミットメッセージを変更
<kbd>R</kbd>: エディタでコミットメッセージを編集
<kbd>d</kbd>: コミットを削除
<kbd>e</kbd>: コミットを編集
<kbd>p</kbd>: Pick commit (when mid-rebase)
<kbd>F</kbd>: このコミットに対するfixupコミットを作成
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
<kbd>&lt;c-j&gt;</kbd>: コミットを1つ下に移動
<kbd>&lt;c-k&gt;</kbd>: コミットを1つ上に移動
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
<kbd>A</kbd>: ステージされた変更でamendコミット
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: コミットをrevert
<kbd>T</kbd>: タグを作成
<kbd>&lt;c-l&gt;</kbd>: ログメニューを開く
<kbd>&lt;space&gt;</kbd>: コミットをチェックアウト
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 検索を開始
</pre>
## コミットファイル
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | パスをクリップボードにコピー | |
| `` y `` | クリップボードにコピー | |
| `` c `` | チェックアウト(ブランチの切り替え) | ファイルをチェックアウトします。これにより、作業ツリー内のファイルが選択したコミットのバージョンに置き換えられます。 |
| `` d `` | 削除 | このコミットのこのファイルへの変更を破棄します。これはバックグラウンドで対話的なリベースを実行するため、後のコミットでもこのファイルが変更されている場合、マージコンフリクトが発生する可能性があります。 |
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
| `` e `` | 編集 | 外部エディタでファイルを開きます。 |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` <space> `` | パッチに含めるファイルを切り替え | ファイルがカスタムパッチに含まれるかどうかを切り替えます。https://github.com/jesseduffield/lazygit#rebase-magic-custom-patchesを参照してください。 |
| `` a `` | すべてのファイルを切り替え | コミットのすべてのファイルをカスタムパッチに追加/削除します。https://github.com/jesseduffield/lazygit#rebase-magic-custom-patchesを参照してください。 |
| `` <enter> `` | ファイルに入る / ディレクトリの折りたたみを切り替える | ファイルが選択されている場合、そのファイルに入ってカスタムパッチに個々の行を追加/削除できます。ディレクトリが選択されている場合、ディレクトリを切り替えます。 |
| `` ` `` | ファイルツリービューを切り替え | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます |
| `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します |
| `` 0 `` | メインビューにフォーカス | |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットされたファイル名をクリップボードにコピー
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: ファイルツリーの表示を切り替え
<kbd>/</kbd>: 検索を開始
</pre>
## コミット概要
## コミットメッセージ
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 閉じる | |
## サブコミット
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したコミットをデタッチドヘッド(特定のブランチに属さない状態)としてチェックアウトします。 |
| `` y `` | コミット属性をクリップボードにコピー | コミット属性をクリップボードにコピーします(例:ハッシュ、URL、差分、メッセージ、作者)。 |
| `` o `` | ブラウザでコミットを開く | |
| `` n `` | コミットから新しいブランチを作成 | |
| `` N `` | コミットを新しいブランチに移動 | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | リセット | 選択した項目へのリセットオプション(ソフト/ミックス/ハード)を表示します。各リセットタイプの詳細は次の通りです:<br>- ソフトリセット:変更を保持し、ステージされた状態にします<br>- ミックスリセット:変更を保持し、ステージされていない状態にします<br>- ハードリセット:すべての変更を破棄します |
| `` C `` | コピー(チェリーピック) | コミットをコピーとしてマークします。ローカルコミットビューで `V` を押すと、コピーしたコミットをチェックアウトしたブランチにペースト(チェリーピック)できます。いつでも `<esc>` を押して選択をキャンセルできます。 |
| `` <c-r> `` | コピーされた(チェリーピックされた)コミットの選択をリセット | |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` * `` | 現在のブランチのコミットを選択 | |
| `` 0 `` | メインビューにフォーカス | |
| `` <enter> `` | ファイルを表示 | |
| `` w `` | ワークツリーオプションを表示 | |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 閉じる
</pre>
## サブモジュール
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | サブモジュール名をクリップボードにコピー | |
| `` <enter> `` | 入る | サブモジュールに入ります。サブモジュールに入った後、`<esc>`を押して親リポジトリに戻ることができます。 |
| `` d `` | 削除 | 選択したサブモジュールとそれに対応するディレクトリを削除します。 |
| `` u `` | 更新 | 選択したサブモジュールを更新します。 |
| `` n `` | 新しいサブモジュール | |
| `` e `` | サブモジュールURLを更新 | |
| `` i `` | 初期化 | 選択したサブモジュールを初期化してフェッチの準備をします。おそらく、続いて「更新」アクションを呼び出してサブモジュールをフェッチしたいでしょう。 |
| `` b `` | 一括サブモジュールオプションを表示 | |
| `` / `` | 現在のビューをテキストでフィルタリング | |
## スタッシュ
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 適用 | スタッシュエントリをワーキングディレクトリに適用します。 |
| `` g `` | ポップ | スタッシュエントリをワーキングディレクトリに適用し、スタッシュエントリを削除します。 |
| `` d `` | 削除 | スタッシュリストからスタッシュエントリを削除します。 |
| `` n `` | 新しいブランチ | 選択したスタッシュエントリから新しいブランチを作成します。これは、スタッシュエントリが作成されたコミットをgitがチェックアウトし、そのコミットから新しいブランチを作成した後、スタッシュエントリを追加のコミットとして新しいブランチに適用することで機能します。 |
| `` r `` | スタッシュの名前を変更 | |
| `` 0 `` | メインビューにフォーカス | |
| `` <enter> `` | ファイルを表示 | |
| `` w `` | ワークツリーオプションを表示 | |
| `` / `` | 現在のビューをテキストでフィルタリング | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: サブモジュール名をクリップボードにコピー
<kbd>&lt;enter&gt;</kbd>: サブモジュールを開く
<kbd>d</kbd>: サブモジュールを削除
<kbd>u</kbd>: サブモジュールを更新
<kbd>n</kbd>: サブモジュールを新規追加
<kbd>e</kbd>: サブモジュールのURLを更新
<kbd>i</kbd>: サブモジュールを初期化
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
## ステータス
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 設定ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
| `` e `` | 設定ファイルを編集 | 外部エディタでファイルを開きます。 |
| `` u `` | 更新を確認 | |
| `` <enter> `` | 最近のリポジトリをチェックアウト | |
| `` a `` | ブランチログの表示モードを順に切り替え | |
| `` 0 `` | メインビューにフォーカス | |
## セカンダリ
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | ビューを切り替え | 他のビュー(ステージされた変更/ステージされていない変更)に切り替えます。 |
| `` <esc> `` | サイドパネルに戻る | |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>o</kbd>: 設定ファイルを開く
<kbd>e</kbd>: 設定ファイルを編集
<kbd>u</kbd>: 更新を確認
<kbd>&lt;enter&gt;</kbd>: 最近使用したリポジトリに切り替え
<kbd>a</kbd>: すべてのブランチログを表示
</pre>
## タグ
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | タグをクリップボードにコピー | |
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したタグをデタッチドHEADとしてチェックアウトします。 |
| `` n `` | 新しいタグを作成 | 現在のコミットから新しいタグを作成します。タグ名とオプションの説明を入力するよう促されます。 |
| `` d `` | 削除 | ローカル/リモートタグの削除オプションを表示します。 |
| `` P `` | タグをプッシュ | 選択したタグをリモートにプッシュします。リモートを選択するよう促されます。 |
| `` g `` | リセット | 選択した項目へのリセットオプション(ソフト/ミックス/ハード)を表示します。各リセットタイプの詳細は次の通りです:<br>- ソフトリセット:変更を保持し、ステージされた状態にします<br>- ミックスリセット:変更を保持し、ステージされていない状態にします<br>- ハードリセット:すべての変更を破棄します |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` 0 `` | メインビューにフォーカス | |
| `` <enter> `` | コミットを表示 | |
| `` w `` | ワークツリーオプションを表示 | |
| `` / `` | 現在のビューをテキストでフィルタリング | |
<pre>
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>d</kbd>: タグを削除
<kbd>P</kbd>: タグをpush
<kbd>n</kbd>: タグを作成
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
## ファイル
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | パスをクリップボードにコピー | |
| `` <space> `` | ステージ | 選択したファイルのステージ状態を切り替えます。 |
| `` <c-b> `` | ステータスでファイルをフィルタリング | |
| `` y `` | クリップボードにコピー | |
| `` c `` | コミット | ステージされた変更をコミットします。 |
| `` w `` | pre-commitフックなしで変更をコミット | |
| `` A `` | 直前のコミットを修正 | |
| `` C `` | Gitエディタを使用して変更をコミット | |
| `` <c-f> `` | フィックスアップのベースコミットを検索 | 現在の変更が基づいているコミットを見つけて、コミットの修正/フィックスアップを行います。これにより、ブランチのコミットを一つずつ確認して、どのコミットを修正/フィックスアップすべきかを調べる手間が省けます。詳細はドキュメントを参照: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | 編集 | 外部エディタでファイルを開きます。 |
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
| `` i `` | ファイルを無視または除外 | |
| `` r `` | ファイルを更新 | |
| `` s `` | スタッシュ | すべての変更をスタッシュします。スタッシュの他のバリエーションについては、スタッシュオプションを表示するキーバインディングを使用してください。 |
| `` S `` | スタッシュオプションを表示 | スタッシュオプション(すべてをスタッシュ、ステージされた変更をスタッシュ、ステージされていない変更をスタッシュなど)を表示します。 |
| `` a `` | すべてステージ | ワーキングツリー内のすべてのファイルのステージ/アンステージを切り替えます。 |
| `` <enter> `` | 行をステージ / ディレクトリを折りたたむ | 選択された項目がファイルの場合、個々のハンク/行をステージできるようにステージングビューにフォーカスします。選択された項目がディレクトリの場合、ディレクトリを折りたたむ/展開します。 |
| `` d `` | 破棄 | 選択したファイルの変更を破棄するオプションを表示します。 |
| `` g `` | アップストリームへのリセットオプションを表示 | |
| `` D `` | リセット | 作業ツリーのリセットオプション(例:作業ツリーの完全破棄)を表示します。 |
| `` ` `` | ファイルツリービューを切り替え | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | フェッチ | リモートから変更をフェッチします。 |
| `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます |
| `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します |
| `` 0 `` | メインビューにフォーカス | |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: ファイル名をクリップボードにコピー
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: ステージ/アンステージ
<kbd>&lt;c-b&gt;</kbd>: ファイルをフィルタ (ステージ/アンステージ)
<kbd>c</kbd>: 変更をコミット
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
<kbd>A</kbd>: 最新のコミットにamend
<kbd>C</kbd>: gitエディタを使用して変更をコミット
<kbd>e</kbd>: ファイルを編集
<kbd>o</kbd>: ファイルを開く
<kbd>i</kbd>: ファイルをignore
<kbd>r</kbd>: ファイルをリフレッシュ
<kbd>s</kbd>: 変更をstash
<kbd>S</kbd>: View stash options
<kbd>a</kbd>: すべての変更をステージ/アンステージ
<kbd>&lt;enter&gt;</kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: View reset options
<kbd>`</kbd>: ファイルツリーの表示を切り替え
<kbd>M</kbd>: Git mergetoolを開く
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: 検索を開始
</pre>
## メインパネル(ステージング)
## ブランチ
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 前のハンクに移動 | |
| `` <right> `` | 次のハンクに移動 | |
| `` v `` | 範囲選択を切り替え | |
| `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 選択したテキストをクリップボードにコピー | |
| `` <space> `` | ステージ | 選択された部分のステージ / アンステージを切り替えます。 |
| `` d `` | 破棄 | ステージされていない変更が選択されている場合、`git reset`を使用して変更を破棄します。ステージされた変更が選択されている場合、変更をアンステージします。 |
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
| `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 |
| `` <esc> `` | ファイルパネルに戻る | |
| `` <tab> `` | ビューを切り替え | 他のビュー(ステージされた変更/ステージされていない変更)に切り替えます。 |
| `` E `` | ハンクを編集 | 選択したハンクを外部エディタで編集します。 |
| `` c `` | コミット | ステージされた変更をコミットします。 |
| `` w `` | pre-commitフックなしで変更をコミット | |
| `` C `` | Gitエディタを使用して変更をコミット | |
| `` <c-f> `` | フィックスアップのベースコミットを検索 | 現在の変更が基づいているコミットを見つけて、コミットの修正/フィックスアップを行います。これにより、ブランチのコミットを一つずつ確認して、どのコミットを修正/フィックスアップすべきかを調べる手間が省けます。詳細はドキュメントを参照: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: ブランチ名をクリップボードにコピー
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>n</kbd>: 新しいブランチを作成
<kbd>o</kbd>: Pull Requestを作成
<kbd>O</kbd>: Create pull request options
<kbd>&lt;c-y&gt;</kbd>: Pull RequestのURLをクリップボードにコピー
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: ブランチを削除
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: タグを作成
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: ブランチ名を変更
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
## メインパネル(パッチ作成)
## メインパネル (Merging)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 前のハンクに移動 | |
| `` <right> `` | 次のハンクに移動 | |
| `` v `` | 範囲選択を切り替え | |
| `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 選択したテキストをクリップボードにコピー | |
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
| `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 |
| `` <space> `` | パッチ内の行を切り替え | |
| `` <esc> `` | カスタムパッチビルダーを終了 | |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>e</kbd>: ファイルを編集
<kbd>o</kbd>: ファイルを開く
<kbd>&lt;left&gt;</kbd>: 前のコンフリクトを選択
<kbd>&lt;right&gt;</kbd>: 次のコンフリクトを選択
<kbd>&lt;up&gt;</kbd>: 前のhunkを選択
<kbd>&lt;down&gt;</kbd>: 次のhunkを選択
<kbd>z</kbd>: アンドゥ
<kbd>M</kbd>: Git mergetoolを開く
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: ファイル一覧に戻る
</pre>
## メインパネル(マージ中)
## メインパネル (Normal)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | ハンクを選択 | |
| `` b `` | すべてのハンクを選択 | |
| `` <up> `` | 前のハンク | |
| `` <down> `` | 次のハンク | |
| `` <left> `` | 前のコンフリクト | |
| `` <right> `` | 次のコンフリクト | |
| `` z `` | 元に戻す | 最後のマージコンフリクト解決を元に戻します。 |
| `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 |
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | ファイルパネルに戻る | |
<pre>
<kbd>mouse wheel down</kbd>: 下にスクロール (fn+up)
<kbd>mouse wheel up</kbd>: 上にスクロール (fn+down)
</pre>
## メインパネル(通常)
## メインパネル (Patch Building)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 下にスクロール | |
| `` mouse wheel up (fn+down) `` | 上にスクロール | |
| `` <tab> `` | ビューを切り替え | 他のビュー(ステージされた変更/ステージされていない変更)に切り替えます。 |
| `` <esc> `` | サイドパネルに戻る | |
| `` / `` | 現在のビューをテキストで検索 | |
<pre>
<kbd>&lt;left&gt;</kbd>: 前のhunkを選択
<kbd>&lt;right&gt;</kbd>: 次のhunkを選択
<kbd>v</kbd>: 範囲選択を切り替え
<kbd>V</kbd>: 範囲選択を切り替え
<kbd>a</kbd>: Hunk選択を切り替え
<kbd>&lt;c-o&gt;</kbd>: 選択されたテキストをクリップボードにコピー
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;space&gt;</kbd>: 行をパッチに追加/削除
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
<kbd>/</kbd>: 検索を開始
</pre>
## メインパネル (Staging)
<pre>
<kbd>&lt;left&gt;</kbd>: 前のhunkを選択
<kbd>&lt;right&gt;</kbd>: 次のhunkを選択
<kbd>v</kbd>: 範囲選択を切り替え
<kbd>V</kbd>: 範囲選択を切り替え
<kbd>a</kbd>: Hunk選択を切り替え
<kbd>&lt;c-o&gt;</kbd>: 選択されたテキストをクリップボードにコピー
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;esc&gt;</kbd>: ファイル一覧に戻る
<kbd>&lt;tab&gt;</kbd>: パネルを切り替え
<kbd>&lt;space&gt;</kbd>: 選択行をステージ/アンステージ
<kbd>d</kbd>: 変更を削除 (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 変更をコミット
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
<kbd>C</kbd>: gitエディタを使用して変更をコミット
<kbd>/</kbd>: 検索を開始
</pre>
## メニュー
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 実行 | |
| `` <esc> `` | 閉じる/キャンセル | |
| `` / `` | 現在のビューをテキストでフィルタリング | |
## リフログ
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したコミットをデタッチドヘッド(特定のブランチに属さない状態)としてチェックアウトします。 |
| `` y `` | コミット属性をクリップボードにコピー | コミット属性をクリップボードにコピーします(例:ハッシュ、URL、差分、メッセージ、作者)。 |
| `` o `` | ブラウザでコミットを開く | |
| `` n `` | コミットから新しいブランチを作成 | |
| `` N `` | コミットを新しいブランチに移動 | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | リセット | 選択した項目へのリセットオプション(ソフト/ミックス/ハード)を表示します。各リセットタイプの詳細は次の通りです:<br>- ソフトリセット:変更を保持し、ステージされた状態にします<br>- ミックスリセット:変更を保持し、ステージされていない状態にします<br>- ハードリセット:すべての変更を破棄します |
| `` C `` | コピー(チェリーピック) | コミットをコピーとしてマークします。ローカルコミットビューで `V` を押すと、コピーしたコミットをチェックアウトしたブランチにペースト(チェリーピック)できます。いつでも `<esc>` を押して選択をキャンセルできます。 |
| `` <c-r> `` | コピーされた(チェリーピックされた)コミットの選択をリセット | |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` * `` | 現在のブランチのコミットを選択 | |
| `` 0 `` | メインビューにフォーカス | |
| `` <enter> `` | コミットを表示 | |
| `` w `` | ワークツリーオプションを表示 | |
| `` / `` | 現在のビューをテキストでフィルタリング | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 実行
<kbd>&lt;esc&gt;</kbd>: 閉じる
<kbd>/</kbd>: Filter the current view by text
</pre>
## リモート
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | ブランチを表示 | |
| `` n `` | 新しいリモート | |
| `` d `` | 削除 | 選択したリモートを削除します。そのリモートからのリモートブランチを追跡しているローカルブランチは影響を受けません。 |
| `` e `` | 編集 | 選択したリモートの名前またはURLを編集します。 |
| `` f `` | フェッチ | リモートリポジトリから更新をフェッチします。これにより、ローカルブランチにマージせずに新しいコミットとブランチを取得します。 |
| `` / `` | 現在のビューをテキストでフィルタリング | |
<pre>
<kbd>f</kbd>: リモートをfetch
<kbd>n</kbd>: リモートを新規追加
<kbd>d</kbd>: リモートを削除
<kbd>e</kbd>: リモートを編集
<kbd>/</kbd>: Filter the current view by text
</pre>
## リモートブランチ
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | ブランチ名をクリップボードにコピー | |
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したリモートブランチに基づいて新しいローカルブランチをチェックアウトするか、リモートブランチをデタッチドヘッドとしてチェックアウトします。 |
| `` n `` | 新しいブランチ | |
| `` M `` | マージ | 選択した項目を現在のブランチにマージするためのオプションを表示します(通常のマージ、スカッシュマージ) |
| `` r `` | リベース | チェックアウトしたブランチを選択したブランチ上にリベースします。 |
| `` d `` | 削除 | リモートからリモートブランチを削除します。 |
| `` u `` | アップストリームとして設定 | 選択したリモートブランチをチェックアウトされたブランチのアップストリームとして設定します。 |
| `` s `` | 並び順 | |
| `` g `` | リセット | 選択した項目へのリセットオプション(ソフト/ミックス/ハード)を表示します。各リセットタイプの詳細は次の通りです:<br>- ソフトリセット:変更を保持し、ステージされた状態にします<br>- ミックスリセット:変更を保持し、ステージされていない状態にします<br>- ハードリセット:すべての変更を破棄します |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` 0 `` | メインビューにフォーカス | |
| `` <enter> `` | コミットを表示 | |
| `` w `` | ワークツリーオプションを表示 | |
| `` / `` | 現在のビューをテキストでフィルタリング | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: ブランチ名をクリップボードにコピー
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>n</kbd>: 新しいブランチを作成
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>d</kbd>: ブランチを削除
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
## ローカルブランチ
## 参照ログ
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | ブランチ名をクリップボードにコピー | |
| `` i `` | git-flowオプションを表示 | |
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択した項目をチェックアウトします。 |
| `` n `` | 新しいブランチ | |
| `` N `` | コミットを新しいブランチに移動 | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | プルリクエストを作成 | |
| `` O `` | プルリクエスト作成オプションを表示 | |
| `` <c-y> `` | プルリクエストURLをクリップボードにコピー | |
| `` c `` | 名前でチェックアウト | 名前でチェックアウトします。入力ボックスに「-」を入力すると、最後のブランチをチェックアウトすることができます。 |
| `` - `` | 直前のブランチにチェックアウト | |
| `` F `` | 強制チェックアウト | 選択したブランチを強制的にチェックアウトします。これにより、選択したブランチをチェックアウトする前にワーキングディレクトリ内のすべてのローカル変更が破棄されます。 |
| `` d `` | 削除 | ローカル/リモートブランチの削除オプションを表示します。 |
| `` r `` | リベース | チェックアウトしたブランチを選択したブランチ上にリベースします。 |
| `` M `` | マージ | 選択した項目を現在のブランチにマージするためのオプションを表示します(通常のマージ、スカッシュマージ) |
| `` f `` | ブランチを最新化(fast-forward) | 選択したブランチを対応するアップストリームの最新状態に追いつかせます(fast-forward)。 |
| `` T `` | 新しいタグを作成 | |
| `` s `` | 並び順 | |
| `` g `` | リセット | |
| `` R `` | ブランチ名を変更 | |
| `` u `` | アップストリームオプションを表示 | ブランチのアップストリームに関連するオプションを表示します(例:アップストリームの設定/解除やアップストリームへのリセット)。 |
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
| `` 0 `` | メインビューにフォーカス | |
| `` <enter> `` | コミットを表示 | |
| `` w `` | ワークツリーオプションを表示 | |
| `` / `` | 現在のビューをテキストでフィルタリング | |
## ワークツリー
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | 新しいワークツリー | |
| `` <space> `` | チェックアウト(切り替え) | 選択したワークツリーをチェックアウト(切り替え)します。 |
| `` o `` | エディタで開く | |
| `` d `` | 削除 | 選択したワークツリーを削除します。これはワークツリーのディレクトリとワークツリーに関するメタデータの両方を.gitディレクトリから削除します。 |
| `` / `` | 現在のビューをテキストでフィルタリング | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットのSHAをクリップボードにコピー
<kbd>&lt;space&gt;</kbd>: コミットをチェックアウト
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
## 確認パネル
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 閉じる/キャンセル | |
| `` <c-o> `` | クリップボードにコピー | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 閉じる/キャンセル
</pre>
+266 -346
View File
@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit 키 바인딩
@@ -6,408 +6,328 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## 글로벌 키 바인딩
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 최근에 사용한 저장소로 전환 | |
| `` <pgup> (fn+up/shift+k) `` | 메인 패널을 위로 스크롤 | |
| `` <pgdown> (fn+down/shift+j) `` | 메인 패널을 아래로로 스크롤 | |
| `` @ `` | 명령어 로그 메뉴 열기 | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | 푸시 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | 업데이트 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기 | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기 | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | 커스텀 Patch 옵션 보기 | |
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | 새로고침 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | 다음 스크린 모드 (normal/half/fullscreen) | |
| `` _ `` | 이전 스크린 모드 | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | 취소 | |
| `` ? `` | 매뉴 열기 | |
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | 종료 | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | 공백문자를 Diff 뷰에서 표시 여부 전환 | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | 되돌리기 (reflog) (실험적) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
| `` Z `` | 다시 실행 (reflog) (실험적) | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
<pre>
<kbd>&lt;c-r&gt;</kbd>: 최근에 사용한 저장소로 전환
<kbd>&lt;pgup&gt;</kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
<kbd>@</kbd>: 명령어 로그 메뉴 열기
<kbd>}</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
<kbd>{</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
<kbd>:</kbd>: Execute custom command
<kbd>&lt;c-p&gt;</kbd>: 커스텀 Patch 옵션 보기
<kbd>m</kbd>: View merge/rebase options
<kbd>R</kbd>: 새로고침
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
<kbd>_</kbd>: 이전 스크린 모드
<kbd>?</kbd>: 매뉴 열기
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Diff 메뉴 열기
<kbd>&lt;c-e&gt;</kbd>: Diff 메뉴 열기
<kbd>&lt;c-w&gt;</kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
<kbd>z</kbd>: 되돌리기 (reflog) (실험적)
<kbd>&lt;c-z&gt;</kbd>: 다시 실행 (reflog) (실험적)
<kbd>P</kbd>: 푸시
<kbd>p</kbd>: 업데이트
</pre>
## List panel navigation
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 이전 페이지 | |
| `` . `` | 다음 페이지 | |
| `` < (<home>) `` | 맨 위로 스크롤 | |
| `` > (<end>) `` | 맨 아래로 스크롤 | |
| `` v `` | 드래그 선택 전환 | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | 검색 시작 | |
| `` H `` | 우 스크롤 | |
| `` L `` | 좌 스크롤 | |
| `` ] `` | 이전 탭 | |
| `` [ `` | 다음 탭 | |
## Input prompt
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 확인 | |
| `` <esc> `` | 닫기/취소 | |
<pre>
<kbd>,</kbd>: 이전 페이지
<kbd>.</kbd>: 다음 페이지
<kbd>&lt;</kbd>: 맨 위로 스크롤
<kbd>&gt;</kbd>: 맨 아래로 스크롤
<kbd>/</kbd>: 검색 시작
<kbd>H</kbd>: 우 스크롤
<kbd>L</kbd>: 좌 스크롤
<kbd>]</kbd>: 이전 탭
<kbd>[</kbd>: 다음 탭
</pre>
## Reflog
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
| `` n `` | 커밋에서 새 브랜치를 만듭니다. | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 커밋을 복사 (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Secondary
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | 패널 전환 | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | 검색 시작 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋 SHA를 클립보드에 복사
<kbd>&lt;space&gt;</kbd>: 커밋을 체크아웃
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
## Stash
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 적용 | Apply the stash entry to your working directory. |
| `` g `` | Pop | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Drop | Remove the stash entry from the stash list. |
| `` n `` | 새 브랜치 생성 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Rename stash | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View selected item's files | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: 적용
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: 새 브랜치 생성
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Filter the current view by text
</pre>
## Sub-commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
| `` n `` | 커밋에서 새 브랜치를 만듭니다. | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 커밋을 복사 (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View selected item's files | |
| `` w `` | View worktree options | |
| `` / `` | 검색 시작 | |
## Worktrees
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋 SHA를 클립보드에 복사
<kbd>&lt;space&gt;</kbd>: 커밋을 체크아웃
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 검색 시작
</pre>
## 메뉴
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 실행 | |
| `` <esc> `` | 닫기/취소 | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 실행
<kbd>&lt;esc&gt;</kbd>: 닫기
<kbd>/</kbd>: Filter the current view by text
</pre>
## 메인 패널 (Merging)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Pick hunk | |
| `` b `` | Pick all hunks | |
| `` <up> `` | 이전 hunk를 선택 | |
| `` <down> `` | 다음 hunk를 선택 | |
| `` <left> `` | 이전 충돌을 선택 | |
| `` <right> `` | 다음 충돌을 선택 | |
| `` z `` | 되돌리기 | Undo last merge conflict resolution. |
| `` e `` | 파일 편집 | Open file in external editor. |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | 파일 목록으로 돌아가기 | |
<pre>
<kbd>e</kbd>: 파일 편집
<kbd>o</kbd>: 파일 닫기
<kbd>&lt;left&gt;</kbd>: 이전 충돌을 선택
<kbd>&lt;right&gt;</kbd>: 다음 충돌을 선택
<kbd>&lt;up&gt;</kbd>: 이전 hunk를 선택
<kbd>&lt;down&gt;</kbd>: 다음 hunk를 선택
<kbd>z</kbd>: 되돌리기
<kbd>M</kbd>: Git mergetool를 열기
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: 파일 목록으로 돌아가기
</pre>
## 메인 패널 (Normal)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 아래로 스크롤 | |
| `` mouse wheel up (fn+down) `` | 위로 스크롤 | |
| `` <tab> `` | 패널 전환 | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | 검색 시작 | |
<pre>
<kbd>mouse wheel down</kbd>: 아래로 스크롤 (fn+up)
<kbd>mouse wheel up</kbd>: 위로 스크롤 (fn+down)
</pre>
## 메인 패널 (Patch Building)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 이전 hunk를 선택 | |
| `` <right> `` | 다음 hunk를 선택 | |
| `` v `` | 드래그 선택 전환 | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 선택한 텍스트를 클립보드에 복사 | |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` e `` | 파일 편집 | Open file in external editor. |
| `` <space> `` | Line(s)을 패치에 추가/삭제 | |
| `` <esc> `` | Exit custom patch builder | |
| `` / `` | 검색 시작 | |
<pre>
<kbd>&lt;left&gt;</kbd>: 이전 hunk를 선택
<kbd>&lt;right&gt;</kbd>: 다음 hunk를 선택
<kbd>v</kbd>: 드래그 선택 전환
<kbd>V</kbd>: 드래그 선택 전환
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: 선택한 텍스트를 클립보드에 복사
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;space&gt;</kbd>: Line(s)을 패치에 추가/삭제
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
<kbd>/</kbd>: 검색 시작
</pre>
## 메인 패널 (Staging)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 이전 hunk를 선택 | |
| `` <right> `` | 다음 hunk를 선택 | |
| `` v `` | 드래그 선택 전환 | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 선택한 텍스트를 클립보드에 복사 | |
| `` <space> `` | Staged 전환 | 선택한 행을 staged / unstaged |
| `` d `` | 변경을 삭제 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` e `` | 파일 편집 | Open file in external editor. |
| `` <esc> `` | 파일 목록으로 돌아가기 | |
| `` <tab> `` | 패널 전환 | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | 커밋 변경내용 | 스테이징된 변경 사항 커밋. |
| `` w `` | Commit changes without pre-commit hook | |
| `` C `` | Git 편집기를 사용하여 변경 내용을 커밋합니다. | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 검색 시작 | |
<pre>
<kbd>&lt;left&gt;</kbd>: 이전 hunk를 선택
<kbd>&lt;right&gt;</kbd>: 다음 hunk를 선택
<kbd>v</kbd>: 드래그 선택 전환
<kbd>V</kbd>: 드래그 선택 전환
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: 선택한 텍스트를 클립보드에 복사
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;esc&gt;</kbd>: 파일 목록으로 돌아가기
<kbd>&lt;tab&gt;</kbd>: 패널 전환
<kbd>&lt;space&gt;</kbd>: 선택한 행을 staged / unstaged
<kbd>d</kbd>: 변경을 삭제 (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 커밋 변경내용
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
<kbd>/</kbd>: 검색 시작
</pre>
## 브랜치
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 브랜치명을 클립보드에 복사 | |
| `` i `` | Git-flow 옵션 보기 | |
| `` <space> `` | 체크아웃 | Checkout selected item. |
| `` n `` | 새 브랜치 생성 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | 풀 리퀘스트 생성 | |
| `` O `` | 풀 리퀘스트 생성 옵션 | |
| `` <c-y> `` | 풀 리퀘스트 URL을 클립보드에 복사 | |
| `` c `` | 이름으로 체크아웃 | Checkout by name. In the input box you can enter '-' to switch to the previous branch. |
| `` - `` | Checkout previous branch | |
| `` F `` | 강제 체크아웃 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | 삭제 | View delete options for local/remote branch. |
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Fast-forward this branch from its upstream | Fast-forward selected branch from its upstream. |
| `` T `` | 태그를 생성 | |
| `` s `` | Sort order | |
| `` g `` | View reset options | |
| `` R `` | 브랜치 이름 변경 | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 브랜치명을 클립보드에 복사
<kbd>i</kbd>: Git-flow 옵션 보기
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>n</kbd>: 새 브랜치 생성
<kbd>o</kbd>: 풀 리퀘스트 생성
<kbd>O</kbd>: 풀 리퀘스트 생성 옵션
<kbd>&lt;c-y&gt;</kbd>: 풀 리퀘스트 URL을 클립보드에 복사
<kbd>c</kbd>: 이름으로 체크아웃
<kbd>F</kbd>: 강제 체크아웃
<kbd>d</kbd>: 브랜치 삭제
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: 태그를 생성
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: 브랜치 이름 변경
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
## 상태
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 설정 파일 열기 | Open file in default application. |
| `` e `` | 설정 파일 수정 | Open file in external editor. |
| `` u `` | 업데이트 확인 | |
| `` <enter> `` | 최근에 사용한 저장소로 전환 | |
| `` a `` | Show/cycle all branch logs | |
| `` 0 `` | Focus main view | |
<pre>
<kbd>o</kbd>: 설정 파일 열기
<kbd>e</kbd>: 설정 파일 수정
<kbd>u</kbd>: 업데이트 확인
<kbd>&lt;enter&gt;</kbd>: 최근에 사용한 저장소로 전환
<kbd>a</kbd>: 모든 브랜치 로그 표시
</pre>
## 서브모듈
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 서브모듈 이름을 클립보드에 복사 | |
| `` <enter> `` | Enter | 서브모듈 열기 |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | 서브모듈 업데이트 |
| `` n `` | 새로운 서브모듈 추가 | |
| `` e `` | 서브모듈의 URL을 수정 | |
| `` i `` | Initialize | 서브모듈 초기화 |
| `` b `` | View bulk submodule options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 서브모듈 이름을 클립보드에 복사
<kbd>&lt;enter&gt;</kbd>: 서브모듈 열기
<kbd>d</kbd>: 서브모듈 삭제
<kbd>u</kbd>: 서브모듈 업데이트
<kbd>n</kbd>: 새로운 서브모듈 추가
<kbd>e</kbd>: 서브모듈의 URL을 수정
<kbd>i</kbd>: 서브모듈 초기화
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
## 원격
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | 새로운 Remote 추가 | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Remote를 수정 |
| `` f `` | Fetch | 원격을 업데이트 |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>f</kbd>: 원격을 업데이트
<kbd>n</kbd>: 새로운 Remote 추가
<kbd>d</kbd>: Remote를 삭제
<kbd>e</kbd>: Remote를 수정
<kbd>/</kbd>: Filter the current view by text
</pre>
## 원격 브랜치
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 브랜치명을 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | 새 브랜치 생성 | |
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
| `` d `` | 삭제 | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | Sort order | |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 브랜치명을 클립보드에 복사
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>n</kbd>: 새 브랜치 생성
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>d</kbd>: 브랜치 삭제
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
## 커밋
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
| `` b `` | Bisect 옵션 보기 | |
| `` s `` | 스쿼시 | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | 커밋메시지 변경 | Reword the selected commit's message. |
| `` R `` | 에디터에서 커밋메시지 수정 | |
| `` d `` | 커밋 삭제 | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | 커밋을 편집 |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.<br>If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Pick commit (when mid-rebase) |
| `` F `` | Create fixup commit | Create fixup commit for this commit |
| `` S `` | Apply fixup commits | Squash all 'fixup!' commits above selected commit (autosquash) |
| `` <c-j> `` | 커밋을 1개 아래로 이동 | |
| `` <c-k> `` | 커밋을 1개 위로 이동 | |
| `` V `` | 커밋을 붙여넣기 (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Amend commit with staged changes |
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | 로그 메뉴 열기 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
| `` n `` | 커밋에서 새 브랜치를 만듭니다. | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 커밋을 복사 (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View selected item's files | |
| `` w `` | View worktree options | |
| `` / `` | 검색 시작 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋 SHA를 클립보드에 복사
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: Bisect 옵션 보기
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: 커밋메시지 변경
<kbd>R</kbd>: 에디터에서 커밋메시지 수정
<kbd>d</kbd>: 커밋 삭제
<kbd>e</kbd>: 커밋을 편집
<kbd>p</kbd>: Pick commit (when mid-rebase)
<kbd>F</kbd>: Create fixup commit for this commit
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
<kbd>&lt;c-j&gt;</kbd>: 커밋을 1개 아래로 이동
<kbd>&lt;c-k&gt;</kbd>: 커밋을 1개 위로 이동
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
<kbd>A</kbd>: Amend commit with staged changes
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: 커밋 되돌리기
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: 로그 메뉴 열기
<kbd>&lt;space&gt;</kbd>: 커밋을 체크아웃
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 검색 시작
</pre>
## 커밋 파일
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 파일명을 클립보드에 복사 | |
| `` y `` | 클립보드에 복사 | |
| `` c `` | 체크아웃 | Checkout file |
| `` d `` | Remove | Discard this commit's changes to this file |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Toggle file included in patch | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Toggle all files included in patch | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Enter file to add selected lines to the patch (or toggle directory collapsed) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | 파일 트리뷰로 전환 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | 검색 시작 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋한 파일명을 클립보드에 복사
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: 파일 트리뷰로 전환
<kbd>/</kbd>: 검색 시작
</pre>
## 커밋메시지
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 확인 | |
| `` <esc> `` | 닫기 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 확인
<kbd>&lt;esc&gt;</kbd>: 닫기
</pre>
## 태그
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy tag to clipboard | |
| `` <space> `` | 체크아웃 | Checkout the selected tag as a detached HEAD. |
| `` n `` | 태그를 생성 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | 삭제 | View delete options for local/remote tag. |
| `` P `` | 태그를 push | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | 초기화 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>d</kbd>: 태그 삭제
<kbd>P</kbd>: 태그를 push
<kbd>n</kbd>: 태그를 생성
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
## 파일
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 파일명을 클립보드에 복사 | |
| `` <space> `` | Staged 전환 | Toggle staged for selected file. |
| `` <c-b> `` | 파일을 필터하기 (Staged/unstaged) | |
| `` y `` | 클립보드에 복사 | |
| `` c `` | 커밋 변경내용 | 스테이징된 변경 사항 커밋. |
| `` w `` | Commit changes without pre-commit hook | |
| `` A `` | 마지맛 커밋 수정 | |
| `` C `` | Git 편집기를 사용하여 변경 내용을 커밋합니다. | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` i `` | Ignore file | |
| `` r `` | 파일 새로고침 | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | Stash 옵션 보기 | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | 모든 변경을 Staged/unstaged으로 전환 | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Stage individual hunks/lines for file, or collapse/expand for directory | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | View 'discard changes' options | View options for discarding changes to the selected file. |
| `` g `` | View upstream reset options | |
| `` D `` | 초기화 | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | 파일 트리뷰로 전환 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | Fetch | Fetch changes from remote. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | 검색 시작 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 파일명을 클립보드에 복사
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Staged 전환
<kbd>&lt;c-b&gt;</kbd>: 파일을 필터하기 (Staged/unstaged)
<kbd>c</kbd>: 커밋 변경내용
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: 마지맛 커밋 수정
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
<kbd>e</kbd>: 파일 편집
<kbd>o</kbd>: 파일 닫기
<kbd>i</kbd>: Ignore file
<kbd>r</kbd>: 파일 새로고침
<kbd>s</kbd>: 변경사항을 Stash
<kbd>S</kbd>: Stash 옵션 보기
<kbd>a</kbd>: 모든 변경을 Staged/unstaged으로 전환
<kbd>&lt;enter&gt;</kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: View reset options
<kbd>`</kbd>: 파일 트리뷰로 전환
<kbd>M</kbd>: Git mergetool를 열기
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: 검색 시작
</pre>
## 확인 패널
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 확인 | |
| `` <esc> `` | 닫기/취소 | |
| `` <c-o> `` | 클립보드에 복사 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 확인
<kbd>&lt;esc&gt;</kbd>: 닫기/취소
</pre>
+266 -346
View File
@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit Sneltoetsen
@@ -6,408 +6,328 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Globale sneltoetsen
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Wissel naar een recente repo | |
| `` <pgup> (fn+up/shift+k) `` | Scroll naar beneden vanaf hoofdpaneel | |
| `` <pgdown> (fn+down/shift+j) `` | Scroll naar beneden vanaf hoofdpaneel | |
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | Bekijk aangepaste patch opties | |
| `` m `` | Bekijk merge/rebase opties | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | Verversen | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Volgende scherm modus (normaal/half/groot) | |
| `` _ `` | Vorige scherm modus | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | Annuleren | |
| `` ? `` | Open menu | |
| `` <c-s> `` | Bekijk scoping opties | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | Quit | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | Ongedaan maken (via reflog) (experimenteel) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
| `` Z `` | Redo (via reflog) (experimenteel) | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
<pre>
<kbd>&lt;c-r&gt;</kbd>: Wissel naar een recente repo
<kbd>&lt;pgup&gt;</kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<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>: Voer aangepaste commando uit
<kbd>&lt;c-p&gt;</kbd>: Bekijk aangepaste patch opties
<kbd>m</kbd>: Bekijk merge/rebase opties
<kbd>R</kbd>: Verversen
<kbd>+</kbd>: Volgende scherm modus (normaal/half/groot)
<kbd>_</kbd>: Vorige scherm modus
<kbd>?</kbd>: Open menu
<kbd>&lt;c-s&gt;</kbd>: Bekijk scoping opties
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: Ongedaan maken (via reflog) (experimenteel)
<kbd>&lt;c-z&gt;</kbd>: Redo (via reflog) (experimenteel)
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## Lijstpaneel navigatie
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Vorige pagina | |
| `` . `` | Volgende pagina | |
| `` < (<home>) `` | Scroll naar boven | |
| `` > (<end>) `` | Scroll naar beneden | |
| `` v `` | Toggle drag selecteer | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | Start met zoeken | |
| `` H `` | Scroll left | |
| `` L `` | Scroll right | |
| `` ] `` | Volgende tabblad | |
| `` [ `` | Vorige tabblad | |
<pre>
<kbd>,</kbd>: Vorige pagina
<kbd>.</kbd>: Volgende pagina
<kbd>&lt;</kbd>: Scroll naar boven
<kbd>&gt;</kbd>: Scroll naar beneden
<kbd>/</kbd>: Start met zoeken
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Volgende tabblad
<kbd>[</kbd>: Vorige tabblad
</pre>
## Bestanden
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer de bestandsnaam naar het klembord | |
| `` <space> `` | Toggle staged | Toggle staged for selected file. |
| `` <c-b> `` | Filter files by status | |
| `` y `` | Copy to clipboard | |
| `` c `` | Commit veranderingen | Commit staged changes. |
| `` w `` | Commit veranderingen zonder pre-commit hook | |
| `` A `` | Wijzig laatste commit | |
| `` C `` | Commit veranderingen met de git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | Open bestand | Open file in default application. |
| `` i `` | Ignore or exclude file | |
| `` r `` | Refresh bestanden | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | Bekijk stash opties | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | Toggle staged alle | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Stage individuele hunks/lijnen | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Bekijk 'veranderingen ongedaan maken' opties | View options for discarding changes to the selected file. |
| `` g `` | Bekijk upstream reset opties | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | Toggle bestandsboom weergave | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | Fetch | Fetch changes from remote. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Start met zoeken | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer de bestandsnaam naar het klembord
<kbd>d</kbd>: Bekijk 'veranderingen ongedaan maken' opties
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
<kbd>A</kbd>: Wijzig laatste commit
<kbd>C</kbd>: Commit veranderingen met de git editor
<kbd>e</kbd>: Verander bestand
<kbd>o</kbd>: Open bestand
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Refresh bestanden
<kbd>s</kbd>: Stash-bestanden
<kbd>S</kbd>: Bekijk stash opties
<kbd>a</kbd>: Toggle staged alle
<kbd>&lt;enter&gt;</kbd>: Stage individuele hunks/lijnen
<kbd>g</kbd>: Bekijk upstream reset opties
<kbd>D</kbd>: Bekijk reset opties
<kbd>`</kbd>: Toggle bestandsboom weergave
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: Start met zoeken
</pre>
## Bevestigingspaneel
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Bevestig | |
| `` <esc> `` | Sluiten | |
| `` <c-o> `` | Copy to clipboard | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Bevestig
<kbd>&lt;esc&gt;</kbd>: Sluiten
</pre>
## Branches
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer branch name naar klembord | |
| `` i `` | Laat git-flow opties zien | |
| `` <space> `` | Uitchecken | Checkout selected item. |
| `` n `` | Nieuwe branch | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | Maak een pull-request | |
| `` O `` | Bekijk opties voor pull-aanvraag | |
| `` <c-y> `` | Kopieer de URL van het pull-verzoek naar het klembord | |
| `` c `` | Uitchecken bij naam | Checkout by name. In the input box you can enter '-' to switch to the previous branch. |
| `` - `` | Checkout previous branch | |
| `` F `` | Forceer checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Merge in met huidige checked out branch | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Fast-forward deze branch vanaf zijn upstream | Fast-forward selected branch from its upstream. |
| `` T `` | Creëer tag | |
| `` s `` | Sort order | |
| `` g `` | Bekijk reset opties | |
| `` R `` | Hernoem branch | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer branch name naar klembord
<kbd>i</kbd>: Laat git-flow opties zien
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>n</kbd>: Nieuwe branch
<kbd>o</kbd>: Maak een pull-request
<kbd>O</kbd>: Bekijk opties voor pull-aanvraag
<kbd>&lt;c-y&gt;</kbd>: Kopieer de URL van het pull-verzoek naar het klembord
<kbd>c</kbd>: Uitchecken bij naam
<kbd>F</kbd>: Forceer checkout
<kbd>d</kbd>: Verwijder branch
<kbd>r</kbd>: Rebase branch
<kbd>M</kbd>: Merge in met huidige checked out branch
<kbd>f</kbd>: Fast-forward deze branch vanaf zijn upstream
<kbd>T</kbd>: Creëer tag
<kbd>g</kbd>: Bekijk reset opties
<kbd>R</kbd>: Hernoem branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Commit bericht
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Bevestig | |
| `` <esc> `` | Sluiten | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Bevestig
<kbd>&lt;esc&gt;</kbd>: Sluiten
</pre>
## Commit bestanden
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer de bestandsnaam naar het klembord | |
| `` y `` | Copy to clipboard | |
| `` c `` | Uitchecken | Bestand uitchecken |
| `` d `` | Remove | Uitsluit deze commit zijn veranderingen aan dit bestand |
| `` o `` | Open bestand | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Toggle bestand inbegrepen in patch | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Toggle all files | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Enter bestand om geselecteerde regels toe te voegen aan de patch | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | Toggle bestandsboom weergave | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Start met zoeken | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer de vastgelegde bestandsnaam naar het klembord
<kbd>c</kbd>: Bestand uitchecken
<kbd>d</kbd>: Uitsluit deze commit zijn veranderingen aan dit bestand
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;space&gt;</kbd>: Toggle bestand inbegrepen in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter bestand om geselecteerde regels toe te voegen aan de patch
<kbd>`</kbd>: Toggle bestandsboom weergave
<kbd>/</kbd>: Start met zoeken
</pre>
## Commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
| `` b `` | View bisect options | |
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | Hernoem commit | Reword the selected commit's message. |
| `` R `` | Hernoem commit met editor | |
| `` d `` | Verwijder commit | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | Wijzig commit |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.<br>If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Kies commit (wanneer midden in rebase) |
| `` F `` | Creëer fixup commit | Creëer fixup commit |
| `` S `` | Apply fixup commits | Squash bovenstaande commits |
| `` <c-j> `` | Verplaats commit 1 naar beneden | |
| `` <c-k> `` | Verplaats commit 1 naar boven | |
| `` V `` | Plak commits (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Wijzig commit met staged veranderingen |
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | View log options | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Creëer nieuwe branch van commit | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopieer commit (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Bekijk gecommite bestanden | |
| `` w `` | View worktree options | |
| `` / `` | Start met zoeken | |
## Input prompt
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Bevestig | |
| `` <esc> `` | Sluiten | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash beneden
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: Hernoem commit
<kbd>R</kbd>: Hernoem commit met editor
<kbd>d</kbd>: Verwijder commit
<kbd>e</kbd>: Wijzig commit
<kbd>p</kbd>: Kies commit (wanneer midden in rebase)
<kbd>F</kbd>: Creëer fixup commit
<kbd>S</kbd>: Squash bovenstaande commits
<kbd>&lt;c-j&gt;</kbd>: Verplaats commit 1 naar beneden
<kbd>&lt;c-k&gt;</kbd>: Verplaats commit 1 naar boven
<kbd>v</kbd>: Plak commits (cherry-pick)
<kbd>A</kbd>: Wijzig commit met staged veranderingen
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Commit ongedaan maken
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Creëer nieuwe branch van commit
<kbd>g</kbd>: Bekijk reset opties
<kbd>c</kbd>: Kopieer commit (cherry-pick)
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
<kbd>/</kbd>: Start met zoeken
</pre>
## Menu
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Uitvoeren | |
| `` <esc> `` | Sluiten | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Uitvoeren
<kbd>&lt;esc&gt;</kbd>: Sluiten
<kbd>/</kbd>: Filter the current view by text
</pre>
## Mergen
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Kies stuk | |
| `` b `` | Kies beide stukken | |
| `` <up> `` | Selecteer bovenste hunk | |
| `` <down> `` | Selecteer onderste hunk | |
| `` <left> `` | Selecteer voorgaand conflict | |
| `` <right> `` | Selecteer volgende conflict | |
| `` z `` | Ongedaan maken | Undo last merge conflict resolution. |
| `` e `` | Verander bestand | Open file in external editor. |
| `` o `` | Open bestand | Open file in default application. |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | Ga terug naar het bestanden paneel | |
<pre>
<kbd>e</kbd>: Verander bestand
<kbd>o</kbd>: Open bestand
<kbd>&lt;left&gt;</kbd>: Selecteer voorgaand conflict
<kbd>&lt;right&gt;</kbd>: Selecteer volgende conflict
<kbd>&lt;up&gt;</kbd>: Selecteer bovenste hunk
<kbd>&lt;down&gt;</kbd>: Selecteer onderste hunk
<kbd>z</kbd>: Ongedaan maken
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Kies stuk
<kbd>b</kbd>: Kies beide stukken
<kbd>&lt;esc&gt;</kbd>: Ga terug naar het bestanden paneel
</pre>
## Normaal
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Scroll omlaag | |
| `` mouse wheel up (fn+down) `` | Scroll omhoog | |
| `` <tab> `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Start met zoeken | |
<pre>
<kbd>mouse wheel down</kbd>: Scroll omlaag (fn+up)
<kbd>mouse wheel up</kbd>: Scroll omhoog (fn+down)
</pre>
## Patch bouwen
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Selecteer de vorige hunk | |
| `` <right> `` | Selecteer de volgende hunk | |
| `` v `` | Toggle drag selecteer | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` o `` | Open bestand | Open file in default application. |
| `` e `` | Verander bestand | Open file in external editor. |
| `` <space> `` | Voeg toe/verwijder lijn(en) in patch | |
| `` <esc> `` | Sluit lijn-bij-lijn modus | |
| `` / `` | Start met zoeken | |
<pre>
<kbd>&lt;left&gt;</kbd>: Selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: Selecteer de volgende hunk
<kbd>v</kbd>: Toggle drag selecteer
<kbd>V</kbd>: Toggle drag selecteer
<kbd>a</kbd>: Toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;space&gt;</kbd>: Voeg toe/verwijder lijn(en) in patch
<kbd>&lt;esc&gt;</kbd>: Sluit lijn-bij-lijn modus
<kbd>/</kbd>: Start met zoeken
</pre>
## Reflog
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Creëer nieuwe branch van commit | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopieer commit (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Creëer nieuwe branch van commit
<kbd>g</kbd>: Bekijk reset opties
<kbd>c</kbd>: Kopieer commit (cherry-pick)
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remote branches
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer branch name naar klembord | |
| `` <space> `` | Uitchecken | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | Nieuwe branch | |
| `` M `` | Merge in met huidige checked out branch | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Stel in als upstream van uitgecheckte branch |
| `` s `` | Sort order | |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer branch name naar klembord
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>n</kbd>: Nieuwe branch
<kbd>M</kbd>: Merge in met huidige checked out branch
<kbd>r</kbd>: Rebase branch
<kbd>d</kbd>: Verwijder branch
<kbd>u</kbd>: Stel in als upstream van uitgecheckte branch
<kbd>g</kbd>: Bekijk reset opties
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remotes
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | Voeg een nieuwe remote toe | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Wijzig remote |
| `` f `` | Fetch | Fetch remote |
| `` / `` | Filter the current view by text | |
## Secondary
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Start met zoeken | |
<pre>
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Voeg een nieuwe remote toe
<kbd>d</kbd>: Verwijder remote
<kbd>e</kbd>: Wijzig remote
<kbd>/</kbd>: Filter the current view by text
</pre>
## Staging
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Selecteer de vorige hunk | |
| `` <right> `` | Selecteer de volgende hunk | |
| `` v `` | Toggle drag selecteer | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` <space> `` | Toggle staged | Toggle lijnen staged / unstaged |
| `` d `` | Verwijdert change (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | Open bestand | Open file in default application. |
| `` e `` | Verander bestand | Open file in external editor. |
| `` <esc> `` | Ga terug naar het bestanden paneel | |
| `` <tab> `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | Commit veranderingen | Commit staged changes. |
| `` w `` | Commit veranderingen zonder pre-commit hook | |
| `` C `` | Commit veranderingen met de git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Start met zoeken | |
<pre>
<kbd>&lt;left&gt;</kbd>: Selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: Selecteer de volgende hunk
<kbd>v</kbd>: Toggle drag selecteer
<kbd>V</kbd>: Toggle drag selecteer
<kbd>a</kbd>: Toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;esc&gt;</kbd>: Ga terug naar het bestanden paneel
<kbd>&lt;tab&gt;</kbd>: Ga naar een ander paneel
<kbd>&lt;space&gt;</kbd>: Toggle lijnen staged / unstaged
<kbd>d</kbd>: Verwijdert change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
<kbd>C</kbd>: Commit veranderingen met de git editor
<kbd>/</kbd>: Start met zoeken
</pre>
## Stash
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Toepassen | Apply the stash entry to your working directory. |
| `` g `` | Pop | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Laten vallen | Remove the stash entry from the stash list. |
| `` n `` | Nieuwe branch | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Rename stash | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Bekijk gecommite bestanden | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: Toepassen
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Laten vallen
<kbd>n</kbd>: Nieuwe branch
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
<kbd>/</kbd>: Filter the current view by text
</pre>
## Status
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Open config bestand | Open file in default application. |
| `` e `` | Verander config bestand | Open file in external editor. |
| `` u `` | Check voor updates | |
| `` <enter> `` | Wissel naar een recente repo | |
| `` a `` | Show/cycle all branch logs | |
| `` 0 `` | Focus main view | |
<pre>
<kbd>o</kbd>: Open config bestand
<kbd>e</kbd>: Verander config bestand
<kbd>u</kbd>: Check voor updates
<kbd>&lt;enter&gt;</kbd>: Wissel naar een recente repo
<kbd>a</kbd>: Alle logs van de branch laten zien
</pre>
## Sub-commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Creëer nieuwe branch van commit | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopieer commit (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Bekijk gecommite bestanden | |
| `` w `` | View worktree options | |
| `` / `` | Start met zoeken | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Creëer nieuwe branch van commit
<kbd>g</kbd>: Bekijk reset opties
<kbd>c</kbd>: Kopieer commit (cherry-pick)
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
<kbd>/</kbd>: Start met zoeken
</pre>
## Submodules
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer submodule naam naar klembord | |
| `` <enter> `` | Enter | Enter submodule |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | Update selected submodule. |
| `` n `` | Voeg nieuwe submodule toe | |
| `` e `` | Update submodule URL | |
| `` i `` | Initialize | Initialiseer submodule |
| `` b `` | Bekijk bulk submodule opties | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer submodule naam naar klembord
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Voeg nieuwe submodule toe
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialiseer submodule
<kbd>b</kbd>: Bekijk bulk submodule opties
<kbd>/</kbd>: Filter the current view by text
</pre>
## Tags
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy tag to clipboard | |
| `` <space> `` | Uitchecken | Checkout the selected tag as a detached HEAD. |
| `` n `` | Creëer tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Worktrees
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>d</kbd>: Verwijder tag
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Creëer tag
<kbd>g</kbd>: Bekijk reset opties
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
+290 -370
View File
@@ -1,413 +1,333 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit Skróty klawiszowe
# Lazygit Keybindings
_Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Globalne skróty klawiszowe
## Globalne
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Przełącz na ostatnie repozytorium | |
| `` <pgup> (fn+up/shift+k) `` | Przewiń główne okno w górę | |
| `` <pgdown> (fn+down/shift+j) `` | Przewiń główne okno w dół | |
| `` @ `` | Pokaż opcje dziennika poleceń | Pokaż opcje dla dziennika poleceń, np. pokazywanie/ukrywanie dziennika poleceń i skupienie na dzienniku poleceń. |
| `` P `` | Wypchnij | Wypchnij bieżącą gałąź do jej gałęzi nadrzędnej. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. |
| `` p `` | Pociągnij | Pociągnij zmiany z zdalnego dla bieżącej gałęzi. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | Zwiększ rozmiar kontekstu w widoku różnic | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | Zmniejsz rozmiar kontekstu w widoku różnic | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | Wyświetl opcje niestandardowej łatki | |
| `` m `` | Pokaż opcje scalania/rebase | Pokaż opcje do przerwania/kontynuowania/pominięcia bieżącego scalania/rebase. |
| `` R `` | Odśwież | Odśwież stan git (tj. uruchom `git status`, `git branch`, itp. w tle, aby zaktualizować zawartość paneli). To nie uruchamia `git fetch`. |
| `` + `` | Następny tryb ekranu (normalny/półpełny/pełnoekranowy) | |
| `` _ `` | Poprzedni tryb ekranu | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | Anuluj | |
| `` ? `` | Otwórz menu przypisań klawiszy | |
| `` <c-s> `` | Pokaż opcje filtrowania | Pokaż opcje filtrowania dziennika commitów, tak aby pokazywane były tylko commity pasujące do filtra. |
| `` W `` | Pokaż opcje różnicowania | Pokaż opcje dotyczące różnicowania dwóch refów, np. różnicowanie względem wybranego refa, wprowadzanie refa do różnicowania i odwracanie kierunku różnic. |
| `` <c-e> `` | Pokaż opcje różnicowania | Pokaż opcje dotyczące różnicowania dwóch refów, np. różnicowanie względem wybranego refa, wprowadzanie refa do różnicowania i odwracanie kierunku różnic. |
| `` q `` | Wyjdź | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | Przełącz białe znaki | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | Cofnij | Dziennik reflog zostanie użyty do określenia, jakie polecenie git należy uruchomić, aby cofnąć ostatnie polecenie git. Nie obejmuje to zmian w drzewie roboczym; brane są pod uwagę tylko commity. |
| `` Z `` | Ponów | Dziennik reflog zostanie użyty do określenia, jakie polecenie git należy uruchomić, aby ponowić ostatnie polecenie git. Nie obejmuje to zmian w drzewie roboczym; brane są pod uwagę tylko commity. |
<pre>
<kbd>&lt;c-r&gt;</kbd>: Switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: Scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<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>: Wykonaj własną komendę
<kbd>&lt;c-p&gt;</kbd>: View custom patch options
<kbd>m</kbd>: Widok scalenia/opcje zmiany bazy
<kbd>R</kbd>: Odśwież
<kbd>+</kbd>: Next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: Prev screen mode
<kbd>?</kbd>: Open menu
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: Undo
<kbd>&lt;c-z&gt;</kbd>: Redo
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## Nawigacja panelu listy
## List panel navigation
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Poprzednia strona | |
| `` . `` | Następna strona | |
| `` < (<home>) `` | Przewiń do góry | |
| `` > (<end>) `` | Przewiń do dołu | |
| `` v `` | Przełącz zaznaczenie zakresu | |
| `` <s-down> `` | Zaznacz zakres w dół | |
| `` <s-up> `` | Zaznacz zakres w górę | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
| `` H `` | Przewiń w lewo | |
| `` L `` | Przewiń w prawo | |
| `` ] `` | Następna zakładka | |
| `` [ `` | Poprzednia zakładka | |
<pre>
<kbd>,</kbd>: Previous page
<kbd>.</kbd>: Next page
<kbd>&lt;</kbd>: Scroll to top
<kbd>&gt;</kbd>: Scroll to bottom
<kbd>/</kbd>: Search the current view by text
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Next tab
<kbd>[</kbd>: Previous tab
</pre>
## Commit summary
<pre>
<kbd>&lt;enter&gt;</kbd>: Potwierdź
<kbd>&lt;esc&gt;</kbd>: Zamknij
</pre>
## Commity
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj hash commita do schowka | |
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
| `` b `` | Zobacz opcje bisect | |
| `` s `` | Scal | Scal wybrany commit z commitami poniżej. Wiadomość wybranego commita zostanie dołączona do commita poniżej. |
| `` f `` | Poprawka | Włącz wybrany commit do commita poniżej. Podobnie do fixup, ale wiadomość wybranego commita zostanie odrzucona. |
| `` r `` | Przeformułuj | Przeformułuj wiadomość wybranego commita. |
| `` R `` | Przeformułuj za pomocą edytora | |
| `` d `` | Usuń | Usuń wybrany commit. To usunie commit z gałęzi za pomocą rebazowania. Jeśli commit wprowadza zmiany, od których zależą późniejsze commity, być może będziesz musiał rozwiązać konflikty scalania. |
| `` e `` | Edytuj (rozpocznij interaktywne rebazowanie) | Edytuj wybrany commit. Użyj tego, aby rozpocząć interaktywne rebazowanie od wybranego commita. Podczas trwania rebazowania, to oznaczy wybrany commit do edycji, co oznacza, że po kontynuacji rebazowania, rebazowanie zostanie wstrzymane na wybranym commicie, aby umożliwić wprowadzenie zmian. |
| `` i `` | Rozpocznij interaktywny rebase | Rozpocznij interaktywny rebase dla commitów na twoim branchu. To będzie zawierać wszystkie commity od HEAD do pierwszego commita scalenia lub commita głównego brancha.<br>Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita, naciśnij `e`. |
| `` p `` | Wybierz | Oznacz wybrany commit do wybrania (podczas rebazowania). Oznacza to, że commit zostanie zachowany po kontynuacji rebazowania. |
| `` F `` | Utwórz commit fixup | Utwórz commit 'fixup!' dla wybranego commita. Później możesz nacisnąć `S` na tym samym commicie, aby zastosować wszystkie powyższe commity fixup. |
| `` S `` | Zastosuj commity fixup | Scal wszystkie commity 'fixup!', albo powyżej wybranego commita, albo wszystkie w bieżącej gałęzi (autosquash). |
| `` <c-j> `` | Przesuń commit w dół | |
| `` <c-k> `` | Przesuń commit w górę | |
| `` V `` | Wklej (cherry-pick) | |
| `` B `` | Oznacz jako bazowy commit dla rebase | Wybierz bazowy commit dla następnego rebase. Kiedy robisz rebase na branch, tylko commity powyżej bazowego commita zostaną przeniesione. Używa to polecenia `git rebase --onto`. |
| `` A `` | Popraw | Popraw commit ze zmianami zatwierdzonymi. Jeśli wybrany commit jest commit HEAD, to wykona `git commit --amend`. W przeciwnym razie commit zostanie poprawiony za pomocą rebazowania. |
| `` a `` | Popraw atrybut commita | Ustaw/Resetuj autora commita lub ustaw współautora. |
| `` t `` | Cofnij | Utwórz commit cofający dla wybranego commita, który stosuje zmiany wybranego commita w odwrotnej kolejności. |
| `` T `` | Otaguj commit | Utwórz nowy tag wskazujący na wybrany commit. Zostaniesz poproszony o wprowadzenie nazwy tagu i opcjonalnego opisu. |
| `` <c-l> `` | Zobacz opcje logów | Zobacz opcje dla logów commitów, np. zmiana kolejności sortowania, ukrywanie grafu gita, pokazywanie całego grafu gita. |
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
| `` o `` | Otwórz commit w przeglądarce | |
| `` n `` | Utwórz nową gałąź z commita | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` C `` | Kopiuj (cherry-pick) | Oznacz commit jako skopiowany. Następnie, w widoku lokalnych commitów, możesz nacisnąć `V`, aby wkleić (cherry-pick) skopiowane commity do sprawdzonej gałęzi. W dowolnym momencie możesz nacisnąć `<esc>`, aby anulować zaznaczenie. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Ściśnij
<kbd>f</kbd>: Napraw commit
<kbd>r</kbd>: Zmień nazwę commita
<kbd>R</kbd>: Zmień nazwę commita w edytorze
<kbd>d</kbd>: Usuń commit
<kbd>e</kbd>: Edytuj commit
<kbd>p</kbd>: Wybierz commit (podczas zmiany bazy)
<kbd>F</kbd>: Utwórz commit naprawczy dla tego commita
<kbd>S</kbd>: Spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash)
<kbd>&lt;c-j&gt;</kbd>: Przenieś commit 1 w dół
<kbd>&lt;c-k&gt;</kbd>: Przenieś commit 1 w górę
<kbd>v</kbd>: Wklej commity (przebieranie)
<kbd>A</kbd>: Popraw commit zmianami z poczekalni
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Odwróć commit
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>c</kbd>: Kopiuj commit (przebieranie)
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
<kbd>/</kbd>: Search the current view by text
</pre>
## Dodatkowy
## Confirmation panel
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Potwierdź
<kbd>&lt;esc&gt;</kbd>: Zamknij
</pre>
## Drzewa pracy
## Local branches
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | Nowe drzewo pracy | |
| `` <space> `` | Przełącz | Przełącz do wybranego drzewa pracy. |
| `` o `` | Otwórz w edytorze | |
| `` d `` | Usuń | Usuń wybrane drzewo pracy. To usunie zarówno katalog drzewa pracy, jak i metadane o drzewie pracy w katalogu .git. |
| `` / `` | Filtruj bieżący widok po tekście | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</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>&lt;c-y&gt;</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>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Główny panel (budowanie łatki)
## Main panel (patch building)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Idź do poprzedniego fragmentu | |
| `` <right> `` | Idź do następnego fragmentu | |
| `` v `` | Przełącz zaznaczenie zakresu | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Kopiuj zaznaczony tekst do schowka | |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. |
| `` <space> `` | Przełącz linie w łatce | |
| `` <esc> `` | Wyjdź z budowniczego niestandardowej łatki | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Input prompt
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij/Anuluj | |
## Lokalne gałęzie
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj nazwę gałęzi do schowka | |
| `` i `` | Pokaż opcje git-flow | |
| `` <space> `` | Przełącz | Przełącz wybrany element. |
| `` n `` | Nowa gałąź | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | Utwórz żądanie ściągnięcia | |
| `` O `` | Zobacz opcje tworzenia pull requesta | |
| `` <c-y> `` | Kopiuj adres URL żądania ściągnięcia do schowka | |
| `` c `` | Przełącz według nazwy | Przełącz według nazwy. W polu wprowadzania możesz wpisać '-' aby przełączyć się na ostatnią gałąź. |
| `` - `` | Checkout previous branch | |
| `` F `` | Wymuś przełączenie | Wymuś przełączenie wybranej gałęzi. To spowoduje odrzucenie wszystkich lokalnych zmian w drzewie roboczym przed przełączeniem na wybraną gałąź. |
| `` d `` | Usuń | Wyświetl opcje usuwania lokalnej/odległej gałęzi. |
| `` r `` | Przebazuj | Przebazuj przełączoną gałąź na wybraną gałąź. |
| `` M `` | Scal | Scal wybraną gałąź z aktualnie sprawdzoną gałęzią. |
| `` f `` | Szybkie przewijanie | Szybkie przewijanie wybranej gałęzi z jej źródła. |
| `` T `` | Nowy tag | |
| `` s `` | Kolejność sortowania | |
| `` g `` | Reset | |
| `` R `` | Zmień nazwę gałęzi | |
| `` u `` | Pokaż opcje upstream | Pokaż opcje dotyczące upstream gałęzi, np. ustawianie/usuwanie upstream i resetowanie do upstream. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
<pre>
<kbd>&lt;left&gt;</kbd>: Poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: Następny kawałek
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;space&gt;</kbd>: Add/Remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: Wyście z trybu "linia po linii"
<kbd>/</kbd>: Search the current view by text
</pre>
## Menu
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Wykonaj | |
| `` <esc> `` | Zamknij/Anuluj | |
| `` / `` | Filtruj bieżący widok po tekście | |
## Panel główny (normalny)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Przewiń w dół | |
| `` mouse wheel up (fn+down) `` | Przewiń w górę | |
| `` <tab> `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Panel główny (scalanie)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Wybierz fragment | |
| `` b `` | Wybierz wszystkie fragmenty | |
| `` <up> `` | Poprzedni fragment | |
| `` <down> `` | Następny fragment | |
| `` <left> `` | Poprzedni konflikt | |
| `` <right> `` | Następny konflikt | |
| `` z `` | Cofnij | Cofnij ostatnie rozwiązanie konfliktu scalania. |
| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | Wróć do panelu plików | |
## Panel główny (zatwierdzanie)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Idź do poprzedniego fragmentu | |
| `` <right> `` | Idź do następnego fragmentu | |
| `` v `` | Przełącz zaznaczenie zakresu | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Kopiuj zaznaczony tekst do schowka | |
| `` <space> `` | Zatwierdź | Przełącz zaznaczenie zatwierdzone/niezatwierdzone. |
| `` d `` | Odrzuć | Gdy zaznaczona jest niezatwierdzona zmiana, odrzuć ją używając `git reset`. Gdy zaznaczona jest zatwierdzona zmiana, cofnij zatwierdzenie. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. |
| `` <esc> `` | Wróć do panelu plików | |
| `` <tab> `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). |
| `` E `` | Edytuj fragment | Edytuj wybrany fragment w zewnętrznym edytorze. |
| `` c `` | Commit | Zatwierdź zmiany zatwierdzone. |
| `` w `` | Zatwierdź zmiany bez hooka pre-commit | |
| `` C `` | Zatwierdź zmiany używając edytora git | |
| `` <c-f> `` | Znajdź bazowy commit do poprawki | Znajdź commit, na którym opierają się Twoje obecne zmiany, w celu poprawienia/zmiany commita. To pozwala Ci uniknąć przeglądania commitów w Twojej gałęzi jeden po drugim, aby zobaczyć, który commit powinien być poprawiony/zmieniony. Zobacz dokumentację: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Panel potwierdzenia
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij/Anuluj | |
| `` <c-o> `` | Kopiuj do schowka | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Wykonaj
<kbd>&lt;esc&gt;</kbd>: Zamknij
<kbd>/</kbd>: Filter the current view by text
</pre>
## Pliki
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj ścieżkę do schowka | |
| `` <space> `` | Zatwierdź | Przełącz zatwierdzenie dla wybranego pliku. |
| `` <c-b> `` | Filtruj pliki według statusu | |
| `` y `` | Kopiuj do schowka | |
| `` c `` | Commit | Zatwierdź zmiany zatwierdzone. |
| `` w `` | Zatwierdź zmiany bez hooka pre-commit | |
| `` A `` | Popraw ostatni commit | |
| `` C `` | Zatwierdź zmiany używając edytora git | |
| `` <c-f> `` | Znajdź bazowy commit do poprawki | Znajdź commit, na którym opierają się Twoje obecne zmiany, w celu poprawienia/zmiany commita. To pozwala Ci uniknąć przeglądania commitów w Twojej gałęzi jeden po drugim, aby zobaczyć, który commit powinien być poprawiony/zmieniony. Zobacz dokumentację: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edytuj | Otwórz plik w zewnętrznym edytorze. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` i `` | Ignoruj lub wyklucz plik | |
| `` r `` | Odśwież pliki | |
| `` s `` | Schowaj | Schowaj wszystkie zmiany. Dla innych wariantów schowania, użyj klawisza wyświetlania opcji schowka. |
| `` S `` | Wyświetl opcje schowka | Wyświetl opcje schowka (np. schowaj wszystko, schowaj zatwierdzone, schowaj niezatwierdzone). |
| `` a `` | Zatwierdź wszystko | Przełącz zatwierdzenie/odznaczenie dla wszystkich plików w drzewie roboczym. |
| `` <enter> `` | Zatwierdź linie / Zwiń katalog | Jeśli wybrany element jest plikiem, skup się na widoku zatwierdzania, aby móc zatwierdzać poszczególne fragmenty/linie. Jeśli wybrany element jest katalogiem, zwiń/rozwiń go. |
| `` d `` | Odrzuć | Wyświetl opcje odrzucania zmian w wybranym pliku. |
| `` g `` | Pokaż opcje resetowania do upstream | |
| `` D `` | Reset | Wyświetl opcje resetu dla drzewa roboczego (np. zniszczenie drzewa roboczego). |
| `` ` `` | Przełącz widok drzewa plików | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | Pobierz | Pobierz zmiany ze zdalnego serwera. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the file name to the clipboard
<kbd>d</kbd>: Pokaż opcje porzucania zmian
<kbd>&lt;space&gt;</kbd>: Przełącz stan poczekalni
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: Zatwierdź zmiany
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
<kbd>A</kbd>: Zmień ostatni commit
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
<kbd>e</kbd>: Edytuj plik
<kbd>o</kbd>: Otwórz plik
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Odśwież pliki
<kbd>s</kbd>: Przechowaj zmiany
<kbd>S</kbd>: Wyświetl opcje schowka
<kbd>a</kbd>: Przełącz stan poczekalni wszystkich
<kbd>&lt;enter&gt;</kbd>: Zatwierdź pojedyncze linie
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: Wyświetl opcje resetu
<kbd>`</kbd>: Toggle file tree view
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Pobierz
<kbd>/</kbd>: Search the current view by text
</pre>
## Pliki commita
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj ścieżkę do schowka | |
| `` y `` | Kopiuj do schowka | |
| `` c `` | Przełącz | Przełącz plik. Zastępuje plik w twoim drzewie roboczym wersją z wybranego commita. |
| `` d `` | Usuń | Odrzuć zmiany w tym pliku z tego commita. Uruchamia interaktywny rebase w tle, więc możesz otrzymać konflikt scalania, jeśli późniejszy commit również zmienia ten plik. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj | Otwórz plik w zewnętrznym edytorze. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <space> `` | Przełącz plik włączony w łatkę | Przełącz, czy plik jest włączony w niestandardową łatkę. Zobacz https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Przełącz wszystkie pliki | Dodaj/usuń wszystkie pliki commita do niestandardowej łatki. Zobacz https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Wejdź do pliku / Przełącz zwiń katalog | Jeśli plik jest wybrany, wejdź do pliku, aby móc dodawać/usuwać poszczególne linie do niestandardowej łatki. Jeśli wybrany jest katalog, przełącz katalog. |
| `` ` `` | Przełącz widok drzewa plików | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the committed file name to the clipboard
<kbd>c</kbd>: Plik wybierania
<kbd>d</kbd>: Porzuć zmiany commita dla tego pliku
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: Toggle file tree view
<kbd>/</kbd>: Search the current view by text
</pre>
## Podsumowanie commita
## Poczekalnia
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij | |
<pre>
<kbd>&lt;left&gt;</kbd>: Poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: Następny kawałek
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;esc&gt;</kbd>: Wróć do panelu plików
<kbd>&lt;tab&gt;</kbd>: Switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: Toggle line staged / unstaged
<kbd>d</kbd>: Discard change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Zatwierdź zmiany
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
<kbd>/</kbd>: Search the current view by text
</pre>
## Reflog
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj hash commita do schowka | |
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
| `` o `` | Otwórz commit w przeglądarce | |
| `` n `` | Utwórz nową gałąź z commita | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` C `` | Kopiuj (cherry-pick) | Oznacz commit jako skopiowany. Następnie, w widoku lokalnych commitów, możesz nacisnąć `V`, aby wkleić (cherry-pick) skopiowane commity do sprawdzonej gałęzi. W dowolnym momencie możesz nacisnąć `<esc>`, aby anulować zaznaczenie. |
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>c</kbd>: Kopiuj commit (przebieranie)
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remote branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>&lt;space&gt;</kbd>: Przełącz
<kbd>n</kbd>: Nowa gałąź
<kbd>M</kbd>: Scal do obecnej gałęzi
<kbd>r</kbd>: Zmiana bazy gałęzi
<kbd>d</kbd>: Usuń gałąź
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remotes
<pre>
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Add new remote
<kbd>d</kbd>: Remove remote
<kbd>e</kbd>: Edit remote
<kbd>/</kbd>: Filter the current view by text
</pre>
## Scalanie
<pre>
<kbd>e</kbd>: Edytuj plik
<kbd>o</kbd>: Otwórz plik
<kbd>&lt;left&gt;</kbd>: Poprzedni konflikt
<kbd>&lt;right&gt;</kbd>: Następny konflikt
<kbd>&lt;up&gt;</kbd>: Wybierz poprzedni kawałek
<kbd>&lt;down&gt;</kbd>: Wybierz następny kawałek
<kbd>z</kbd>: Cofnij
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Wybierz kawałek
<kbd>b</kbd>: Wybierz oba kawałki
<kbd>&lt;esc&gt;</kbd>: Wróć do panelu plików
</pre>
## Schowek
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Zastosuj | Zastosuj wpis schowka do katalogu roboczego. |
| `` g `` | Wyciągnij | Zastosuj wpis schowka do katalogu roboczego i usuń wpis schowka. |
| `` d `` | Usuń | Usuń wpis schowka z listy schowka. |
| `` n `` | Nowa gałąź | Utwórz nową gałąź z wybranego wpisu schowka. Działa poprzez przełączenie git na commit, na którym wpis schowka został utworzony, tworzenie nowej gałęzi z tego commita, a następnie zastosowanie wpisu schowka do nowej gałęzi jako dodatkowego commita. |
| `` r `` | Zmień nazwę schowka | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
<pre>
<kbd>&lt;space&gt;</kbd>: Zastosuj
<kbd>g</kbd>: Wyciągnij
<kbd>d</kbd>: Porzuć
<kbd>n</kbd>: Nowa gałąź
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
<kbd>/</kbd>: Filter the current view by text
</pre>
## Status
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Otwórz plik konfiguracyjny | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj plik konfiguracyjny | Otwórz plik w zewnętrznym edytorze. |
| `` u `` | Sprawdź aktualizacje | |
| `` <enter> `` | Przełącz na ostatnie repozytorium | |
| `` a `` | Show/cycle all branch logs | |
| `` 0 `` | Focus main view | |
<pre>
<kbd>o</kbd>: Otwórz konfigurację
<kbd>e</kbd>: Edytuj konfigurację
<kbd>u</kbd>: Sprawdź aktualizacje
<kbd>&lt;enter&gt;</kbd>: Switch to a recent repo
<kbd>a</kbd>: Pokaż wszystkie logi gałęzi
</pre>
## Sub-commity
## Sub-commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj hash commita do schowka | |
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
| `` o `` | Otwórz commit w przeglądarce | |
| `` n `` | Utwórz nową gałąź z commita | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` C `` | Kopiuj (cherry-pick) | Oznacz commit jako skopiowany. Następnie, w widoku lokalnych commitów, możesz nacisnąć `V`, aby wkleić (cherry-pick) skopiowane commity do sprawdzonej gałęzi. W dowolnym momencie możesz nacisnąć `<esc>`, aby anulować zaznaczenie. |
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>c</kbd>: Kopiuj commit (przebieranie)
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
<kbd>/</kbd>: Search the current view by text
</pre>
## Submoduły
## Submodules
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj nazwę submodułu do schowka | |
| `` <enter> `` | Wejdź | Wejdź do submodułu. Po wejściu do submodułu możesz nacisnąć `<esc>`, aby wrócić do repozytorium nadrzędnego. |
| `` d `` | Usuń | Usuń wybrany submoduł i odpowiadający mu katalog. |
| `` u `` | Aktualizuj | Aktualizuj wybrany submoduł. |
| `` n `` | Nowy submoduł | |
| `` e `` | Zaktualizuj URL submodułu | |
| `` i `` | Zainicjuj | Zainicjuj wybrany submoduł, aby przygotować do pobrania. Prawdopodobnie chcesz to kontynuować, wywołując akcję 'update', aby pobrać submoduł. |
| `` b `` | Pokaż opcje masowych operacji na submodułach | |
| `` / `` | Filtruj bieżący widok po tekście | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Add new submodule
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialize submodule
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
## Tagi
## Tags
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy tag to clipboard | |
| `` <space> `` | Przełącz | Przełącz wybrany tag jako odłączoną głowę (detached HEAD). |
| `` n `` | Nowy tag | Utwórz nowy tag z bieżącego commita. Zostaniesz poproszony o wprowadzenie nazwy tagu i opcjonalnego opisu. |
| `` d `` | Usuń | Wyświetl opcje usuwania lokalnego/odległego tagu. |
| `` P `` | Wyślij tag | Wyślij wybrany tag do zdalnego. Zostaniesz poproszony o wybranie zdalnego. |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
<pre>
<kbd>&lt;space&gt;</kbd>: Przełącz
<kbd>d</kbd>: Delete tag
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Create tag
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Zdalne
## Zwykłe
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Wyświetl gałęzie | |
| `` n `` | Nowy zdalny | |
| `` d `` | Usuń | Usuń wybrany zdalny. Wszelkie lokalne gałęzie śledzące gałąź zdalną z tego zdalnego nie zostaną dotknięte. |
| `` e `` | Edytuj | Edytuj nazwę lub URL wybranego zdalnego. |
| `` f `` | Pobierz | Pobierz aktualizacje z zdalnego repozytorium. Pobiera nowe commity i gałęzie bez scalania ich z lokalnymi gałęziami. |
| `` / `` | Filtruj bieżący widok po tekście | |
## Zdalne gałęzie
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj nazwę gałęzi do schowka | |
| `` <space> `` | Przełącz | Przełącz na nową lokalną gałąź na podstawie wybranej gałęzi zdalnej. Nowa gałąź będzie śledzić gałąź zdalną. |
| `` n `` | Nowa gałąź | |
| `` M `` | Scal | Scal wybraną gałąź z aktualnie sprawdzoną gałęzią. |
| `` r `` | Przebazuj | Przebazuj przełączoną gałąź na wybraną gałąź. |
| `` d `` | Usuń | Usuń gałąź zdalną ze zdalnego. |
| `` u `` | Ustaw jako upstream | Ustaw wybraną gałąź zdalną jako upstream sprawdzonej gałęzi. |
| `` s `` | Kolejność sortowania | |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
<pre>
<kbd>mouse wheel down</kbd>: Przewiń w dół (fn+up)
<kbd>mouse wheel up</kbd>: Przewiń w górę (fn+down)
</pre>
-413
View File
@@ -1,413 +0,0 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit Keybindings
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Combinações globais de teclas
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Mudar para um repositório recente | |
| `` <pgup> (fn+up/shift+k) `` | Rolar janela principal para cima | |
| `` <pgdown> (fn+down/shift+j) `` | Rolar a janela principal para baixo | |
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Empurre (Push) | Faça push do branch atual para o seu branch upstream. Se nenhum upstream estiver configurado, você será solicitado a configurar um branch a montante. |
| `` p `` | Puxar (Pull) | Puxe alterações do controle remoto para o ramo atual. Se nenhum upstream estiver configurado, será solicitado configurar um ramo a montante. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | Executar comando da shell | Traga um prompt onde você pode digitar um comando shell para executar. |
| `` <c-p> `` | Ver opções de patch personalizadas | |
| `` m `` | Ver opções de mesclar/rebase | Ver opções para abortar/continuar/pular o merge/rebase atual. |
| `` R `` | Atualizar | Atualize o estado do git (ou seja, execute `git status`, `git branch`, etc em segundo plano para atualizar o conteúdo de painéis). Isso não executa `git fetch`. |
| `` + `` | Next screen mode (normal/half/fullscreen) | |
| `` _ `` | Prev screen mode | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | Cancelar | |
| `` ? `` | Open keybindings menu | |
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | Sair | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | Desfazer | O reflog será usado para determinar qual comando git para executar para desfazer o último comando git. Isto não inclui mudanças na árvore de trabalho; apenas compromissos são tidos em consideração. |
| `` Z `` | Refazer | O reflog será usado para determinar qual comando git para executar para refazer o último comando git. Isto não inclui mudanças na árvore de trabalho; apenas compromissos são tidos em consideração. |
## List panel navigation
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Previous page | |
| `` . `` | Next page | |
| `` < (<home>) `` | Scroll to top | |
| `` > (<end>) `` | Scroll to bottom | |
| `` v `` | Toggle range select | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | Search the current view by text | |
| `` H `` | Rolar à esquerda | |
| `` L `` | Scroll para a direita | |
| `` ] `` | Next tab | |
| `` [ `` | Previous tab | |
## Arquivos
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy path to clipboard | |
| `` <space> `` | Etapa | Alternar para staging para o arquivo selecionado. |
| `` <c-b> `` | Filtrar arquivos por status | |
| `` y `` | Copy to clipboard | |
| `` c `` | Commit | Submeter mudanças em staging |
| `` w `` | Fazer commit de alterações sem pré-commit | |
| `` A `` | Alterar último commit | |
| `` C `` | Enviar alteração usando um editor Git | |
| `` <c-f> `` | Encontrar commit da base para consertar | Encontre o commit em que as suas mudanças atuais estão se baseando, para alterar/consertar o commit. Isso poupa-te você de ter que olhar pelos commits da sua branch um por um para ver qual commit deve ser alterado/consertado<br>Veja a documentação:<br><https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Editar | Abrir arquivo no editor externo. |
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
| `` i `` | Ignore or exclude file | |
| `` r `` | Atualizar arquivos | |
| `` s `` | Stash | Stash todas as alterações. Para outras variações de armazenamento, use a fixação de teclas de armazenamento. |
| `` S `` | Ver opções de stash | Ver opções de stash (por exemplo, trash all, stash staged, stash unsttued). |
| `` a `` | Stage completo | Alternar para todos os arquivos na árvore de trabalho |
| `` <enter> `` | Stage lines / Colapso diretório | Se o item selecionado for um arquivo, o foco na exibição de preparo para o estágio de cenas/linhas individuais. Se o item selecionado for um diretório, recolher/expandi-lo. |
| `` d `` | Descartar | Exibir opções para descartar alterações para o arquivo selecionado. |
| `` g `` | View upstream reset options | |
| `` D `` | Restaurar | Opções de redefinição de exibição para árvore de trabalho (por exemplo, nukando a árvore de trabalho). |
| `` ` `` | Alternar exibição de árvore de arquivo | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | Buscar | Buscar alterações do controle remoto. |
| `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
| `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
| `` 0 `` | Focus main view | |
| `` / `` | Search the current view by text | |
## Branches locais
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy branch name to clipboard | |
| `` i `` | Exibir opções do git-flow | |
| `` <space> `` | Verificar | Checar item selecionado |
| `` n `` | Nova branch | |
| `` N `` | Mover commits para uma nova branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | Create pull request | |
| `` O `` | View create pull request options | |
| `` <c-y> `` | Copiar URL do pull request para área de transferência | |
| `` c `` | Checar por nome | Checar por nome. Na caixa de entrada você pode inserir '-' para trocar para a última branch |
| `` - `` | Checkout da branch anterior | |
| `` F `` | Forçar checagem | Forçar checagem da branch selecionada. Isso irá descartar todas as mudanças no seu diretório de trabalho antes cheque a branch selecionada |
| `` d `` | Apagar | Ver opções de exclusão para a branch local/remoto. |
| `` r `` | Refazer | Refazer a branch checada na branch selecionada |
| `` M `` | Mesclar | Ver opções para mesclar o item selecionado no branch atual (mesclar regularmente, mesclar squash) |
| `` f `` | Avanço rápido | Encaminhamento rápido de branch selecionada a partir do upstream. |
| `` T `` | New tag | |
| `` s `` | Sort order | |
| `` g `` | Restaurar | |
| `` R `` | Rename branch | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Branches remotos
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy branch name to clipboard | |
| `` <space> `` | Verificar | Checar a nova branch baseada na brach remota selecionada, ou a branch remota como HEAD, desanexado |
| `` n `` | Nova branch | |
| `` M `` | Mesclar | Ver opções para mesclar o item selecionado no branch atual (mesclar regularmente, mesclar squash) |
| `` r `` | Refazer | Refazer a branch checada na branch selecionada |
| `` d `` | Apagar | Excluir o branch remoto do controle remoto. |
| `` u `` | Definir como upstream | Definir o ramo remoto selecionado como fluxo do branch check-out. |
| `` s `` | Sort order | |
| `` g `` | Restaurar | Ver opções de redefinição (soft/mixed/hard) para redefinir para o item selecionado. |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Commit arquivos
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy path to clipboard | |
| `` y `` | Copy to clipboard | |
| `` c `` | Verificar | Arquivo de check-out. Isso substitui o arquivo em sua árvore de trabalho com a versão do commit selecionado. |
| `` d `` | Remover | Descartar as alterações desse commit para este arquivo. Isso executa uma rebase interativa em segundo plano, então você pode ter um conflito de merge se um commit posterior também alterar este arquivo. |
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
| `` e `` | Editar | Abrir arquivo no editor externo. |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` <space> `` | Alternar entre o arquivo incluído no patch | Alternar se o arquivo está incluído no patch personalizado. Veja https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Alternar todos os arquivos | Adicionar/remover todos os arquivos de commit para atualização personalizada. Consulte https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Insira o arquivo / Alternar diretório recolhido | Se um arquivo estiver selecionado, insira o arquivo para que você possa adicionar/remover linhas individuais no patch personalizado. Se um diretório for selecionado, ative o diretório. |
| `` ` `` | Alternar exibição de árvore de arquivo | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
| `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
| `` 0 `` | Focus main view | |
| `` / `` | Search the current view by text | |
## Commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` b `` | View bisect options | |
| `` s `` | Squash | Squash o commit selecionado no commit abaixo dele. A mensagem do commit selecionado será anexada ao commit abaixo dele. |
| `` f `` | Fixup | Faça o commit selecionado no commit abaixo dele. Semelhante para o squash, mas a mensagem do commit selecionado será descartada. |
| `` r `` | Reword | Repetir a mensagem de submissão selecionada. |
| `` R `` | Republicar com o editor | |
| `` d `` | Descartar | Solte o commit selecionado. Isso irá remover o commit do branch através de uma rebase. Se o commit faz com que as alterações em commits posteriores dependem, você pode precisar resolver conflitos de merge. |
| `` e `` | Editar (iniciar rebase interativa) | Editar o commit selecionado. Use isto para iniciar uma rebase interativa a partir do commit selecionado. Quando já estiver no meio da reconstrução, isto irá marcar o commit selecionado para edição, o que significa que ao continuar com a reformulação. a rebase irá pausar no commit selecionado para permitir que você faça alterações. |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.<br>If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Escolher | Marque o commit selecionado para ser escolhido (quando meados da base). Isso significa que o commit será mantido ao continuar o rebase. |
| `` F `` | Criar commit de correção | Crie o commit 'correção!' para o commit selecionado. Mais tarde, você pode pressionar `S` neste mesmo commit para aplicar todas os commits de correção acima. |
| `` S `` | Aplicar commits de correções | Aplicar Squash all 'correção!', seja acima do commit selecionado, ou tudo no branch atual (autosquash). |
| `` <c-j> `` | Mover commit um para baixo | |
| `` <c-k> `` | Mover o commit um para cima | |
| `` V `` | Colar (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Modificar | Alterar o commit com mudanças em sted. Se o commit selecionado for o commit HEAD, ele executará o `git commit --amend`. Caso contrário, o compromisso será alterado por meio de uma base de apoio. |
| `` a `` | Alterar atributo de commit | Definir/Redefinir autor de submissão ou co-autor definido. |
| `` t `` | Reverter | Crie um commit reverter para o commit selecionado, que aplica as alterações do commit selecionado em reverso. |
| `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | View log options | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | Verificar | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` N `` | Mover commits para uma nova branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Restaurar | Ver opções de redefinição (soft/mixed/hard) para redefinir para o item selecionado. |
| `` C `` | Copiar (cherry-pick) | Marcar commit como copiado. Então, dentro da visualização local de commits, você pode pressionar `V` para colar (cherry-pick) o(s) commit(s) copiado(s) em seu branch de check-out. A qualquer momento você pode pressionar `<esc>` para cancelar a seleção. |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Ver arquivos | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
## Confirmation panel
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirmar | |
| `` <esc> `` | Fechar/Cancelar | |
| `` <c-o> `` | Copy to clipboard | |
## Etiquetas
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy tag to clipboard | |
| `` <space> `` | Verificar | Checar a tag selecionada como um HEAD, desanexado |
| `` n `` | New tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Apagar | Ver opções de exclusão para tag local/remoto. |
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Restaurar | Ver opções de redefinição (soft/mixed/hard) para redefinir para o item selecionado. |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Input prompt
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirmar | |
| `` <esc> `` | Fechar/Cancelar | |
## Menu
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Executar | |
| `` <esc> `` | Fechar/Cancelar | |
| `` / `` | Filter the current view by text | |
## Painel Principal (Normal)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Rolar para baixo | |
| `` mouse wheel up (fn+down) `` | Rolar para cima | |
| `` <tab> `` | Mudar de visão | Alternar para outra visão (staged/não processadas alterações). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Search the current view by text | |
## Painel Principal (preparação)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Ir para o local anterior | |
| `` <right> `` | Ir para o próximo trecho | |
| `` v `` | Toggle range select | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` <space> `` | Etapa | Ativar/desativar seleção em staged/unstaged |
| `` d `` | Descartar | Quando a mudança não desejada for selecionada, descarte a mudança usando `git reset`. Quando a mudança em fase é selecionada, despare a mudança. |
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
| `` e `` | Editar arquivo | Abrir arquivo no editor externo. |
| `` <esc> `` | Retornar ao painel de arquivos | |
| `` <tab> `` | Mudar de visão | Alternar para outra visão (staged/não processadas alterações). |
| `` E `` | Editar hunk | Editar o local selecionado no editor externo. |
| `` c `` | Commit | Submeter mudanças em staging |
| `` w `` | Fazer commit de alterações sem pré-commit | |
| `` C `` | Enviar alteração usando um editor Git | |
| `` <c-f> `` | Encontrar commit da base para consertar | Encontre o commit em que as suas mudanças atuais estão se baseando, para alterar/consertar o commit. Isso poupa-te você de ter que olhar pelos commits da sua branch um por um para ver qual commit deve ser alterado/consertado<br>Veja a documentação:<br><https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Search the current view by text | |
## Painel principal (mesclagem)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Escolha o local | |
| `` b `` | Pegar todos os pedaços | |
| `` <up> `` | Trecho anterior | |
| `` <down> `` | Próximo trecho | |
| `` <left> `` | Conflito anterior | |
| `` <right> `` | Próximo conflito | |
| `` z `` | Desfazer | Desfazer resolução de conflitos de última mesclagem. |
| `` e `` | Editar arquivo | Abrir arquivo no editor externo. |
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | Retornar ao painel de arquivos | |
## Painel principal (patch build)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Ir para o local anterior | |
| `` <right> `` | Ir para o próximo trecho | |
| `` v `` | Toggle range select | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
| `` e `` | Editar arquivo | Abrir arquivo no editor externo. |
| `` <space> `` | Alternar linhas no caminho | |
| `` <esc> `` | Sair do construtor de patch personalizado | |
| `` / `` | Search the current view by text | |
## Reflog
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <space> `` | Verificar | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` N `` | Mover commits para uma nova branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Restaurar | Ver opções de redefinição (soft/mixed/hard) para redefinir para o item selecionado. |
| `` C `` | Copiar (cherry-pick) | Marcar commit como copiado. Então, dentro da visualização local de commits, você pode pressionar `V` para colar (cherry-pick) o(s) commit(s) copiado(s) em seu branch de check-out. A qualquer momento você pode pressionar `<esc>` para cancelar a seleção. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Remotes
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Ver branches | |
| `` n `` | Novo controle | |
| `` d `` | Remover | Remover o controle remoto. Quaisquer ramificações locais de rastreamento de um ramo remoto do controle não serão afetadas. |
| `` e `` | Editar | Edit the selected remote's name or URL. |
| `` f `` | Buscar | Fetch updates from the remote repository. This retrieves new commits and branches without merging them into your local branches. |
| `` / `` | Filter the current view by text | |
## Secundário
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | Mudar de visão | Alternar para outra visão (staged/não processadas alterações). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Search the current view by text | |
## Stash
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Aplicar | Aplique o stash no seu diretório de trabalho. |
| `` g `` | Pop | Aplique a entrada de stash no seu diretório de trabalho e remova a entrada de stash. |
| `` d `` | Descartar | Remova a entrada do stash da lista de armazenamento. |
| `` n `` | Nova branch | Criar um novo ramo a partir da entrada de lixo selecionada. Isso funciona verificando o commit do qual a entrada de lixo foi criada, criar um novo branch a partir desse commit e, em seguida, aplicar a entrada de lixo ao novo branch como um commit adicional. |
| `` r `` | Renomear o stasj | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Ver arquivos | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Status
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Abrir o ficheiro de config | Abrir arquivo no aplicativo padrão. |
| `` e `` | Editar arquivo de configuração | Abrir arquivo no editor externo. |
| `` u `` | Verificar atualização | |
| `` <enter> `` | Mudar para um repositório recente | |
| `` a `` | Mostrar/ciclo todos os logs de filiais | |
| `` 0 `` | Focus main view | |
## Sub-commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <space> `` | Verificar | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` N `` | Mover commits para uma nova branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Restaurar | Ver opções de redefinição (soft/mixed/hard) para redefinir para o item selecionado. |
| `` C `` | Copiar (cherry-pick) | Marcar commit como copiado. Então, dentro da visualização local de commits, você pode pressionar `V` para colar (cherry-pick) o(s) commit(s) copiado(s) em seu branch de check-out. A qualquer momento você pode pressionar `<esc>` para cancelar a seleção. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Ver arquivos | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
## Submodules
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy submodule name to clipboard | |
| `` <enter> `` | Enter | Enter submodule. After entering the submodule, you can press `<esc>` to escape back to the parent repo. |
| `` d `` | Remover | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | Update selected submodule. |
| `` n `` | New submodule | |
| `` e `` | Update submodule URL | |
| `` i `` | Initialize | Initialize the selected submodule to prepare for fetching. You probably want to follow this up by invoking the 'update' action to fetch the submodule. |
| `` b `` | View bulk submodule options | |
| `` / `` | Filter the current view by text | |
## Sumário do commit
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirmar | |
| `` <esc> `` | Fechar | |
## Worktrees
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Abrir no editor | |
| `` d `` | Remover | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
+266 -346
View File
@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit Связки клавиш
@@ -6,408 +6,328 @@ _Связки клавиш_
## Глобальные сочетания клавиш
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Переключиться на последний репозиторий | |
| `` <pgup> (fn+up/shift+k) `` | Прокрутить вверх главную панель | |
| `` <pgdown> (fn+down/shift+j) `` | Прокрутить вниз главную панель | |
| `` @ `` | Открыть меню журнала команд | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Отправить изменения | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Получить и слить изменения | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | Просмотреть пользовательские параметры патча | |
| `` m `` | Просмотреть параметры слияния/перебазирования | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | Обновить | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Следующий режим экрана (нормальный/полуэкранный/полноэкранный) | |
| `` _ `` | Предыдущий режим экрана | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | Отменить | |
| `` ? `` | Открыть меню | |
| `` <c-s> `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | Выйти | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | Переключить отображение изменении пробелов в просмотрщике сравнении | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | Отменить (через reflog) (экспериментальный) | Журнал ссылок (reflog) будет использоваться для определения того, какую команду git запустить, чтобы отменить последнюю команду git. Сюда не входят изменения в рабочем дереве; учитываются только коммиты. |
| `` Z `` | Повторить (через reflog) (экспериментальный) | Журнал ссылок (reflog) будет использоваться для определения того, какую команду git нужно запустить, чтобы повторить последнюю команду git. Сюда не входят изменения в рабочем дереве; учитываются только коммиты. |
<pre>
<kbd>&lt;c-r&gt;</kbd>: Переключиться на последний репозиторий
<kbd>&lt;pgup&gt;</kbd>: Прокрутить вверх главную панель (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Прокрутить вниз главную панель (fn+down/shift+j)
<kbd>@</kbd>: Открыть меню журнала команд
<kbd>}</kbd>: Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении
<kbd>{</kbd>: Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении
<kbd>:</kbd>: Выполнить пользовательскую команду
<kbd>&lt;c-p&gt;</kbd>: Просмотреть пользовательские параметры патча
<kbd>m</kbd>: Просмотреть параметры слияния/перебазирования
<kbd>R</kbd>: Обновить
<kbd>+</kbd>: Следующий режим экрана (нормальный/полуэкранный/полноэкранный)
<kbd>_</kbd>: Предыдущий режим экрана
<kbd>?</kbd>: Открыть меню
<kbd>&lt;c-s&gt;</kbd>: Просмотреть параметры фильтрации по пути
<kbd>W</kbd>: Открыть меню сравнении
<kbd>&lt;c-e&gt;</kbd>: Открыть меню сравнении
<kbd>&lt;c-w&gt;</kbd>: Переключить отображение изменении пробелов в просмотрщике сравнении
<kbd>z</kbd>: Отменить (через reflog) (экспериментальный)
<kbd>&lt;c-z&gt;</kbd>: Повторить (через reflog) (экспериментальный)
<kbd>P</kbd>: Отправить изменения
<kbd>p</kbd>: Получить и слить изменения
</pre>
## Навигация по панели списка
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Предыдущая страница | |
| `` . `` | Следующая страница | |
| `` < (<home>) `` | Пролистать наверх | |
| `` > (<end>) `` | Прокрутить вниз | |
| `` v `` | Переключить выборку перетаскивания | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | Найти | |
| `` H `` | Прокрутить влево | |
| `` L `` | Прокрутить вправо | |
| `` ] `` | Следующая вкладка | |
| `` [ `` | Предыдущая вкладка | |
## Input prompt
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Подтвердить | |
| `` <esc> `` | Закрыть/отменить | |
## Worktrees
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
## Вторичный
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Найти | |
<pre>
<kbd>,</kbd>: Предыдущая страница
<kbd>.</kbd>: Следующая страница
<kbd>&lt;</kbd>: Пролистать наверх
<kbd>&gt;</kbd>: Прокрутить вниз
<kbd>/</kbd>: Найти
<kbd>H</kbd>: Прокрутить влево
<kbd>L</kbd>: Прокрутить вправо
<kbd>]</kbd>: Следующая вкладка
<kbd>[</kbd>: Предыдущая вкладка
</pre>
## Главная панель (Индексирование)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Выбрать предыдущую часть | |
| `` <right> `` | Выбрать следующую часть | |
| `` v `` | Переключить выборку перетаскивания | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Скопировать выделенный текст в буфер обмена | |
| `` <space> `` | Переключить индекс | Переключить строку в проиндексированные / непроиндексированные |
| `` d `` | Отменить изменение (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | Открыть файл | Open file in default application. |
| `` e `` | Редактировать файл | Open file in external editor. |
| `` <esc> `` | Вернуться к панели файлов | |
| `` <tab> `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). |
| `` E `` | Изменить эту часть | Edit selected hunk in external editor. |
| `` c `` | Сохранить изменения | Commit staged changes. |
| `` w `` | Закоммитить изменения без предварительного хука коммита | |
| `` C `` | Сохранить изменения с помощью редактора git | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Найти | |
<pre>
<kbd>&lt;left&gt;</kbd>: Выбрать предыдущую часть
<kbd>&lt;right&gt;</kbd>: Выбрать следующую часть
<kbd>v</kbd>: Переключить выборку перетаскивания
<kbd>V</kbd>: Переключить выборку перетаскивания
<kbd>a</kbd>: Переключить выборку частей
<kbd>&lt;c-o&gt;</kbd>: Скопировать выделенный текст в буфер обмена
<kbd>o</kbd>: Открыть файл
<kbd>e</kbd>: Редактировать файл
<kbd>&lt;esc&gt;</kbd>: Вернуться к панели файлов
<kbd>&lt;tab&gt;</kbd>: Переключиться на другую панель (проиндексированные/непроиндексированные изменения)
<kbd>&lt;space&gt;</kbd>: Переключить строку в проиндексированные / непроиндексированные
<kbd>d</kbd>: Отменить изменение (git reset)
<kbd>E</kbd>: Изменить эту часть
<kbd>c</kbd>: Сохранить изменения
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
<kbd>/</kbd>: Найти
</pre>
## Главная панель (Обычный)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Прокрутить вниз | |
| `` mouse wheel up (fn+down) `` | Прокрутить вверх | |
| `` <tab> `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | Найти | |
<pre>
<kbd>mouse wheel down</kbd>: Прокрутить вниз (fn+up)
<kbd>mouse wheel up</kbd>: Прокрутить вверх (fn+down)
</pre>
## Главная панель (Слияние)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Выбрать эту часть | |
| `` b `` | Выбрать все части | |
| `` <up> `` | Выбрать предыдущую часть | |
| `` <down> `` | Выбрать следующую часть | |
| `` <left> `` | Выбрать предыдущий конфликт | |
| `` <right> `` | Выбрать следующий конфликт | |
| `` z `` | Отменить | Undo last merge conflict resolution. |
| `` e `` | Редактировать файл | Open file in external editor. |
| `` o `` | Открыть файл | Open file in default application. |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | Вернуться к панели файлов | |
<pre>
<kbd>e</kbd>: Редактировать файл
<kbd>o</kbd>: Открыть файл
<kbd>&lt;left&gt;</kbd>: Выбрать предыдущий конфликт
<kbd>&lt;right&gt;</kbd>: Выбрать следующий конфликт
<kbd>&lt;up&gt;</kbd>: Выбрать предыдущую часть
<kbd>&lt;down&gt;</kbd>: Выбрать следующую часть
<kbd>z</kbd>: Отменить
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
<kbd>&lt;space&gt;</kbd>: Выбрать эту часть
<kbd>b</kbd>: Выбрать все части
<kbd>&lt;esc&gt;</kbd>: Вернуться к панели файлов
</pre>
## Главная панель (сборка патчей)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Выбрать предыдущую часть | |
| `` <right> `` | Выбрать следующую часть | |
| `` v `` | Переключить выборку перетаскивания | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | Скопировать выделенный текст в буфер обмена | |
| `` o `` | Открыть файл | Open file in default application. |
| `` e `` | Редактировать файл | Open file in external editor. |
| `` <space> `` | Добавить/удалить строку(и) для патча | |
| `` <esc> `` | Выйти из сборщика пользовательских патчей | |
| `` / `` | Найти | |
<pre>
<kbd>&lt;left&gt;</kbd>: Выбрать предыдущую часть
<kbd>&lt;right&gt;</kbd>: Выбрать следующую часть
<kbd>v</kbd>: Переключить выборку перетаскивания
<kbd>V</kbd>: Переключить выборку перетаскивания
<kbd>a</kbd>: Переключить выборку частей
<kbd>&lt;c-o&gt;</kbd>: Скопировать выделенный текст в буфер обмена
<kbd>o</kbd>: Открыть файл
<kbd>e</kbd>: Редактировать файл
<kbd>&lt;space&gt;</kbd>: Добавить/удалить строку(и) для патча
<kbd>&lt;esc&gt;</kbd>: Выйти из сборщика пользовательских патчей
<kbd>/</kbd>: Найти
</pre>
## Журнал ссылок (Reflog)
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
| `` n `` | Создать новую ветку с этого коммита | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Сбросить отобранную (скопированную \| cherry-picked) выборку коммитов | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать SHA коммита в буфер обмена
<kbd>&lt;space&gt;</kbd>: Переключить коммит
<kbd>y</kbd>: Скопировать атрибут коммита
<kbd>o</kbd>: Открыть коммит в браузере
<kbd>n</kbd>: Создать новую ветку с этого коммита
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
<kbd>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
## Коммиты
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <c-r> `` | Сбросить отобранную (скопированную \| cherry-picked) выборку коммитов | |
| `` b `` | Просмотреть параметры бинарного поиска | |
| `` s `` | Объединить коммиты (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Объединить несколько коммитов в один отбросив сообщение коммита (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | Перефразировать коммит | Reword the selected commit's message. |
| `` R `` | Переписать коммит с помощью редактора | |
| `` d `` | Удалить коммит | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | Изменить коммит |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.<br>If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Выбрать коммит (в середине перебазирования) |
| `` F `` | Создать fixup коммит | Создать fixup коммит для этого коммита |
| `` S `` | Apply fixup commits | Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение) |
| `` <c-j> `` | Переместить коммит вниз на один | |
| `` <c-k> `` | Переместить коммит вверх на один | |
| `` V `` | Вставить отобранные коммиты (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Править последний коммит с проиндексированными изменениями |
| `` a `` | Установить/убрать автора коммита | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Пометить коммит тегом | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | Открыть меню журнала | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
| `` n `` | Создать новую ветку с этого коммита | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Просмотреть файлы выбранного элемента | |
| `` w `` | View worktree options | |
| `` / `` | Найти | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать SHA коммита в буфер обмена
<kbd>&lt;c-r&gt;</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>&lt;c-j&gt;</kbd>: Переместить коммит вниз на один
<kbd>&lt;c-k&gt;</kbd>: Переместить коммит вверх на один
<kbd>v</kbd>: Вставить отобранные коммиты (cherry-pick)
<kbd>A</kbd>: Править последний коммит с проиндексированными изменениями
<kbd>a</kbd>: Установить/убрать автора коммита
<kbd>t</kbd>: Отменить коммит
<kbd>T</kbd>: Пометить коммит тегом
<kbd>&lt;c-l&gt;</kbd>: Открыть меню журнала
<kbd>&lt;space&gt;</kbd>: Переключить коммит
<kbd>y</kbd>: Скопировать атрибут коммита
<kbd>o</kbd>: Открыть коммит в браузере
<kbd>n</kbd>: Создать новую ветку с этого коммита
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: Просмотреть файлы выбранного элемента
<kbd>/</kbd>: Найти
</pre>
## Локальные Ветки
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название ветки в буфер обмена | |
| `` i `` | Показать параметры git-flow | |
| `` <space> `` | Переключить | Checkout selected item. |
| `` n `` | Новая ветка | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | Создать запрос на принятие изменений | |
| `` O `` | Создать параметры запроса принятие изменений | |
| `` <c-y> `` | Скопировать URL запроса на принятие изменений в буфер обмена | |
| `` c `` | Переключить по названию | Checkout by name. In the input box you can enter '-' to switch to the previous branch. |
| `` - `` | Checkout previous branch | |
| `` F `` | Принудительное переключение | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Перемотать эту ветку вперёд из её upstream-ветки | Fast-forward selected branch from its upstream. |
| `` T `` | Создать тег | |
| `` s `` | Порядок сортировки | |
| `` g `` | Просмотреть параметры сброса | |
| `` R `` | Переименовать ветку | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название ветки в буфер обмена
<kbd>i</kbd>: Показать параметры git-flow
<kbd>&lt;space&gt;</kbd>: Переключить
<kbd>n</kbd>: Новая ветка
<kbd>o</kbd>: Создать запрос на принятие изменений
<kbd>O</kbd>: Создать параметры запроса принятие изменений
<kbd>&lt;c-y&gt;</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>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
## Меню
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Выполнить | |
| `` <esc> `` | Закрыть/отменить | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Выполнить
<kbd>&lt;esc&gt;</kbd>: Закрыть
<kbd>/</kbd>: Filter the current view by text
</pre>
## Панель Подтверждения
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Подтвердить | |
| `` <esc> `` | Закрыть/отменить | |
| `` <c-o> `` | Copy to clipboard | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Подтвердить
<kbd>&lt;esc&gt;</kbd>: Закрыть/отменить
</pre>
## Подкоммиты
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
| `` n `` | Создать новую ветку с этого коммита | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Сбросить отобранную (скопированную \| cherry-picked) выборку коммитов | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Просмотреть файлы выбранного элемента | |
| `` w `` | View worktree options | |
| `` / `` | Найти | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать SHA коммита в буфер обмена
<kbd>&lt;space&gt;</kbd>: Переключить коммит
<kbd>y</kbd>: Скопировать атрибут коммита
<kbd>o</kbd>: Открыть коммит в браузере
<kbd>n</kbd>: Создать новую ветку с этого коммита
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
<kbd>&lt;enter&gt;</kbd>: Просмотреть файлы выбранного элемента
<kbd>/</kbd>: Найти
</pre>
## Подмодули
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название подмодуля в буфер обмена | |
| `` <enter> `` | Enter | Ввести подмодуль |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | Обновить подмодуль |
| `` n `` | Добавить новый подмодуль | |
| `` e `` | Обновить URL подмодуля | |
| `` i `` | Initialize | Инициализировать подмодуль |
| `` b `` | Просмотреть параметры массового подмодуля | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название подмодуля в буфер обмена
<kbd>&lt;enter&gt;</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>
## Сводка коммита
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Подтвердить | |
| `` <esc> `` | Закрыть | |
<pre>
<kbd>&lt;enter&gt;</kbd>: Подтвердить
<kbd>&lt;esc&gt;</kbd>: Закрыть
</pre>
## Сохранить Изменения Файлов
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название файла в буфер обмена | |
| `` y `` | Copy to clipboard | |
| `` c `` | Переключить | Переключить файл |
| `` d `` | Remove | Отменить изменения коммита в этом файле |
| `` o `` | Открыть файл | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Переключить файлы включённые в патч | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Переключить все файлы, включённые в патч | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | Переключить вид дерева файлов | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Найти | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать закомиченное имя файла в буфер обмена
<kbd>c</kbd>: Переключить файл
<kbd>d</kbd>: Отменить изменения коммита в этом файле
<kbd>o</kbd>: Открыть файл
<kbd>e</kbd>: Редактировать файл
<kbd>&lt;space&gt;</kbd>: Переключить файлы включённые в патч
<kbd>a</kbd>: Переключить все файлы, включённые в патч
<kbd>&lt;enter&gt;</kbd>: Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения)
<kbd>`</kbd>: Переключить вид дерева файлов
<kbd>/</kbd>: Найти
</pre>
## Статус
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Открыть файл конфигурации | Open file in default application. |
| `` e `` | Редактировать файл конфигурации | Open file in external editor. |
| `` u `` | Проверить обновления | |
| `` <enter> `` | Переключиться на последний репозиторий | |
| `` a `` | Show/cycle all branch logs | |
| `` 0 `` | Focus main view | |
<pre>
<kbd>o</kbd>: Открыть файл конфигурации
<kbd>e</kbd>: Редактировать файл конфигурации
<kbd>u</kbd>: Проверить обновления
<kbd>&lt;enter&gt;</kbd>: Переключиться на последний репозиторий
<kbd>a</kbd>: Показать все логи ветки
</pre>
## Теги
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy tag to clipboard | |
| `` <space> `` | Переключить | Checkout the selected tag as a detached HEAD. |
| `` n `` | Создать тег | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Отправить тег | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: Переключить
<kbd>d</kbd>: Удалить тег
<kbd>P</kbd>: Отправить тег
<kbd>n</kbd>: Создать тег
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
## Удалённые ветки
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название ветки в буфер обмена | |
| `` <space> `` | Переключить | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | Новая ветка | |
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Установить как upstream-ветку переключённую ветку |
| `` s `` | Порядок сортировки | |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название ветки в буфер обмена
<kbd>&lt;space&gt;</kbd>: Переключить
<kbd>n</kbd>: Новая ветка
<kbd>M</kbd>: Слияние с текущей переключённой веткой
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
<kbd>d</kbd>: Удалить ветку
<kbd>u</kbd>: Установить как upstream-ветку переключённую ветку
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
## Удалённые репозитории
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | Добавить новую удалённую ветку | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Редактировать удалённый репозитории |
| `` f `` | Получить изменения | Получение изменения из удалённого репозитория |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>f</kbd>: Получение изменения из удалённого репозитория
<kbd>n</kbd>: Добавить новую удалённую ветку
<kbd>d</kbd>: Удалить удалённую ветку
<kbd>e</kbd>: Редактировать удалённый репозитории
<kbd>/</kbd>: Filter the current view by text
</pre>
## Файлы
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название файла в буфер обмена | |
| `` <space> `` | Переключить индекс | Toggle staged for selected file. |
| `` <c-b> `` | Фильтровать файлы (проиндексированные/непроиндексированные) | |
| `` y `` | Copy to clipboard | |
| `` c `` | Сохранить изменения | Commit staged changes. |
| `` w `` | Закоммитить изменения без предварительного хука коммита | |
| `` A `` | Правка последнего коммита | |
| `` C `` | Сохранить изменения с помощью редактора git | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | Открыть файл | Open file in default application. |
| `` i `` | Игнорировать или исключить файл | |
| `` r `` | Обновить файлы | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | Просмотреть параметры хранилища | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | Все проиндексированные/непроиндексированные | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Проиндексировать отдельные части/строки для файла или свернуть/развернуть для каталога | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Просмотреть параметры «отмены изменении» | View options for discarding changes to the selected file. |
| `` g `` | Просмотреть параметры сброса upstream-ветки | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | Переключить вид дерева файлов | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | Получить изменения | Fetch changes from remote. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | Найти | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название файла в буфер обмена
<kbd>d</kbd>: Просмотреть параметры «отмены изменении»
<kbd>&lt;space&gt;</kbd>: Переключить индекс
<kbd>&lt;c-b&gt;</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>&lt;enter&gt;</kbd>: Проиндексировать отдельные части/строки для файла или свернуть/развернуть для каталога
<kbd>g</kbd>: Просмотреть параметры сброса upstream-ветки
<kbd>D</kbd>: Просмотреть параметры сброса
<kbd>`</kbd>: Переключить вид дерева файлов
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
<kbd>f</kbd>: Получить изменения
<kbd>/</kbd>: Найти
</pre>
## Хранилище
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Применить припрятанные изменения | Apply the stash entry to your working directory. |
| `` g `` | Применить припрятанные изменения и тут же удалить их из хранилища | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Удалить припрятанные изменения из хранилища | Remove the stash entry from the stash list. |
| `` n `` | Новая ветка | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Переименовать хранилище | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | Просмотреть файлы выбранного элемента | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
<pre>
<kbd>&lt;space&gt;</kbd>: Применить припрятанные изменения
<kbd>g</kbd>: Применить припрятанные изменения и тут же удалить их из хранилища
<kbd>d</kbd>: Удалить припрятанные изменения из хранилища
<kbd>n</kbd>: Новая ветка
<kbd>r</kbd>: Переименовать хранилище
<kbd>&lt;enter&gt;</kbd>: Просмотреть файлы выбранного элемента
<kbd>/</kbd>: Filter the current view by text
</pre>
+276 -356
View File
@@ -1,413 +1,333 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit 按键绑定
_图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## 全局键绑定
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 切换到最近的仓库 | |
| `` <pgup> (fn+up/shift+k) `` | 向上滚动主面板 | |
| `` <pgdown> (fn+down/shift+j) `` | 向下滚动主面板 | |
| `` @ `` | 打开命令日志菜单 | 查看命令日志的选项,例如显示/隐藏命令日志以及聚焦命令日志 |
| `` P `` | 推送 | 推送当前分支到它的上游。如果上游未配置,您可以在弹窗中配置上游分支。 |
| `` p `` | 拉取 | 从当前分支的远程分支获取改动。如果上游未配置,您可以在弹窗中配置上游分支。 |
| `` ) `` | 提高重命名相似度阈值 | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | 降低重命名相似度阈值 | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | 扩大差异视图中显示的上下文范围 | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | 缩小差异视图中显示的上下文范围 | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | 执行 Shell 命令 | 调出可输入shell命令执行的提示符。 |
| `` <c-p> `` | 查看自定义补丁选项 | |
| `` m `` | 查看合并/变基选项 | 查看当前合并或变基的中止、继续、跳过选项 |
| `` R `` | 刷新 | 刷新git状态(即在后台上运行`git status`,`git branch`等命令以更新面板内容) 不会运行`git fetch` |
| `` + `` | 下一屏模式(正常/半屏/全屏) | |
| `` _ `` | 上一屏模式 | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | 取消 | |
| `` ? `` | 打开菜单 | |
| `` <c-s> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 |
| `` W `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 |
| `` <c-e> `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 |
| `` q `` | 退出 | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | 切换是否在差异视图中显示空白字符差异 | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | 撤销 | Reflog将用于确定运行哪个git命令来撤消最后一个git命令。这并不包括对工作树的更改,只考虑提交。 |
| `` Z `` | 重做 | Reflog将用于确定运行哪个git命令来重做上一个git命令。这并不包括对工作树的更改,只考虑提交。 |
<pre>
<kbd>&lt;c-r&gt;</kbd>: 切换到最近的仓库
<kbd>&lt;pgup&gt;</kbd>: 向上滚动主面板 (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: 向下滚动主面板 (fn+down/shift+j)
<kbd>@</kbd>: 打开命令日志菜单
<kbd>}</kbd>: 扩大差异视图中显示的上下文范围
<kbd>{</kbd>: 缩小差异视图中显示的上下文范围
<kbd>:</kbd>: 执行自定义命令
<kbd>&lt;c-p&gt;</kbd>: 查看自定义补丁选项
<kbd>m</kbd>: 查看 合并/变基 选项
<kbd>R</kbd>: 刷新
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
<kbd>_</kbd>: 上一屏模式
<kbd>?</kbd>: 打开菜单
<kbd>&lt;c-s&gt;</kbd>: 查看按路径过滤选项
<kbd>W</kbd>: 打开 diff 菜单
<kbd>&lt;c-e&gt;</kbd>: 打开 diff 菜单
<kbd>&lt;c-w&gt;</kbd>: 切换是否在差异视图中显示空白字符差异
<kbd>z</kbd>: (通过 reflog)撤销「实验功能」
<kbd>&lt;c-z&gt;</kbd>: (通过 reflog)重做「实验功能」
<kbd>P</kbd>: 推送
<kbd>p</kbd>: 拉取
</pre>
## 列表面板导航
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 上一页 | |
| `` . `` | 下一页 | |
| `` < (<home>) `` | 滚动到顶部 | |
| `` > (<end>) `` | 滚动到底部 | |
| `` v `` | 切换拖动选择 | |
| `` <s-down> `` | 向下扩展选择范围 | |
| `` <s-up> `` | 向上扩展选择范围 | |
| `` / `` | 开始搜索 | |
| `` H `` | 向左滚动 | |
| `` L `` | 向右滚动 | |
| `` ] `` | 下一个标签 | |
| `` [ `` | 上一个标签 | |
<pre>
<kbd>,</kbd>: 上一页
<kbd>.</kbd>: 下一页
<kbd>&lt;</kbd>: 滚动到顶部
<kbd>&gt;</kbd>: 滚动到底部
<kbd>/</kbd>: 开始搜索
<kbd>H</kbd>: 向左滚动
<kbd>L</kbd>: 向右滚动
<kbd>]</kbd>: 下一个标签
<kbd>[</kbd>: 上一个标签
</pre>
## Input prompt
## Reflog 页面
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 确认 | |
| `` <esc> `` | 关闭 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
<kbd>c</kbd>: 复制提交(拣选)
<kbd>C</kbd>: 复制提交范围(拣选)
<kbd>&lt;c-r&gt;</kbd>: 重置已拣选(复制)的提交
<kbd>&lt;enter&gt;</kbd>: 查看提交
<kbd>/</kbd>: Filter the current view by text
</pre>
## Reflog
## 分支页面
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(如hash、URL、diff、消息、作者)。 |
| `` o `` | 在浏览器中打开提交 | |
| `` n `` | 从提交创建新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,您可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` * `` | 选择当前分支的提交 | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将分支名称复制到剪贴板
<kbd>i</kbd>: 显示 git-flow 选项
<kbd>&lt;space&gt;</kbd>: 检出
<kbd>n</kbd>: 新分支
<kbd>o</kbd>: 创建抓取请求
<kbd>O</kbd>: 创建抓取请求选项
<kbd>&lt;c-y&gt;</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>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: 查看提交
<kbd>/</kbd>: Filter the current view by text
</pre>
## 子提交
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(如hash、URL、diff、消息、作者)。 |
| `` o `` | 在浏览器中打开提交 | |
| `` n `` | 从提交创建新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,您可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` * `` | 选择当前分支的提交 | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 查看提交的文件 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 开始搜索 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
<kbd>c</kbd>: 复制提交(拣选)
<kbd>C</kbd>: 复制提交范围(拣选)
<kbd>&lt;c-r&gt;</kbd>: 重置已拣选(复制)的提交
<kbd>&lt;enter&gt;</kbd>: 查看提交的文件
<kbd>/</kbd>: 开始搜索
</pre>
## 子模块
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制子模块名称到剪贴板 | |
| `` <enter> `` | 进入 | 输入子模块 |
| `` d `` | 删除 | 删除选定的子模块及其相应的目录 |
| `` u `` | 更新 | 更新子模块 |
| `` n `` | 添加新的子模块 | |
| `` e `` | 更新子模块 URL | |
| `` i `` | 初始化 | 初始化子模块 |
| `` b `` | 查看批量子模块选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 工作区
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | 新建工作树 | |
| `` <space> `` | 切换 | 切换到选中的工作树 |
| `` o `` | 在编辑器中编写 | |
| `` d `` | 删除 | 删除选定的工作树。这将删除工作树的目录以及 .git 目录中有关工作树的元数据。 |
| `` / `` | 通过文本过滤当前视图 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将子模块名称复制到剪贴板
<kbd>&lt;enter&gt;</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>
## 提交
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
| `` b `` | 查看二分查找选项 | |
| `` s `` | 压缩(Squash) | 将已选提交压缩到该提交之下。这些选定的提交的消息会附加到该提交的消息之下。 |
| `` f `` | 修正 (fixup) | 将选定的提交合并到其下面的提交中。与压缩类似,但所选提交的消息将被丢弃。 |
| `` r `` | 改写提交 | 重写所选提交的消息。 |
| `` R `` | 使用编辑器重命名提交 | |
| `` d `` | 删除提交 | 删除选中的提交。这将通过变基从分支中删除该提交,如果该提交修改的内容依赖于后续的提交,则需要解决合并冲突。 |
| `` e `` | 编辑(开始交互式变基) | 编辑提交 |
| `` i `` | 开始交互式变基 | 为分支上的提交启动交互式变基。这将包括从 HEAD 提交到第一个合并提交或主分支提交的所有提交。<br>如果您想从所选提交启动交互式变基,请按 `e`。 |
| `` p `` | 拣选(Pick) | 标记选中的提交为 picked(变基过程中)。这意味该提交将在后续的变基中保留。 |
| `` F `` | 为此提交创建修正 | 创建修正提交 |
| `` S `` | 应用该修复提交 | 压缩所选提交之上或当前分支的所有 “fixup!” 提交(自动压缩)。 |
| `` <c-j> `` | 下移提交 | |
| `` <c-k> `` | 上移提交 | |
| `` V `` | 粘贴提交(拣选) | |
| `` B `` | 标记一个主提交用于变基 | 选择下一次变基的主提交。当您变基到一个分支时,只有高于主提交的提交才会被引入。这使用“git rebase --onto”命令。 |
| `` A `` | 修补(Amend) | 用已暂存的变更来修补提交 |
| `` a `` | 修补提交属性 | 设置或重置提交的作者,或添加其他作者。 |
| `` t `` | 撤销(Revert) | 为所选提交创建还原提交,这会反向应用所选提交的更改。 |
| `` T `` | 标签提交 | 创建一个新标签指向所选提交。您可以在弹窗中输入标签名称和描述(可选)。 |
| `` <c-l> `` | 打开日志菜单 | 查看提交日志的选项,例如更改排序顺序、隐藏 git graph、显示整个 git graph。 |
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(如hash、URL、diff、消息、作者)。 |
| `` o `` | 在浏览器中打开提交 | |
| `` n `` | 从提交创建新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,您可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` * `` | 选择当前分支的提交 | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 查看提交的文件 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 开始搜索 | |
## 提交信息
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 确认 | |
| `` <esc> `` | 关闭 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>&lt;c-r&gt;</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>&lt;c-j&gt;</kbd>: 下移提交
<kbd>&lt;c-k&gt;</kbd>: 上移提交
<kbd>v</kbd>: 粘贴提交(拣选)
<kbd>A</kbd>: 用已暂存的更改来修补提交
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: 还原提交
<kbd>T</kbd>: 标签提交
<kbd>&lt;c-l&gt;</kbd>: 打开日志菜单
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
<kbd>c</kbd>: 复制提交(拣选)
<kbd>C</kbd>: 复制提交范围(拣选)
<kbd>&lt;enter&gt;</kbd>: 查看提交的文件
<kbd>/</kbd>: 开始搜索
</pre>
## 提交文件
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制路径到剪贴板 | |
| `` y `` | 复制到剪贴板 | |
| `` c `` | 检出 | 检出文件 |
| `` d `` | 删除 | 放弃对此文件的提交变更 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <space> `` | 补丁中包含的切换文件 | 切换文件是否包含在自定义补丁中。请参阅 https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches。 |
| `` a `` | 操作所有文件 | 添加或删除所有提交中的文件到自定义的补丁中。请参阅 https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches。 |
| `` <enter> `` | 输入文件以将所选行添加到补丁中(或切换目录折叠) | 如果已选择一个文件,则Enter进入该文件,以便您可以向自定义补丁添加/删除单独的行。如果选择了目录,则切换目录。 |
| `` ` `` | 切换文件树视图 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
| `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
| `` 0 `` | Focus main view | |
| `` / `` | 开始搜索 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的文件名复制到剪贴板
<kbd>c</kbd>: 检出文件
<kbd>d</kbd>: 放弃对此文件的提交更改
<kbd>o</kbd>: 打开文件
<kbd>e</kbd>: 编辑文件
<kbd>&lt;space&gt;</kbd>: 补丁中包含的切换文件
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
<kbd>`</kbd>: 切换文件树视图
<kbd>/</kbd>: 开始搜索
</pre>
## 提交讯息
<pre>
<kbd>&lt;enter&gt;</kbd>: 确认
<kbd>&lt;esc&gt;</kbd>: 关闭
</pre>
## 文件
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制路径到剪贴板 | |
| `` <space> `` | 切换暂存状态 | 为选定的文件切换暂存状态 |
| `` <c-b> `` | 通过状态过滤文件 | |
| `` y `` | 复制到剪贴板 | |
| `` c `` | 提交变更 | 提交暂存文件 |
| `` w `` | 提交变更而无需预先提交钩子 | |
| `` A `` | 修补最后一次提交 | |
| `` C `` | 使用 Git 编辑器提交变更 | |
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` i `` | 忽略文件 | |
| `` r `` | 刷新文件 | |
| `` s `` | 贮藏 | 贮藏所有变更.若要使用其他贮藏变体,请使用查看贮藏选项快捷键 |
| `` S `` | 查看贮藏选项 | 查看贮藏选项(例如:贮藏所有、贮藏已暂存变更、贮藏未暂存变更) |
| `` a `` | 切换所有文件的暂存状态 | 切换工作区中所有文件的已暂存/未暂存状态 |
| `` <enter> `` | 暂存单个 块/行 用于文件, 或 折叠/展开 目录 | 如果选中的是一个文件,则会进入到暂存视图,以便可以暂存单个代码块/行。如果选中的是一个目录,则会折叠/展开这个目录 |
| `` d `` | 查看'放弃变更'选项 | 查看选中文件的放弃变更选项 |
| `` g `` | 查看上游重置选项 | |
| `` D `` | 重置 | 查看工作树的重置选项(例如:清除工作树)。 |
| `` ` `` | 切换文件树视图 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | 抓取 | 从远程获取变更 |
| `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
| `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
| `` 0 `` | Focus main view | |
| `` / `` | 开始搜索 | |
## 本地分支
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制分支名称到剪贴板 | |
| `` i `` | 显示 git-flow 选项 | |
| `` <space> `` | 检出 | 检出选中的项目 |
| `` n `` | 新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | 创建拉取请求 | |
| `` O `` | 创建拉取请求选项 | |
| `` <c-y> `` | 复制拉取请求 URL 到剪贴板 | |
| `` c `` | 按名称检出 | 按名称检出。在输入框中,您可以输入'-' 来切换到最后一个分支。 |
| `` - `` | Checkout previous branch | |
| `` F `` | 强制检出 | 强制检出所选分支。这将在检出所选分支之前放弃工作目录中的所有本地更改。 |
| `` d `` | 删除 | 查看本地/远程分支的删除选项 |
| `` r `` | 变基 | 将检出的分支变基到所选的分支上。 |
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
| `` f `` | 从上游快进此分支 | 将当前分支直接移动到远程追踪分支的最新提交 |
| `` T `` | 创建标签 | |
| `` s `` | 排序 | |
| `` g `` | 查看重置选项 | |
| `` R `` | 重命名分支 | |
| `` u `` | 查看上游选项 | 查看与分支上游相关的选项,例如设置/取消设置上游和重置为上游。 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将文件名复制到剪贴板
<kbd>d</kbd>: 查看'放弃更改'选项
<kbd>&lt;space&gt;</kbd>: 切换暂存状态
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: 提交更改
<kbd>w</kbd>: 提交更改而无需预先提交钩子
<kbd>A</kbd>: 修补最后一次提交
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
<kbd>e</kbd>: 编辑文件
<kbd>o</kbd>: 打开文件
<kbd>i</kbd>: 忽略文件
<kbd>r</kbd>: 刷新文件
<kbd>s</kbd>: 将所有更改加入贮藏
<kbd>S</kbd>: 查看贮藏选项
<kbd>a</kbd>: 切换所有文件的暂存状态
<kbd>&lt;enter&gt;</kbd>: 暂存单个 块/行 用于文件, 或 折叠/展开 目录
<kbd>g</kbd>: 查看上游重置选项
<kbd>D</kbd>: 查看重置选项
<kbd>`</kbd>: 切换文件树视图
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
<kbd>f</kbd>: 抓取
<kbd>/</kbd>: 开始搜索
</pre>
## 构建补丁中
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 选择上一个区块 | |
| `` <right> `` | 选择下一个区块 | |
| `` v `` | 切换拖动选择 | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 复制选中文本到剪贴板 | |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
| `` <space> `` | 添加/移除 行到补丁 | |
| `` <esc> `` | 退出逐行模式 | |
| `` / `` | 开始搜索 | |
<pre>
<kbd>&lt;left&gt;</kbd>: 选择上一个区块
<kbd>&lt;right&gt;</kbd>: 选择下一个区块
<kbd>v</kbd>: 切换拖动选择
<kbd>V</kbd>: 切换拖动选择
<kbd>a</kbd>: 切换选择区块
<kbd>&lt;c-o&gt;</kbd>: 将选中文本复制到剪贴板
<kbd>o</kbd>: 打开文件
<kbd>e</kbd>: 编辑文件
<kbd>&lt;space&gt;</kbd>: 添加/移除 行到补丁
<kbd>&lt;esc&gt;</kbd>: 退出逐行模式
<kbd>/</kbd>: 开始搜索
</pre>
## 标签
## 标签页面
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制标签到剪贴板 | |
| `` <space> `` | 检出 | 检出选择的标签作为分离的HEAD |
| `` n `` | 创建标签 | 基于当前提交创建一个新标签。您将在弹窗中输入标签名称和描述(可选)。 |
| `` d `` | 删除 | 查看本地/远程标签的删除选项 |
| `` P `` | 推送标签 | 推送选择的标签到远端。您将在弹窗中选择一个远端。 |
| `` g `` | 重置 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 次要
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | 开始搜索 | |
<pre>
<kbd>&lt;space&gt;</kbd>: 检出
<kbd>d</kbd>: 删除标签
<kbd>P</kbd>: 推送标签
<kbd>n</kbd>: 创建标签
<kbd>g</kbd>: 查看重置选项
<kbd>&lt;enter&gt;</kbd>: 查看提交
<kbd>/</kbd>: Filter the current view by text
</pre>
## 正在合并
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 选中区块 | |
| `` b `` | 选中所有区块 | |
| `` <up> `` | 选择顶部块 | |
| `` <down> `` | 选择底部块 | |
| `` <left> `` | 选择上一个冲突 | |
| `` <right> `` | 选择下一个冲突 | |
| `` z `` | 撤销 | 撤消上次合并冲突解决 |
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | 返回文件面板 | |
<pre>
<kbd>e</kbd>: 编辑文件
<kbd>o</kbd>: 打开文件
<kbd>&lt;left&gt;</kbd>: 选择上一个冲突
<kbd>&lt;right&gt;</kbd>: 选择下一个冲突
<kbd>&lt;up&gt;</kbd>: 选择顶部块
<kbd>&lt;down&gt;</kbd>: 选择底部块
<kbd>z</kbd>: 撤销
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
<kbd>&lt;space&gt;</kbd>: 选中区块
<kbd>b</kbd>: 选中所有区块
<kbd>&lt;esc&gt;</kbd>: 返回文件面板
</pre>
## 正在暂存
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 选择上一个区块 | |
| `` <right> `` | 选择下一个区块 | |
| `` v `` | 切换拖动选择 | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 复制选中文本到剪贴板 | |
| `` <space> `` | 切换暂存状态 | 切换行暂存状态 |
| `` d `` | 取消变更(git reset) | 当选择未暂存的变更时,使用git reset丢弃该变更。当选择已暂存的变更时,取消暂存该变更 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
| `` <esc> `` | 返回文件面板 | |
| `` <tab> `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) |
| `` E `` | 编辑代码块 | 在外部编辑器中编辑选中的代码块 |
| `` c `` | 提交变更 | 提交暂存文件 |
| `` w `` | 提交变更而无需预先提交钩子 | |
| `` C `` | 使用 Git 编辑器提交变更 | |
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 开始搜索 | |
<pre>
<kbd>&lt;left&gt;</kbd>: 选择上一个区块
<kbd>&lt;right&gt;</kbd>: 选择下一个区块
<kbd>v</kbd>: 切换拖动选择
<kbd>V</kbd>: 切换拖动选择
<kbd>a</kbd>: 切换选择区块
<kbd>&lt;c-o&gt;</kbd>: 将选中文本复制到剪贴板
<kbd>o</kbd>: 打开文件
<kbd>e</kbd>: 编辑文件
<kbd>&lt;esc&gt;</kbd>: 返回文件面板
<kbd>&lt;tab&gt;</kbd>: 切换到其他面板
<kbd>&lt;space&gt;</kbd>: 切换行暂存状态
<kbd>d</kbd>: 取消变更 (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 提交更改
<kbd>w</kbd>: 提交更改而无需预先提交钩子
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
<kbd>/</kbd>: 开始搜索
</pre>
## 正常
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 向下滚动 | |
| `` mouse wheel up (fn+down) `` | 向上滚动 | |
| `` <tab> `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | 开始搜索 | |
<pre>
<kbd>mouse wheel down</kbd>: 向下滚动 (fn+up)
<kbd>mouse wheel up</kbd>: 向上滚动 (fn+down)
</pre>
## 状态
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 打开配置文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑配置文件 | 使用外部编辑器打开文件 |
| `` u `` | 检查更新 | |
| `` <enter> `` | 切换到最近的仓库 | |
| `` a `` | 显示/循环所有分支日志 | |
| `` 0 `` | Focus main view | |
<pre>
<kbd>o</kbd>: 打开配置文件
<kbd>e</kbd>: 编辑配置文件
<kbd>u</kbd>: 检查更新
<kbd>&lt;enter&gt;</kbd>: 切换到最近的仓库
<kbd>a</kbd>: 显示所有分支的日志
</pre>
## 确认面板
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 确认 | |
| `` <esc> `` | 关闭 | |
| `` <c-o> `` | 复制到剪贴板 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 确认
<kbd>&lt;esc&gt;</kbd>: 关闭
</pre>
## 菜单
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 执行 | |
| `` <esc> `` | 关闭 | |
| `` / `` | 通过文本过滤当前视图 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 执行
<kbd>&lt;esc&gt;</kbd>: 关闭
<kbd>/</kbd>: Filter the current view by text
</pre>
## 贮藏
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 应用 | 将贮藏项应用到您的工作目录。 |
| `` g `` | 应用并删除 | 将存储项应用到工作目录并删除存储项。 |
| `` d `` | 删除 | 从贮藏列表中删除该贮藏项 |
| `` n `` | 新分支 | 从选定的贮藏项创建一个新分支。这是通过 git 检查创建贮藏项的提交,从该提交创建一个新分支,然后将贮藏项作为附加提交应用到新分支来实现的。 |
| `` r `` | 重命名贮藏 | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 查看提交的文件 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 远程
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 查看分支 | |
| `` n `` | 添加新的远程仓库 | |
| `` d `` | 删除 | 删除选中的远程。从远程跟踪远程分支的任何本地分支都不会受到影响。 |
| `` e `` | 编辑 | 编辑远程仓库 |
| `` f `` | 抓取 | 抓取远程仓库 |
| `` / `` | 通过文本过滤当前视图 | |
<pre>
<kbd>&lt;space&gt;</kbd>: 应用
<kbd>g</kbd>: 应用并删除
<kbd>d</kbd>: 删除
<kbd>n</kbd>: 新分支
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: 查看提交的文件
<kbd>/</kbd>: Filter the current view by text
</pre>
## 远程分支
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 复制分支名称到剪贴板 | |
| `` <space> `` | 检出 | 基于当前选中的远程分支检出一个新的本地分支,或者将远程分支作分离的HEAD。 |
| `` n `` | 新分支 | |
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
| `` r `` | 变基 | 将检出的分支变基到所选的分支上。 |
| `` d `` | 删除 | 从远程删除远程分支。 |
| `` u `` | 设置为上游 | 设置为检出分支的上游 |
| `` s `` | 排序 | |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将分支名称复制到剪贴板
<kbd>&lt;space&gt;</kbd>: 检出
<kbd>n</kbd>: 新分支
<kbd>M</kbd>: 合并到当前检出的分支
<kbd>r</kbd>: 将已检出的分支变基到该分支
<kbd>d</kbd>: 删除分支
<kbd>u</kbd>: 设置为检出分支的上游
<kbd>g</kbd>: 查看重置选项
<kbd>&lt;enter&gt;</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>
+279 -359
View File
@@ -1,413 +1,333 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
# Lazygit 鍵盤快捷鍵
_說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
_說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
## 全域快捷鍵
## 全局快捷鍵
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 切換到最近使用的版本庫 | |
| `` <pgup> (fn+up/shift+k) `` | 向上捲動主面板 | |
| `` <pgdown> (fn+down/shift+j) `` | 向下捲動主面板 | |
| `` @ `` | 開啟命令記錄選單 | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | 推送 | 推送到遠端。如果沒有設定遠端,會開啟設定視窗。 |
| `` p `` | 拉取 | 從遠端同步當前分支。如果沒有設定遠端,會開啟設定視窗。 |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename.<br><br>The default can be changed in the config file with the key 'git.renameSimilarityThreshold'. |
| `` } `` | 增加差異檢視中顯示變更周圍上下文的大小 | Increase the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` { `` | 減小差異檢視中顯示變更周圍上下文的大小 | Decrease the amount of the context shown around changes in the diff view.<br><br>The default can be changed in the config file with the key 'git.diffContextSize'. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | 檢視自訂補丁選項 | |
| `` m `` | 查看合併/變基選項 | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | 重新整理 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | 下一個螢幕模式(常規/半螢幕/全螢幕) | |
| `` _ `` | 上一個螢幕模式 | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` <esc> `` | 取消 | |
| `` ? `` | 開啟選單 | |
| `` <c-s> `` | 檢視篩選路徑選項 | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | 結束 | |
| `` <c-z> `` | Suspend the application | |
| `` <c-w> `` | 切換是否在差異檢視中顯示空格變更 | Toggle whether or not whitespace changes are shown in the diff view.<br><br>The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. |
| `` z `` | 復原 | 將使用 reflog 確任 git 指令以復原。這不包括工作區更改;只考慮提交。 |
| `` Z `` | 取消復原 | 將使用 reflog 確任 git 指令以重作。這不包括工作區更改;只考慮提交。 |
<pre>
<kbd>&lt;c-r&gt;</kbd>: 切換到最近使用的版本庫
<kbd>&lt;pgup&gt;</kbd>: 向上捲動主面板 (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: 向下捲動主面板 (fn+down/shift+j)
<kbd>@</kbd>: 開啟命令記錄選單
<kbd>}</kbd>: 增加差異檢視中顯示變更周圍上下文的大小
<kbd>{</kbd>: 減小差異檢視中顯示變更周圍上下文的大小
<kbd>:</kbd>: 執行自訂命令
<kbd>&lt;c-p&gt;</kbd>: 檢視自訂補丁選項
<kbd>m</kbd>: 查看合併/變基選項
<kbd>R</kbd>: 重新整理
<kbd>+</kbd>: 下一個螢幕模式(常規/半螢幕/全螢幕)
<kbd>_</kbd>: 上一個螢幕模式
<kbd>?</kbd>: 開啟選單
<kbd>&lt;c-s&gt;</kbd>: 檢視篩選路徑選項
<kbd>W</kbd>: 開啟差異比較選單
<kbd>&lt;c-e&gt;</kbd>: 開啟差異比較選單
<kbd>&lt;c-w&gt;</kbd>: 切換是否在差異檢視中顯示空格變更
<kbd>z</kbd>: 復原
<kbd>&lt;c-z&gt;</kbd>: 取消復原
<kbd>P</kbd>: 推送
<kbd>p</kbd>: 拉取
</pre>
## 移動
## 列表面板導航
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 上一頁 | |
| `` . `` | 下一頁 | |
| `` < (<home>) `` | 捲動到頂部 | |
| `` > (<end>) `` | 捲動到底部 | |
| `` v `` | 切換拖曳選擇 | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | 搜尋 | |
| `` H `` | 向左捲動 | |
| `` L `` | 向右捲動 | |
| `` ] `` | 下一個索引標籤 | |
| `` [ `` | 上一個索引標籤 | |
<pre>
<kbd>,</kbd>: 上一頁
<kbd>.</kbd>: 下一頁
<kbd>&lt;</kbd>: 捲動到頂部
<kbd>&gt;</kbd>: 捲動到底部
<kbd>/</kbd>: 開始搜尋
<kbd>H</kbd>: 向左捲動
<kbd>L</kbd>: 向右捲動
<kbd>]</kbd>: 下一個索引標籤
<kbd>[</kbd>: 上一個索引標籤
</pre>
## Input prompt
## Reflog
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 關閉/取消 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交 SHA 到剪貼簿
<kbd>&lt;space&gt;</kbd>: 檢出提交
<kbd>y</kbd>: 複製提交屬性
<kbd>o</kbd>: 在瀏覽器中開啟提交
<kbd>n</kbd>: 從提交建立新分支
<kbd>g</kbd>: 檢視重設選項
<kbd>c</kbd>: 複製提交 (揀選)
<kbd>C</kbd>: 複製提交範圍 (揀選)
<kbd>&lt;c-r&gt;</kbd>: 重設選定的揀選 (複製) 提交
<kbd>&lt;enter&gt;</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>&lt;left&gt;</kbd>: 選擇上一個衝突
<kbd>&lt;right&gt;</kbd>: 選擇下一個衝突
<kbd>&lt;up&gt;</kbd>: 選擇上一段
<kbd>&lt;down&gt;</kbd>: 選擇下一段
<kbd>z</kbd>: 復原
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
<kbd>&lt;space&gt;</kbd>: 挑選程式碼片段
<kbd>b</kbd>: 挑選所有程式碼片段
<kbd>&lt;esc&gt;</kbd>: 返回檔案面板
</pre>
## 主視窗 (預存中)
<pre>
<kbd>&lt;left&gt;</kbd>: 選擇上一段
<kbd>&lt;right&gt;</kbd>: 選擇下一段
<kbd>v</kbd>: 切換拖曳選擇
<kbd>V</kbd>: 切換拖曳選擇
<kbd>a</kbd>: 切換選擇程式碼塊
<kbd>&lt;c-o&gt;</kbd>: 複製所選文本至剪貼簿
<kbd>o</kbd>: 開啟檔案
<kbd>e</kbd>: 編輯檔案
<kbd>&lt;esc&gt;</kbd>: 返回檔案面板
<kbd>&lt;tab&gt;</kbd>: 切換至另一個面板 (已預存/未預存更改)
<kbd>&lt;space&gt;</kbd>: 切換現有行的狀態 (已預存/未預存)
<kbd>d</kbd>: 刪除變更 (git reset)
<kbd>E</kbd>: 編輯程式碼塊
<kbd>c</kbd>: 提交變更
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
<kbd>C</kbd>: 使用 git 編輯器提交變更
<kbd>/</kbd>: 開始搜尋
</pre>
## 主面板 (補丁生成)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 選擇上一段 | |
| `` <right> `` | 選擇下一段 | |
| `` v `` | 切換拖曳選擇 | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
| `` <esc> `` | 退出自訂補丁建立器 | |
| `` / `` | 搜尋 | |
## 主面板(一般)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 向下捲動 | |
| `` mouse wheel up (fn+down) `` | 向上捲動 | |
| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | 搜尋 | |
## 主面板(合併)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 挑選程式碼片段 | |
| `` b `` | 挑選所有程式碼片段 | |
| `` <up> `` | 選擇上一段 | |
| `` <down> `` | 選擇下一段 | |
| `` <left> `` | 選擇上一個衝突 | |
| `` <right> `` | 選擇下一個衝突 | |
| `` z `` | 復原 | Undo last merge conflict resolution. |
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` <esc> `` | 返回檔案面板 | |
## 主面板(預存)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 選擇上一段 | |
| `` <right> `` | 選擇下一段 | |
| `` v `` | 切換拖曳選擇 | |
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
| `` <space> `` | 切換預存 | 切換現有行的狀態 (已預存/未預存) |
| `` d `` | 刪除變更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
| `` <esc> `` | 返回檔案面板 | |
| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
| `` E `` | 編輯程式碼塊 | Edit selected hunk in external editor. |
| `` c `` | 提交變更 | 提交暫存區變更 |
| `` w `` | 沒有預提交 hook 就提交更改 | |
| `` C `` | 使用 git 編輯器提交變更 | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;left&gt;</kbd>: 選擇上一段
<kbd>&lt;right&gt;</kbd>: 選擇下一段
<kbd>v</kbd>: 切換拖曳選擇
<kbd>V</kbd>: 切換拖曳選擇
<kbd>a</kbd>: 切換選擇程式碼塊
<kbd>&lt;c-o&gt;</kbd>: 複製所選文本至剪貼簿
<kbd>o</kbd>: 開啟檔案
<kbd>e</kbd>: 編輯檔案
<kbd>&lt;space&gt;</kbd>: 向 (或從) 補丁中添加/刪除行
<kbd>&lt;esc&gt;</kbd>: 退出自訂補丁建立器
<kbd>/</kbd>: 開始搜尋
</pre>
## 功能表
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 執行 | |
| `` <esc> `` | 關閉/取消 | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 執行
<kbd>&lt;esc&gt;</kbd>: 關閉
<kbd>/</kbd>: Filter the current view by text
</pre>
## 子提交
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
| `` n `` | 從提交建立新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交 SHA 到剪貼簿
<kbd>&lt;space&gt;</kbd>: 檢出提交
<kbd>y</kbd>: 複製提交屬性
<kbd>o</kbd>: 在瀏覽器中開啟提交
<kbd>n</kbd>: 從提交建立新分支
<kbd>g</kbd>: 檢視重設選項
<kbd>c</kbd>: 複製提交 (揀選)
<kbd>C</kbd>: 複製提交範圍 (揀選)
<kbd>&lt;c-r&gt;</kbd>: 重設選定的揀選 (複製) 提交
<kbd>&lt;enter&gt;</kbd>: 檢視所選項目的檔案
<kbd>/</kbd>: 開始搜尋
</pre>
## 子模組
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製子模組名稱到剪貼簿 | |
| `` <enter> `` | Enter | 進入子模組 |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | 更新子模組 |
| `` n `` | 新增子模組 | |
| `` e `` | 更新子模組 URL | |
| `` i `` | Initialize | 初始化子模組 |
| `` b `` | 查看批量子模組選項 | |
| `` / `` | 搜尋 | |
## 工作目錄
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | 在編輯器中開啟 | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製子模組名稱到剪貼簿
<kbd>&lt;enter&gt;</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>
## 提交
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` b `` | 查看二分選項 | |
| `` s `` | 壓縮 (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | 修復 (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | 改寫提交 | 改寫選中的提交訊息 |
| `` R `` | 使用編輯器改寫提交 | |
| `` d `` | 刪除提交 | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | 編輯(開始互動變基) | 編輯提交 |
| `` i `` | 開始互動變基 | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.<br>If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | 挑選 | 挑選提交 (於變基過程中) |
| `` F `` | 建立修復提交 | 為此提交建立修復提交 |
| `` S `` | 壓縮上方所有「fixup」提交(自動壓縮) | 是否壓縮上方 {{.commit}} 所有「fixup」提交? |
| `` <c-j> `` | 向下移動提交 | |
| `` <c-k> `` | 向上移動提交 | |
| `` V `` | 貼上提交 (揀選) | |
| `` B `` | 為了變基已標注提交為基準提交 | 請為了下一次變基選擇一項基準提交;此將執行 `git rebase --onto`。 |
| `` A `` | 修改 | 使用已預存的更改修正提交 |
| `` a `` | 設定/重設提交作者 | Set/Reset commit author or set co-author. |
| `` t `` | 還原 | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | 打標籤到提交 | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | 開啟記錄選單 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
| `` n `` | 從提交建立新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交 SHA 到剪貼簿
<kbd>&lt;c-r&gt;</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>&lt;c-j&gt;</kbd>: 向下移動提交
<kbd>&lt;c-k&gt;</kbd>: 向上移動提交
<kbd>v</kbd>: 貼上提交 (揀選)
<kbd>A</kbd>: 使用已預存的更改修正提交
<kbd>a</kbd>: 設置/重設提交作者
<kbd>t</kbd>: 還原提交
<kbd>T</kbd>: 打標籤到提交
<kbd>&lt;c-l&gt;</kbd>: 開啟記錄選單
<kbd>&lt;space&gt;</kbd>: 檢出提交
<kbd>y</kbd>: 複製提交屬性
<kbd>o</kbd>: 在瀏覽器中開啟提交
<kbd>n</kbd>: 從提交建立新分支
<kbd>g</kbd>: 檢視重設選項
<kbd>c</kbd>: 複製提交 (揀選)
<kbd>C</kbd>: 複製提交範圍 (揀選)
<kbd>&lt;enter&gt;</kbd>: 檢視所選項目的檔案
<kbd>/</kbd>: 開始搜尋
</pre>
## 提交摘要
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 關閉 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 關閉
</pre>
## 提交檔案
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
| `` y `` | 複製到剪貼簿 | |
| `` c `` | 檢出 | 檢出檔案 |
| `` d `` | Remove | Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file. |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯 | 使用外部編輯器開啟 |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <space> `` | 切換檔案是否包含在補丁中 | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | 切換所有檔案是否包含在補丁中 | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | 輸入檔案以將選定的行添加至補丁(或切換目錄折疊) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交的檔案名稱到剪貼簿
<kbd>c</kbd>: 檢出檔案
<kbd>d</kbd>: 捨棄此提交對此檔案的更改
<kbd>o</kbd>: 開啟檔案
<kbd>e</kbd>: 編輯檔案
<kbd>&lt;space&gt;</kbd>: 切換檔案是否包含在補丁中
<kbd>a</kbd>: 切換所有檔案是否包含在補丁中
<kbd>&lt;enter&gt;</kbd>: 輸入檔案以將選定的行添加至補丁(或切換目錄折疊)
<kbd>`</kbd>: 切換檔案樹狀視圖
<kbd>/</kbd>: 開始搜尋
</pre>
## 收藏 (Stash)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 套用 | Apply the stash entry to your working directory. |
| `` g `` | 還原 | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | 捨棄 | Remove the stash entry from the stash list. |
| `` n `` | 新分支 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | 重新命名收藏 | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
## 日誌
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
| `` n `` | 從提交建立新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` * `` | Select commits of current branch | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;space&gt;</kbd>: 套用
<kbd>g</kbd>: 還原
<kbd>d</kbd>: 捨棄
<kbd>n</kbd>: 新分支
<kbd>r</kbd>: 重新命名收藏
<kbd>&lt;enter&gt;</kbd>: 檢視所選項目的檔案
<kbd>/</kbd>: Filter the current view by text
</pre>
## 本地分支
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
| `` i `` | 顯示 git-flow 選項 | |
| `` <space> `` | 檢出 | 檢出選定的項目。 |
| `` n `` | 新分支 | |
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
| `` o `` | 建立拉取請求 | |
| `` O `` | 建立拉取請求選項 | |
| `` <c-y> `` | 複製拉取請求的 URL 到剪貼板 | |
| `` c `` | 根據名稱檢出 | Checkout by name. In the input box you can enter '-' to switch to the previous branch. |
| `` - `` | Checkout previous branch | |
| `` F `` | 強制檢出 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | 刪除 | View delete options for local/remote branch. |
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | 從上游快進此分支 | 從遠端快進所選的分支 |
| `` T `` | 建立標籤 | |
| `` s `` | 排序規則 | |
| `` g `` | 檢視重設選項 | |
| `` R `` | 重新命名分支 | |
| `` u `` | 檢視遠端設定 | 檢視有關遠端分支的設定(例如重設至遠端) |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製分支名稱到剪貼簿
<kbd>i</kbd>: 顯示 git-flow 選項
<kbd>&lt;space&gt;</kbd>: 檢出
<kbd>n</kbd>: 新分支
<kbd>o</kbd>: 建立拉取請求
<kbd>O</kbd>: 建立拉取請求選項
<kbd>&lt;c-y&gt;</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>&lt;enter&gt;</kbd>: 檢視提交
<kbd>/</kbd>: Filter the current view by text
</pre>
## 標籤
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy tag to clipboard | |
| `` <space> `` | 檢出 | Checkout the selected tag as a detached HEAD. |
| `` n `` | 建立標籤 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | 刪除 | View delete options for local/remote tag. |
| `` P `` | 推送標籤 | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | 重設 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;space&gt;</kbd>: 檢出
<kbd>d</kbd>: 刪除標籤
<kbd>P</kbd>: 推送標籤
<kbd>n</kbd>: 建立標籤
<kbd>g</kbd>: 檢視重設選項
<kbd>&lt;enter&gt;</kbd>: 檢視提交
<kbd>/</kbd>: Filter the current view by text
</pre>
## 檔案
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
| `` <space> `` | 切換預存 | Toggle staged for selected file. |
| `` <c-b> `` | 篩選檔案 (預存/未預存) | |
| `` y `` | 複製到剪貼簿 | |
| `` c `` | 提交變更 | 提交暫存區變更 |
| `` w `` | 沒有預提交 hook 就提交更改 | |
| `` A `` | 修改上次提交 | |
| `` C `` | 使用 git 編輯器提交變更 | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | 編輯 | 使用外部編輯器開啟 |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` i `` | 忽略或排除檔案 | |
| `` r `` | 重新整理檔案 | |
| `` s `` | 收藏 | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | 檢視收藏選項 | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | 全部預存/取消預存 | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | 選擇檔案中的單個程式碼塊/行,或展開/折疊目錄 | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | 捨棄 | 檢視選中變動進行捨棄復原 |
| `` g `` | 檢視遠端重設選項 | |
| `` D `` | 重設 | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
| `` f `` | 擷取 | 同步遠端異動 |
| `` - `` | Collapse all files | Collapse all directories in the files tree |
| `` = `` | Expand all files | Expand all directories in the file tree |
| `` 0 `` | Focus main view | |
| `` / `` | 搜尋 | |
## 次要
| Key | Action | Info |
|-----|--------|-------------|
| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
| `` <esc> `` | Exit back to side panel | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製檔案名稱到剪貼簿
<kbd>d</kbd>: 檢視“捨棄更改”的選項
<kbd>&lt;space&gt;</kbd>: 切換預存
<kbd>&lt;c-b&gt;</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>&lt;enter&gt;</kbd>: 選擇檔案中的單個程式碼塊/行,或展開/折疊目錄
<kbd>g</kbd>: 檢視上游重設選項
<kbd>D</kbd>: 檢視重設選項
<kbd>`</kbd>: 切換檔案樹狀視圖
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
<kbd>f</kbd>: 擷取
<kbd>/</kbd>: 開始搜尋
</pre>
## 狀態
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 開啟設定檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯設定檔案 | 使用外部編輯器開啟 |
| `` u `` | 檢查更新 | |
| `` <enter> `` | 切換到最近使用的版本庫 | |
| `` a `` | Show/cycle all branch logs | |
| `` 0 `` | Focus main view | |
<pre>
<kbd>o</kbd>: 開啟設定檔案
<kbd>e</kbd>: 編輯設定檔案
<kbd>u</kbd>: 檢查更新
<kbd>&lt;enter&gt;</kbd>: 切換到最近使用的版本庫
<kbd>a</kbd>: 顯示所有分支日誌
</pre>
## 確認面板
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 關閉/取消 | |
| `` <c-o> `` | 複製到剪貼簿 | |
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 關閉/取消
</pre>
## 遠端
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | 新增遠端 | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | 編輯 | 編輯遠端 |
| `` f `` | 擷取 | 擷取遠端 |
| `` / `` | 搜尋 | |
<pre>
<kbd>f</kbd>: 擷取遠端
<kbd>n</kbd>: 新增遠端
<kbd>d</kbd>: 移除遠端
<kbd>e</kbd>: 編輯遠端
<kbd>/</kbd>: Filter the current view by text
</pre>
## 遠端分支
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | 新分支 | |
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
| `` d `` | 刪除 | Delete the remote branch from the remote. |
| `` u `` | 設置為遠端 | 將此分支設為當前分支之遠端 |
| `` s `` | 排序規則 | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` 0 `` | Focus main view | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製分支名稱到剪貼簿
<kbd>&lt;space&gt;</kbd>: 檢出
<kbd>n</kbd>: 新分支
<kbd>M</kbd>: 合併到當前檢出的分支
<kbd>r</kbd>: 將已檢出的分支變基至此分支
<kbd>d</kbd>: 刪除分支
<kbd>u</kbd>: 將此分支設為當前分支之上游
<kbd>g</kbd>: 檢視重設選項
<kbd>&lt;enter&gt;</kbd>: 檢視提交
<kbd>/</kbd>: Filter the current view by text
</pre>
Generated
-127
View File
@@ -1,127 +0,0 @@
{
"nodes": {
"flake-compat": {
"locked": {
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"revCount": 69,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.1.0/01948eb7-9cba-704f-bbf3-3fa956735b52/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1759362264,
"narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "758cf7296bee11f1706a574c77d072b8a7baa881",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1759831965,
"narHash": "sha256-vgPm2xjOmKdZ0xKA6yLXPJpjOtQPHfaZDRtH+47XEBo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c9b6fb798541223bbb396d287d16f43520250518",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1754788789,
"narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "a73b9c743612e4244d865a2fdee11865283c04e6",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1754340878,
"narHash": "sha256-lgmUyVQL9tSnvvIvBp7x1euhkkCho7n3TMzgjdvgPoU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "cab778239e705082fe97bb4990e0d24c50924c04",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"systems": "systems",
"treefmt-nix": "treefmt-nix"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1758728421,
"narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
-125
View File
@@ -1,125 +0,0 @@
{
description = "A simple terminal UI for git commands";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
systems.url = "github:nix-systems/default";
flake-parts.url = "github:hercules-ci/flake-parts";
flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz";
treefmt-nix.url = "github:numtide/treefmt-nix";
};
outputs =
inputs@{ flake-parts, systems, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = import systems;
imports = [
inputs.treefmt-nix.flakeModule
];
perSystem =
{
pkgs,
system,
...
}:
let
goMod = builtins.readFile ./go.mod;
versionMatch = builtins.match ".*go[[:space:]]([0-9]+\\.[0-9]+)(\\.[0-9]+)?.*" goMod;
goVersion =
if versionMatch != null then
builtins.head versionMatch
else
throw "Could not extract Go version from go.mod";
goOverlay = final: prev: {
go = prev."go_${builtins.replaceStrings [ "." ] [ "_" ] goVersion}";
};
lazygit = pkgs.buildGoModule rec {
pname = "lazygit";
version = "dev";
gitCommit = inputs.self.rev or inputs.self.dirtyRev or "dev";
src = ./.;
vendorHash = null;
# Disable integration tests that require specific environment
doCheck = false;
nativeBuildInputs = with pkgs; [
git
makeWrapper
];
buildInputs = [ pkgs.git ];
ldflags = [
"-s"
"-w"
"-X main.commit=${gitCommit}"
"-X main.version=${version}"
"-X main.buildSource=nix"
];
postInstall = ''
wrapProgram $out/bin/lazygit \
--prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.git ]}
'';
meta = {
description = "A simple terminal UI for git commands";
homepage = "https://github.com/jesseduffield/lazygit";
license = pkgs.lib.licenses.mit;
maintainers = [ "jesseduffield" ];
platforms = pkgs.lib.platforms.unix;
mainProgram = "lazygit";
};
};
in
{
_module.args.pkgs = import inputs.nixpkgs {
inherit system;
overlays = [ goOverlay ];
config = { };
};
packages = {
default = lazygit;
inherit lazygit;
};
devShells.default = pkgs.mkShell {
name = "lazygit-dev";
buildInputs = with pkgs; [
# Go toolchain
go
gotools
# Development tools
git
gnumake
];
# Environment variables for development
CGO_ENABLED = "0";
};
treefmt = {
programs.nixfmt.enable = pkgs.lib.meta.availableOn pkgs.stdenv.buildPlatform pkgs.nixfmt-rfc-style.compiler;
programs.nixfmt.package = pkgs.nixfmt-rfc-style;
programs.gofmt.enable = true;
};
checks.build = lazygit;
};
flake = {
overlays.default = final: prev: {
lazygit = inputs.self.packages.${final.system}.lazygit;
};
};
};
}
+37 -52
View File
@@ -1,89 +1,74 @@
module github.com/jesseduffield/lazygit
go 1.25.0
// This is necessary to ignore test files when executing gofumpt.
ignore ./test
go 1.18
require (
dario.cat/mergo v1.0.1
github.com/adrg/xdg v0.4.0
github.com/OpenPeeDeeP/xdg v1.0.0
github.com/atotto/clipboard v0.1.4
github.com/aybabtme/humanlog v0.4.1
github.com/cli/safeexec v1.0.0
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.11
github.com/gdamore/tcell/v2 v2.9.0
github.com/go-errors/errors v1.5.1
github.com/fsmiamoto/git-todo-parser v0.0.5
github.com/fsnotify/fsnotify v1.4.7
github.com/gdamore/tcell/v2 v2.6.0
github.com/go-errors/errors v1.4.2
github.com/gookit/color v1.4.2
github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
github.com/jesseduffield/gocui v0.3.1-0.20230710004407-9bbfd873713b
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
github.com/kyokomi/emoji/v2 v2.2.8
github.com/lucasb-eyer/go-colorful v1.3.0
github.com/mattn/go-runewidth v0.0.19
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mattn/go-runewidth v0.0.14
github.com/mgutz/str v1.2.0
github.com/mitchellh/go-ps v1.0.0
github.com/pmezard/go-difflib v1.0.0
github.com/sahilm/fuzzy v0.1.0
github.com/samber/lo v1.31.0
github.com/sanity-io/litter v1.5.2
github.com/sasha-s/go-deadlock v0.3.6
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.9.5
github.com/sasha-s/go-deadlock v0.3.1
github.com/sirupsen/logrus v1.4.2
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
github.com/stefanhaller/git-todo-parser v0.0.7-0.20250905083220-c50528f08304
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.8.0
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/sync v0.17.0
golang.org/x/sys v0.36.0
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.0.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/hpcloud/tail v1.0.0 // indirect
github.com/invopop/jsonschema v0.10.0 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/onsi/ginkgo v1.10.3 // indirect
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
github.com/onsi/gomega v1.7.1 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
+106 -555
View File
@@ -1,54 +1,9 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/OpenPeeDeeP/xdg v1.0.0 h1:UDLmNjCGFZZCaVMB74DqYEtXkHxnTxcr4FeJVF9uCn8=
github.com/OpenPeeDeeP/xdg v1.0.0/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
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/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
@@ -56,168 +11,86 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
github.com/aybabtme/humanlog v0.4.1/go.mod h1:B0bnQX4FTSU3oftPMTTPvENCy8LqixLDvYJA9TUCAGo=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
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/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v2 v2.8.0/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys=
github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsmiamoto/git-todo-parser v0.0.5 h1:Bhzd/vz/6Qm3udfkd6NO9fWfD3TpwR9ucp3N75/J5I8=
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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg=
github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/invopop/jsonschema v0.10.0 h1:c1ktzNLBun3LyQQhyty5WE3lulbOdIIyOVlkmDLehcE=
github.com/invopop/jsonschema v0.10.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c h1:tC2PaiisXAC5sOjDPfMArSnbswDObtCssx+xn28edX4=
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c/go.mod h1:F2fEBk0ddf6ixrBrJjY7phfQ3hL9rXG0uSjvwYe50bE=
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd h1:ViKj6qth8FgcIWizn9KiACWwPemWSymx62OPN0tHT+Q=
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd/go.mod h1:lRhCiBr6XjQrvcQVa+UYsy/99d3wMXn/a0nSQlhnhlA=
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a h1:z3NQvFXAWGT4B/MwQBZc+1ej8WJ/Nv35xngQRvwzPuI=
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8TIcC6Y4RI+1ZbJDOHfGJ570tPeYVCqo7/tws=
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20230710004407-9bbfd873713b h1:8FmmdaYHes1m3oNyNdS+VIgkgkFpNZAWuwTnvp0tG14=
github.com/jesseduffield/gocui v0.3.1-0.20230710004407-9bbfd873713b/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/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/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/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/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/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3 h1:s995u+gNQADMaixtNOs+jilRC/Q78q0UXSI7+4T0cDE=
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3/go.mod h1:MCbEh21gjOzxc31udr3u4QM9DAdf8TFJCZz3u5hYIxA=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -226,11 +99,9 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE=
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
@@ -240,438 +111,126 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE=
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
github.com/sanity-io/litter v1.5.2 h1:AnC8s9BMORWH5a4atZ4D6FPVvKGzHcnc5/IVTa87myw=
github.com/sanity-io/litter v1.5.2/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0=
github.com/sasha-s/go-deadlock v0.3.6 h1:TR7sfOnZ7x00tWPfD397Peodt57KzMDo+9Ae9rMiUmw=
github.com/sasha-s/go-deadlock v0.3.6/go.mod h1:CUqNyyvMxTyjFqDT7MRg9mb4Dv/btmGTqSR+rky/UXo=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stefanhaller/git-todo-parser v0.0.7-0.20250905083220-c50528f08304 h1:bg+K3E0GYuqwTGaEfNrsZ0rH0Bw4p3EmPjk9Zjnua+w=
github.com/stefanhaller/git-todo-parser v0.0.7-0.20250905083220-c50528f08304/go.mod h1:HFt9hGqMzgQ+gVxMKcvTvGaFz4Y0yYycqqAp2V3wcJY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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-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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
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.1-0.20180807135948-17ff2d5776d2/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0 h1:KzcWKJ0nMAmGoBhYVMnkWc1rXjB42lKy5aIys4TdLOA=
@@ -681,17 +240,9 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+33 -52
View File
@@ -11,9 +11,10 @@ import (
"github.com/go-errors/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/jesseduffield/generics/slices"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
@@ -22,7 +23,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/i18n"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"github.com/jesseduffield/lazygit/pkg/logs"
"github.com/jesseduffield/lazygit/pkg/updates"
)
@@ -42,7 +42,7 @@ func Run(
common *common.Common,
startArgs appTypes.StartArgs,
) {
app, err := NewApp(config, startArgs.IntegrationTest, common)
app, err := NewApp(config, common)
if err == nil {
err = app.Run(startArgs)
@@ -62,21 +62,20 @@ func Run(
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
userConfig := config.GetUserConfig()
appState := config.GetAppState()
log := newLogger(config)
// Initialize with English for the time being; the real translation set for
// the configured language will be read after reading the user config
tr := i18n.EnglishTranslationSet()
cmn := &common.Common{
Log: log,
Tr: tr,
AppState: appState,
Debug: config.GetDebug(),
Fs: afero.NewOsFs(),
var err error
log := newLogger(config)
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
if err != nil {
return nil, err
}
cmn.SetUserConfig(userConfig)
return cmn, nil
return &common.Common{
Log: log,
Tr: tr,
UserConfig: userConfig,
Debug: config.GetDebug(),
}, nil
}
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
@@ -86,13 +85,13 @@ func newLogger(cfg config.AppConfigurer) *logrus.Entry {
log.Fatal(err)
}
return logs.NewDevelopmentLogger(logPath)
} else {
return logs.NewProductionLogger()
}
return logs.NewProductionLogger()
}
// NewApp bootstrap a new application
func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest, common *common.Common) (*App, error) {
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
app := &App{
closers: []io.Closer{},
Config: config,
@@ -116,14 +115,7 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
return app, err
}
// If we're not in a repo, GetRepoPaths will return an error. The error is moot for us
// at this stage, since we'll try to init a new repo in setupRepo(), below
repoPaths, err := git_commands.GetRepoPaths(app.OSCommand.Cmd, gitVersion)
if err != nil {
common.Log.Infof("Error getting repo paths: %v", err)
}
showRecentRepos, err := app.setupRepo(repoPaths)
showRecentRepos, err := app.setupRepo()
if err != nil {
return app, err
}
@@ -133,29 +125,22 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
showRecentRepos = true
}
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName, test)
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
if err != nil {
return app, err
}
return app, nil
}
const minGitVersionStr = "2.32.0"
func minGitVersionErrorMessage(tr *i18n.TranslationSet) string {
return fmt.Sprintf(tr.MinGitVersionError, minGitVersionStr)
}
func (app *App) validateGitVersion() (*git_commands.GitVersion, error) {
version, err := git_commands.GetGitVersion(app.OSCommand)
// if we get an error anywhere here we'll show the same status
minVersionError := errors.New(minGitVersionErrorMessage(app.Tr))
minVersionError := errors.New(app.Tr.MinGitVersionError)
if err != nil {
return nil, minVersionError
}
minRequiredVersion, _ := git_commands.ParseGitVersion(minGitVersionStr)
if version.IsOlderThanVersion(minRequiredVersion) {
if version.IsOlderThan(2, 20, 0) {
return nil, minVersionError
}
@@ -179,16 +164,14 @@ func openRecentRepo(app *App) bool {
return false
}
func (app *App) setupRepo(
repoPaths *git_commands.RepoPaths,
) (bool, error) {
func (app *App) setupRepo() (bool, error) {
if env.GetGitDirEnv() != "" {
// we've been given the git dir directly. Skip setup
// we've been given the git dir directly. We'll verify this dir when initializing our Git object
return false, nil
}
// if we are not in a git repo, we ask if we want to `git init`
if repoPaths == nil {
if err := commands.VerifyInGitRepo(app.OSCommand); err != nil {
cwd, err := os.Getwd()
if err != nil {
return false, err
@@ -200,7 +183,7 @@ func (app *App) setupRepo(
var shouldInitRepo bool
initialBranchArg := ""
switch app.UserConfig().NotARepository {
switch app.UserConfig.NotARepository {
case "prompt":
// Offer to initialize a new repository in current directory.
fmt.Print(app.Tr.CreateRepo)
@@ -234,7 +217,6 @@ func (app *App) setupRepo(
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
return false, err
}
return false, nil
}
@@ -252,7 +234,10 @@ func (app *App) setupRepo(
}
// Run this afterward so that the previous repo creation steps can run without this interfering
if repoPaths.IsBareRepo() {
if isBare, err := git_commands.IsBareRepo(app.OSCommand); isBare {
if err != nil {
return false, err
}
fmt.Print(app.Tr.BareRepo)
@@ -280,11 +265,7 @@ func (app *App) Run(startArgs appTypes.StartArgs) error {
// Close closes any resources
func (app *App) Close() error {
for _, closer := range app.closers {
if err := closer.Close(); err != nil {
return err
}
}
return nil
return slices.TryForEach(app.closers, func(closer io.Closer) error {
return closer.Close()
})
}
+73 -123
View File
@@ -5,10 +5,12 @@ import (
"fmt"
"log"
"os"
"os/exec"
"strconv"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
@@ -31,14 +33,12 @@ const (
DaemonKindUnknown DaemonKind = iota
DaemonKindExitImmediately
DaemonKindRemoveUpdateRefsForCopiedBranch
DaemonKindMoveTodosUp
DaemonKindMoveTodosDown
DaemonKindCherryPick
DaemonKindMoveTodoUp
DaemonKindMoveTodoDown
DaemonKindInsertBreak
DaemonKindChangeTodoActions
DaemonKindDropMergeCommit
DaemonKindMoveFixupCommitDown
DaemonKindWriteRebaseTodo
)
const (
@@ -52,15 +52,13 @@ func getInstruction() Instruction {
jsonData := os.Getenv(DaemonInstructionEnvKey)
mapping := map[DaemonKind]func(string) Instruction{
DaemonKindExitImmediately: deserializeInstruction[*ExitImmediatelyInstruction],
DaemonKindRemoveUpdateRefsForCopiedBranch: deserializeInstruction[*RemoveUpdateRefsForCopiedBranchInstruction],
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
DaemonKindDropMergeCommit: deserializeInstruction[*DropMergeCommitInstruction],
DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction],
DaemonKindMoveTodosUp: deserializeInstruction[*MoveTodosUpInstruction],
DaemonKindMoveTodosDown: deserializeInstruction[*MoveTodosDownInstruction],
DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction],
DaemonKindWriteRebaseTodo: deserializeInstruction[*WriteRebaseTodoInstruction],
DaemonKindExitImmediately: deserializeInstruction[*ExitImmediatelyInstruction],
DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction],
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction],
DaemonKindMoveTodoUp: deserializeInstruction[*MoveTodoUpInstruction],
DaemonKindMoveTodoDown: deserializeInstruction[*MoveTodoDownInstruction],
DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction],
}
return mapping[getDaemonKind()](jsonData)
@@ -76,6 +74,8 @@ func Handle(common *common.Common) {
if err := instruction.run(common); err != nil {
log.Fatal(err)
}
os.Exit(0)
}
func InDaemonMode() bool {
@@ -92,7 +92,7 @@ func getDaemonKind() DaemonKind {
}
func getCommentChar() byte {
cmd := exec.Command("git", "config", "--get", "--null", "core.commentChar")
cmd := secureexec.Command("git", "config", "--get", "--null", "core.commentChar")
if output, err := cmd.Output(); err == nil && len(output) == 2 {
return output[0]
}
@@ -155,26 +155,39 @@ func NewExitImmediatelyInstruction() Instruction {
return &ExitImmediatelyInstruction{}
}
type RemoveUpdateRefsForCopiedBranchInstruction struct{}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) Kind() DaemonKind {
return DaemonKindRemoveUpdateRefsForCopiedBranch
type CherryPickCommitsInstruction struct {
Todo string
}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) SerializedInstructions() string {
func NewCherryPickCommitsInstruction(commits []*models.Commit) Instruction {
todoLines := lo.Map(commits, func(commit *models.Commit, _ int) TodoLine {
return TodoLine{
Action: "pick",
Commit: commit,
}
})
todo := TodoLinesToString(todoLines)
return &CherryPickCommitsInstruction{
Todo: todo,
}
}
func (self *CherryPickCommitsInstruction) Kind() DaemonKind {
return DaemonKindCherryPick
}
func (self *CherryPickCommitsInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) run(common *common.Common) error {
func (self *CherryPickCommitsInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return nil
return utils.PrependStrToTodoFile(path, []byte(self.Todo))
})
}
func NewRemoveUpdateRefsForCopiedBranchInstruction() Instruction {
return &RemoveUpdateRefsForCopiedBranchInstruction{}
}
type ChangeTodoActionsInstruction struct {
Changes []ChangeTodoAction
}
@@ -195,55 +208,28 @@ func (self *ChangeTodoActionsInstruction) SerializedInstructions() string {
func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
changes := lo.Map(self.Changes, func(c ChangeTodoAction, _ int) utils.TodoChange {
return utils.TodoChange{
Hash: c.Hash,
NewAction: c.NewAction,
for _, c := range self.Changes {
if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction, getCommentChar()); err != nil {
return err
}
})
}
return utils.EditRebaseTodo(path, changes, getCommentChar())
return nil
})
}
type DropMergeCommitInstruction struct {
Hash string
}
func NewDropMergeCommitInstruction(hash string) Instruction {
return &DropMergeCommitInstruction{
Hash: hash,
}
}
func (self *DropMergeCommitInstruction) Kind() DaemonKind {
return DaemonKindDropMergeCommit
}
func (self *DropMergeCommitInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *DropMergeCommitInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return utils.DropMergeCommit(path, self.Hash, getCommentChar())
})
}
// Takes the hash of some commit, and the hash of a fixup commit that was created
// Takes the sha of some commit, and the sha of a fixup commit that was created
// at the end of the branch, then moves the fixup commit down to right after the
// original commit, changing its type to "fixup" (only if ChangeToFixup is true)
// original commit, changing its type to "fixup"
type MoveFixupCommitDownInstruction struct {
OriginalHash string
FixupHash string
ChangeToFixup bool
OriginalSha string
FixupSha string
}
func NewMoveFixupCommitDownInstruction(originalHash string, fixupHash string, changeToFixup bool) Instruction {
func NewMoveFixupCommitDownInstruction(originalSha string, fixupSha string) Instruction {
return &MoveFixupCommitDownInstruction{
OriginalHash: originalHash,
FixupHash: fixupHash,
ChangeToFixup: changeToFixup,
OriginalSha: originalSha,
FixupSha: fixupSha,
}
}
@@ -257,67 +243,55 @@ func (self *MoveFixupCommitDownInstruction) SerializedInstructions() string {
func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return utils.MoveFixupCommitDown(path, self.OriginalHash, self.FixupHash, self.ChangeToFixup, getCommentChar())
return utils.MoveFixupCommitDown(path, self.OriginalSha, self.FixupSha, getCommentChar())
})
}
type MoveTodosUpInstruction struct {
Hashes []string
type MoveTodoUpInstruction struct {
Sha string
}
func NewMoveTodosUpInstruction(hashes []string) Instruction {
return &MoveTodosUpInstruction{
Hashes: hashes,
func NewMoveTodoUpInstruction(sha string) Instruction {
return &MoveTodoUpInstruction{
Sha: sha,
}
}
func (self *MoveTodosUpInstruction) Kind() DaemonKind {
return DaemonKindMoveTodosUp
func (self *MoveTodoUpInstruction) Kind() DaemonKind {
return DaemonKindMoveTodoUp
}
func (self *MoveTodosUpInstruction) SerializedInstructions() string {
func (self *MoveTodoUpInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *MoveTodosUpInstruction) run(common *common.Common) error {
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
return utils.Todo{
Hash: hash,
}
})
func (self *MoveTodoUpInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return utils.MoveTodosUp(path, todosToMove, false, getCommentChar())
return utils.MoveTodoUp(path, self.Sha, todo.Pick, getCommentChar())
})
}
type MoveTodosDownInstruction struct {
Hashes []string
type MoveTodoDownInstruction struct {
Sha string
}
func NewMoveTodosDownInstruction(hashes []string) Instruction {
return &MoveTodosDownInstruction{
Hashes: hashes,
func NewMoveTodoDownInstruction(sha string) Instruction {
return &MoveTodoDownInstruction{
Sha: sha,
}
}
func (self *MoveTodosDownInstruction) Kind() DaemonKind {
return DaemonKindMoveTodosDown
func (self *MoveTodoDownInstruction) Kind() DaemonKind {
return DaemonKindMoveTodoDown
}
func (self *MoveTodosDownInstruction) SerializedInstructions() string {
func (self *MoveTodoDownInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *MoveTodosDownInstruction) run(common *common.Common) error {
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
return utils.Todo{
Hash: hash,
}
})
func (self *MoveTodoDownInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return utils.MoveTodosDown(path, todosToMove, false, getCommentChar())
return utils.MoveTodoDown(path, self.Sha, todo.Pick, getCommentChar())
})
}
@@ -340,27 +314,3 @@ func (self *InsertBreakInstruction) run(common *common.Common) error {
return utils.PrependStrToTodoFile(path, []byte("break\n"))
})
}
type WriteRebaseTodoInstruction struct {
TodosFileContent []byte
}
func NewWriteRebaseTodoInstruction(todosFileContent []byte) Instruction {
return &WriteRebaseTodoInstruction{
TodosFileContent: todosFileContent,
}
}
func (self *WriteRebaseTodoInstruction) Kind() DaemonKind {
return DaemonKindWriteRebaseTodo
}
func (self *WriteRebaseTodoInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *WriteRebaseTodoInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return os.WriteFile(path, self.TodosFileContent, 0o644)
})
}
+25 -7
View File
@@ -5,14 +5,36 @@ import (
"path/filepath"
"strings"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stefanhaller/git-todo-parser/todo"
)
type TodoLine struct {
Action string
Commit *models.Commit
}
func (self *TodoLine) ToString() string {
if self.Action == "break" {
return self.Action + "\n"
} else {
return self.Action + " " + self.Commit.Sha + " " + self.Commit.Name + "\n"
}
}
func TodoLinesToString(todoLines []TodoLine) string {
lines := slices.Map(todoLines, func(todoLine TodoLine) string {
return todoLine.ToString()
})
return strings.Join(slices.Reverse(lines), "")
}
type ChangeTodoAction struct {
Hash string
Sha string
NewAction todo.TodoCommand
}
@@ -22,10 +44,6 @@ func handleInteractiveRebase(common *common.Common, f func(path string) error) e
path := os.Args[1]
if strings.HasSuffix(path, "git-rebase-todo") {
err := utils.RemoveUpdateRefsForCopiedBranch(path, getCommentChar())
if err != nil {
return err
}
return f(path)
} else if strings.HasSuffix(path, filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
+22 -72
View File
@@ -4,11 +4,7 @@ import (
"bytes"
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"runtime/debug"
@@ -21,6 +17,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/env"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"github.com/jesseduffield/lazygit/pkg/logs/tail"
"github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"gopkg.in/yaml.v3"
@@ -30,17 +27,15 @@ type cliArgs struct {
RepoPath string
FilterPath string
GitArg string
PrintVersionInfo bool
Debug bool
TailLogs bool
PrintDefaultConfig bool
PrintConfigDir bool
UseConfigDir string
WorkTree string
GitDir string
CustomConfigFile string
ScreenMode string
PrintVersionInfo bool
Debug bool
TailLogs bool
Profile bool
PrintDefaultConfig bool
PrintConfigDir bool
}
type BuildInfo struct {
@@ -68,17 +63,8 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
log.Fatal(absRepoPath + " is not a valid git repository.")
}
cliArgs.WorkTree = absRepoPath
cliArgs.GitDir = filepath.Join(absRepoPath, ".git")
err = os.Chdir(absRepoPath)
if err != nil {
log.Fatalf("Failed to change directory to %s: %v", absRepoPath, err)
}
} else if cliArgs.WorkTree != "" {
env.SetWorkTreeEnv(cliArgs.WorkTree)
if err := os.Chdir(cliArgs.WorkTree); err != nil {
log.Fatalf("Failed to change directory to %s: %v", cliArgs.WorkTree, err)
}
}
if cliArgs.CustomConfigFile != "" {
@@ -89,6 +75,10 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
os.Setenv("CONFIG_DIR", cliArgs.UseConfigDir)
}
if cliArgs.WorkTree != "" {
env.SetGitWorkTreeEnv(cliArgs.WorkTree)
}
if cliArgs.GitDir != "" {
env.SetGitDirEnv(cliArgs.GitDir)
}
@@ -125,13 +115,14 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
os.Exit(0)
}
tempDirBase := getTempDirBase()
tempDir, err := os.MkdirTemp(tempDirBase, "lazygit-*")
if err != nil {
if os.IsPermission(err) {
log.Fatalf("Your temp directory (%s) is not writeable. Try if rebooting your machine fixes this.", tempDirBase)
if cliArgs.WorkTree != "" {
if err := os.Chdir(cliArgs.WorkTree); err != nil {
log.Fatal(err.Error())
}
}
tempDir, err := os.MkdirTemp("", "lazygit-*")
if err != nil {
log.Fatal(err.Error())
}
defer os.RemoveAll(tempDir)
@@ -143,15 +134,6 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
if integrationTest != nil {
integrationTest.SetupConfig(appConfig)
// Set this to true so that integration tests don't have to explicitly deal with the hunk
// staging hint:
appConfig.GetAppState().DidShowHunkStagingHint = true
// Preserve the changes that the test setup just made to the config, so
// they don't get lost when we reload the config while running the test
// (which happens when switching between repos, going in and out of
// submodules, etc).
appConfig.SaveGlobalUserConfig()
}
common, err := NewCommon(appConfig)
@@ -164,17 +146,9 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
return
}
if cliArgs.Profile {
go func() {
if err := http.ListenAndServe("localhost:6060", nil); err != nil {
log.Fatal(err)
}
}()
}
parsedGitArg := parseGitArg(cliArgs.GitArg)
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, cliArgs.ScreenMode, integrationTest))
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest))
}
func parseCliArgsAndEnvVars() *cliArgs {
@@ -198,9 +172,6 @@ func parseCliArgsAndEnvVars() *cliArgs {
tailLogs := false
flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)")
profile := false
flaggy.Bool(&profile, "", "profile", "Start the profiler and serve it on http port 6060. See CONTRIBUTING.md for more info.")
printDefaultConfig := false
flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config")
@@ -210,18 +181,15 @@ func parseCliArgsAndEnvVars() *cliArgs {
useConfigDir := ""
flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory")
workTree := os.Getenv("GIT_WORK_TREE")
workTree := ""
flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
gitDir := os.Getenv("GIT_DIR")
gitDir := ""
flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
customConfigFile := ""
flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)")
screenMode := ""
flaggy.String(&screenMode, "sm", "screen-mode", "The initial screen-mode, which determines the size of the focused panel. Valid options: 'normal' (default), 'half', 'full'")
flaggy.Parse()
if os.Getenv("DEBUG") == "TRUE" {
@@ -235,14 +203,12 @@ func parseCliArgsAndEnvVars() *cliArgs {
PrintVersionInfo: printVersionInfo,
Debug: debug,
TailLogs: tailLogs,
Profile: profile,
PrintDefaultConfig: printDefaultConfig,
PrintConfigDir: printConfigDir,
UseConfigDir: useConfigDir,
WorkTree: workTree,
GitDir: gitDir,
CustomConfigFile: customConfigFile,
ScreenMode: screenMode,
}
}
@@ -298,7 +264,7 @@ func mergeBuildInfo(buildInfo *BuildInfo) {
buildInfo.Commit = revision.Value
// if lazygit was built from source we'll show the version as the
// abbreviated commit hash
buildInfo.Version = utils.ShortHash(revision.Value)
buildInfo.Version = utils.ShortSha(revision.Value)
}
// if version hasn't been set we assume that neither has the date
@@ -311,24 +277,8 @@ func mergeBuildInfo(buildInfo *BuildInfo) {
}
func getGitVersionInfo() string {
cmd := exec.Command("git", "--version")
cmd := secureexec.Command("git", "--version")
stdout, _ := cmd.Output()
gitVersion := strings.Trim(strings.TrimPrefix(string(stdout), "git version "), " \r\n")
return gitVersion
}
func getTempDirBase() string {
tempDir := os.TempDir()
user, err := user.Current()
if err != nil || user.Uid == "" {
return tempDir
}
tmpDirBase := filepath.Join(tempDir, "lazygit-"+user.Uid)
if err := os.MkdirAll(tmpDirBase, 0o700); err != nil {
return tempDir
}
return tmpDirBase
}
+4 -8
View File
@@ -3,8 +3,8 @@ package app
import (
"strings"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/samber/lo"
)
type errorMapping struct {
@@ -16,9 +16,9 @@ type errorMapping struct {
func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
errorMessage := err.Error()
knownErrorMessages := []string{minGitVersionErrorMessage(tr)}
knownErrorMessages := []string{tr.MinGitVersionError}
if lo.Contains(knownErrorMessages, errorMessage) {
if slices.Contains(knownErrorMessages, errorMessage) {
return errorMessage, true
}
@@ -27,13 +27,9 @@ func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
originalError: "fatal: not a git repository",
newError: tr.NotARepository,
},
{
originalError: "getwd: no such file or directory",
newError: tr.WorkingDirectoryDoesNotExist,
},
}
if mapping, ok := lo.Find(mappings, func(mapping errorMapping) bool {
if mapping, ok := slices.Find(mappings, func(mapping errorMapping) bool {
return strings.Contains(errorMessage, mapping.originalError)
}); ok {
return mapping.newError, true
+3 -6
View File
@@ -6,14 +6,12 @@ import (
// StartArgs is the struct that represents some things we want to do on program start
type StartArgs struct {
// FilterPath determines which path we're going to filter on so that we only see commits from that file.
FilterPath string
// GitArg determines what context we open in
GitArg GitArg
// integration test (only relevant when invoking lazygit in the context of an integration test)
IntegrationTest integrationTypes.IntegrationTest
// FilterPath determines which path we're going to filter on so that we only see commits from that file.
FilterPath string
// ScreenMode determines the initial Screen Mode (normal, half or full) to use
ScreenMode string
}
type GitArg string
@@ -26,11 +24,10 @@ const (
GitArgStash GitArg = "stash"
)
func NewStartArgs(filterPath string, gitArg GitArg, screenMode string, test integrationTypes.IntegrationTest) StartArgs {
func NewStartArgs(filterPath string, gitArg GitArg, test integrationTypes.IntegrationTest) StartArgs {
return StartArgs{
FilterPath: filterPath,
GitArg: gitArg,
ScreenMode: screenMode,
IntegrationTest: test,
}
}
+77
View File
@@ -0,0 +1,77 @@
package cheatsheet
import (
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"regexp"
"github.com/pmezard/go-difflib/difflib"
)
func Check() {
dir := GetKeybindingsDir()
tmpDir := filepath.Join(os.TempDir(), "lazygit_cheatsheet")
err := os.RemoveAll(tmpDir)
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
err = os.Mkdir(tmpDir, 0o700)
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
generateAtDir(tmpDir)
defer os.RemoveAll(tmpDir)
actualContent := obtainContent(dir)
expectedContent := obtainContent(tmpDir)
if expectedContent == "" {
log.Fatal("empty expected content")
}
if actualContent != expectedContent {
err := difflib.WriteUnifiedDiff(os.Stdout, difflib.UnifiedDiff{
A: difflib.SplitLines(expectedContent),
B: difflib.SplitLines(actualContent),
FromFile: "Expected",
FromDate: "",
ToFile: "Actual",
ToDate: "",
Context: 1,
})
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
fmt.Printf("\nCheatsheets are out of date. Please run `%s` at the project root and commit the changes. If you run the script and no keybindings files are updated as a result, try rebasing onto master and trying again.\n", CommandToRun())
os.Exit(1)
}
fmt.Println("\nCheatsheets are up to date")
}
func obtainContent(dir string) string {
re := regexp.MustCompile(`Keybindings_\w+\.md$`)
content := ""
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if re.MatchString(path) {
bytes, err := os.ReadFile(path)
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
content += fmt.Sprintf("\n%s\n\n", filepath.Base(path))
content += string(bytes)
}
return nil
})
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
return content
}
+26 -42
View File
@@ -1,24 +1,21 @@
//go:generate go run generator.go
// This "script" generates files called Keybindings_{{.LANG}}.md
// in the docs/keybindings directory.
// This "script" generates a file called Keybindings_{{.LANG}}.md
// in current working directory.
//
// The content of these generated files is a keybindings cheatsheet.
// The content of this generated file is a keybindings cheatsheet.
//
// To generate the cheatsheets, run:
// go generate pkg/cheatsheet/generate.go
// To generate cheatsheet in english run:
// go run scripts/generate_cheatsheet.go
package cheatsheet
import (
"cmp"
"fmt"
"log"
"os"
"slices"
"strings"
"github.com/jesseduffield/generics/maps"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazycore/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/app"
"github.com/jesseduffield/lazygit/pkg/config"
@@ -45,7 +42,7 @@ type headerWithBindings struct {
}
func CommandToRun() string {
return "go generate ./..."
return "go run scripts/cheatsheet/main.go generate"
}
func GetKeybindingsDir() string {
@@ -53,10 +50,7 @@ func GetKeybindingsDir() string {
}
func generateAtDir(cheatsheetDir string) {
translationSetsByLang, err := i18n.GetTranslationSets()
if err != nil {
log.Fatal(err)
}
translationSetsByLang := i18n.GetTranslationSets()
mConfig := config.NewDummyAppConfig()
for lang := range translationSetsByLang {
@@ -65,12 +59,7 @@ func generateAtDir(cheatsheetDir string) {
if err != nil {
log.Fatal(err)
}
tr, err := i18n.NewTranslationSetFromConfig(common.Log, lang)
if err != nil {
log.Fatal(err)
}
common.Tr = tr
mApp, _ := app.NewApp(mConfig, nil, common)
mApp, _ := app.NewApp(mConfig, common)
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
file, err := os.Create(path)
if err != nil {
@@ -112,11 +101,10 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
"reflogCommits": tr.ReflogCommitsTitle,
"tags": tr.TagsTitle,
"commitFiles": tr.CommitFilesTitle,
"commitMessage": tr.CommitSummaryTitle,
"commitMessage": tr.CommitMessageTitle,
"commitDescription": tr.CommitDescriptionTitle,
"commits": tr.CommitsTitle,
"confirmation": tr.ConfirmationTitle,
"prompt": tr.PromptTitle,
"information": tr.InformationTitle,
"main": tr.NormalTitle,
"patchBuilding": tr.PatchBuildingTitle,
@@ -141,7 +129,7 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*bindingSection {
excludedViews := []string{"stagingSecondary", "patchBuildingSecondary"}
bindingsToDisplay := lo.Filter(bindings, func(binding *types.Binding, _ int) bool {
bindingsToDisplay := slices.Filter(bindings, func(binding *types.Binding) bool {
if lo.Contains(excludedViews, binding.ViewName) {
return false
}
@@ -167,14 +155,14 @@ func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*b
},
)
slices.SortFunc(bindingGroups, func(a, b headerWithBindings) int {
slices.SortFunc(bindingGroups, func(a, b headerWithBindings) bool {
if a.header.priority != b.header.priority {
return cmp.Compare(b.header.priority, a.header.priority)
return a.header.priority > b.header.priority
}
return strings.Compare(a.header.title, b.header.title)
return a.header.title < b.header.title
})
return lo.Map(bindingGroups, func(hb headerWithBindings, _ int) *bindingSection {
return slices.Map(bindingGroups, func(hb headerWithBindings) *bindingSection {
return &bindingSection{
title: hb.header.title,
bindings: hb.bindings,
@@ -201,11 +189,11 @@ func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection)
for _, section := range bindingSections {
content += formatTitle(section.title)
content += "| Key | Action | Info |\n"
content += "|-----|--------|-------------|\n"
content += "<pre>\n"
for _, binding := range section.bindings {
content += formatBinding(binding)
}
content += "</pre>\n"
}
return content
@@ -216,23 +204,19 @@ func formatTitle(title string) string {
}
func formatBinding(binding *types.Binding) string {
action := keybindings.LabelFromKey(binding.Key)
description := binding.Description
result := fmt.Sprintf(" <kbd>%s</kbd>: %s", escapeAngleBrackets(keybindings.LabelFromKey(binding.Key)), binding.Description)
if binding.Alternative != "" {
action += fmt.Sprintf(" (%s)", binding.Alternative)
result += fmt.Sprintf(" (%s)", binding.Alternative)
}
result += "\n"
// Replace newlines with <br> tags for proper markdown table formatting
tooltip := strings.ReplaceAll(binding.Tooltip, "\n", "<br>")
return result
}
// Escape pipe characters to avoid breaking the table format
action = strings.ReplaceAll(action, `|`, `\|`)
description = strings.ReplaceAll(description, `|`, `\|`)
tooltip = strings.ReplaceAll(tooltip, `|`, `\|`)
// Use backticks for keyboard keys. Two backticks are needed with an inner space
// to escape a key that is itself a backtick.
return fmt.Sprintf("| `` %s `` | %s | %s |\n", action, description, tooltip)
func escapeAngleBrackets(str string) string {
result := strings.ReplaceAll(str, ">", "&gt;")
result = strings.ReplaceAll(result, "<", "&lt;")
return result
}
func italicize(str string) string {
+1 -1
View File
@@ -262,7 +262,7 @@ func TestGetBindingSections(t *testing.T) {
for _, test := range tests {
t.Run(test.testName, func(t *testing.T) {
actual := getBindingSections(test.bindings, tr)
actual := getBindingSections(test.bindings, &tr)
assert.EqualValues(t, test.expected, actual)
})
}
-14
View File
@@ -1,14 +0,0 @@
//go:build ignore
package main
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/cheatsheet"
)
func main() {
fmt.Printf("Generating cheatsheets in %s...\n", cheatsheet.GetKeybindingsDir())
cheatsheet.Generate()
}
+131 -30
View File
@@ -2,9 +2,11 @@ package commands
import (
"os"
"path/filepath"
"strings"
"github.com/go-errors/errors"
"github.com/sasha-s/go-deadlock"
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
@@ -12,13 +14,12 @@ import (
"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/config"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// GitCommand is our main git interface
type GitCommand struct {
Blame *git_commands.BlameCommands
Branch *git_commands.BranchCommands
Commit *git_commands.CommitCommands
Config *git_commands.ConfigCommands
@@ -38,7 +39,6 @@ type GitCommand struct {
Bisect *git_commands.BisectCommands
Worktree *git_commands.WorktreeCommands
Version *git_commands.GitVersion
RepoPaths *git_commands.RepoPaths
Loaders Loaders
}
@@ -60,26 +60,19 @@ func NewGitCommand(
version *git_commands.GitVersion,
osCommand *oscommands.OSCommand,
gitConfig git_config.IGitConfig,
pagerConfig *config.PagerConfig,
syncMutex *deadlock.Mutex,
) (*GitCommand, error) {
repoPaths, err := git_commands.GetRepoPaths(osCommand.Cmd, version)
if err != nil {
return nil, errors.Errorf("Error getting repo paths: %v", err)
if err := navigateToRepoRootDirectory(os.Stat, os.Chdir); err != nil {
return nil, err
}
err = os.Chdir(repoPaths.WorktreePath())
repo, err := setupRepository(gogit.PlainOpenWithOptions, gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true}, cmn.Tr.GitconfigParseErr)
if err != nil {
return nil, utils.WrapError(err)
return nil, err
}
repository, err := gogit.PlainOpenWithOptions(
repoPaths.WorktreeGitDirPath(),
&gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true},
)
dotGitDir, err := findDotGitDir(os.Stat, os.ReadFile)
if err != nil {
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
return nil, errors.New(cmn.Tr.GitconfigParseErr)
}
return nil, err
}
@@ -88,9 +81,9 @@ func NewGitCommand(
version,
osCommand,
gitConfig,
repoPaths,
repository,
pagerConfig,
dotGitDir,
repo,
syncMutex,
), nil
}
@@ -99,9 +92,9 @@ func NewGitCommandAux(
version *git_commands.GitVersion,
osCommand *oscommands.OSCommand,
gitConfig git_config.IGitConfig,
repoPaths *git_commands.RepoPaths,
dotGitDir string,
repo *gogit.Repository,
pagerConfig *config.PagerConfig,
syncMutex *deadlock.Mutex,
) *GitCommand {
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd)
@@ -112,9 +105,9 @@ func NewGitCommandAux(
// common ones are: cmn, osCommand, dotGitDir, configCommands
configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo)
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands, pagerConfig)
fileLoader := git_commands.NewFileLoader(cmn, cmd, configCommands)
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, dotGitDir, repo, configCommands, syncMutex)
statusCommands := git_commands.NewStatusCommands(gitCommon)
flowCommands := git_commands.NewFlowCommands(gitCommon)
remoteCommands := git_commands.NewRemoteCommands(gitCommon)
@@ -131,24 +124,24 @@ func NewGitCommandAux(
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
patchBuilder := patch.NewPatchBuilder(cmn.Log,
func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, plain)
// TODO: make patch builder take Gui.IgnoreWhitespaceInDiffView into
// account. For now we just pass false.
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, plain, false)
})
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
bisectCommands := git_commands.NewBisectCommands(gitCommon)
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
blameCommands := git_commands.NewBlameCommands(gitCommon)
branchLoader := git_commands.NewBranchLoader(cmn, gitCommon, cmd, branchCommands.CurrentBranchInfo, configCommands)
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.WorkingTreeState, gitCommon)
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode, gitCommon)
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
worktreeLoader := git_commands.NewWorktreeLoader(gitCommon)
worktreeLoader := git_commands.NewWorktreeLoader(cmn, cmd)
stashLoader := git_commands.NewStashLoader(cmn, cmd)
tagLoader := git_commands.NewTagLoader(cmn, cmd)
return &GitCommand{
Blame: blameCommands,
Branch: branchCommands,
Commit: commitCommands,
Config: configCommands,
@@ -179,10 +172,118 @@ func NewGitCommandAux(
StashLoader: stashLoader,
TagLoader: tagLoader,
},
RepoPaths: repoPaths,
}
}
func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error {
gitDir := env.GetGitDirEnv()
if gitDir != "" {
// we've been given the git directory explicitly so no need to navigate to it
_, err := stat(gitDir)
if err != nil {
return utils.WrapError(err)
}
return nil
}
// we haven't been given the git dir explicitly so we assume it's in the current working directory as `.git/` (or an ancestor directory)
for {
_, err := stat(".git")
if err == nil {
return nil
}
if !os.IsNotExist(err) {
return utils.WrapError(err)
}
if err = chdir(".."); err != nil {
return utils.WrapError(err)
}
currentPath, err := os.Getwd()
if err != nil {
return err
}
atRoot := currentPath == filepath.Dir(currentPath)
if atRoot {
// we should never really land here: the code that creates GitCommand should
// verify we're in a git directory
return errors.New("Must open lazygit in a git repository")
}
}
}
// resolvePath takes a path containing a symlink and returns the true path
func resolvePath(path string) (string, error) {
l, err := os.Lstat(path)
if err != nil {
return "", err
}
if l.Mode()&os.ModeSymlink == 0 {
return path, nil
}
return filepath.EvalSymlinks(path)
}
func setupRepository(openGitRepository func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error), options gogit.PlainOpenOptions, gitConfigParseErrorStr string) (*gogit.Repository, error) {
unresolvedPath := env.GetGitDirEnv()
if unresolvedPath == "" {
var err error
unresolvedPath, err = os.Getwd()
if err != nil {
return nil, err
}
}
path, err := resolvePath(unresolvedPath)
if err != nil {
return nil, err
}
repository, err := openGitRepository(path, &options)
if err != nil {
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
return nil, errors.New(gitConfigParseErrorStr)
}
return nil, err
}
return repository, err
}
func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) {
if env.GetGitDirEnv() != "" {
return env.GetGitDirEnv(), nil
}
f, err := stat(".git")
if err != nil {
return "", err
}
if f.IsDir() {
return ".git", nil
}
fileBytes, err := readFile(".git")
if err != nil {
return "", err
}
fileContent := string(fileBytes)
if !strings.HasPrefix(fileContent, "gitdir: ") {
return "", errors.New(".git is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory")
}
return strings.TrimSpace(strings.TrimPrefix(fileContent, "gitdir: ")), nil
}
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
}
+3 -3
View File
@@ -30,12 +30,12 @@ func NewGitCmdObjBuilder(log *logrus.Entry, innerBuilder *oscommands.CmdObjBuild
var defaultEnvVar = "GIT_OPTIONAL_LOCKS=0"
func (self *gitCmdObjBuilder) New(args []string) *oscommands.CmdObj {
func (self *gitCmdObjBuilder) New(args []string) oscommands.ICmdObj {
return self.innerBuilder.New(args).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) NewShell(cmdStr string, shellFunctionsFile string) *oscommands.CmdObj {
return self.innerBuilder.NewShell(cmdStr, shellFunctionsFile).AddEnvVars(defaultEnvVar)
func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) Quote(str string) string {
+6 -6
View File
@@ -20,15 +20,15 @@ type gitCmdObjRunner struct {
innerRunner oscommands.ICmdObjRunner
}
func (self *gitCmdObjRunner) Run(cmdObj *oscommands.CmdObj) error {
func (self *gitCmdObjRunner) Run(cmdObj oscommands.ICmdObj) error {
_, err := self.RunWithOutput(cmdObj)
return err
}
func (self *gitCmdObjRunner) RunWithOutput(cmdObj *oscommands.CmdObj) (string, error) {
func (self *gitCmdObjRunner) RunWithOutput(cmdObj oscommands.ICmdObj) (string, error) {
var output string
var err error
for range RetryCount {
for i := 0; i < RetryCount; i++ {
newCmdObj := cmdObj.Clone()
output, err = self.innerRunner.RunWithOutput(newCmdObj)
@@ -44,10 +44,10 @@ func (self *gitCmdObjRunner) RunWithOutput(cmdObj *oscommands.CmdObj) (string, e
return output, err
}
func (self *gitCmdObjRunner) RunWithOutputs(cmdObj *oscommands.CmdObj) (string, string, error) {
func (self *gitCmdObjRunner) RunWithOutputs(cmdObj oscommands.ICmdObj) (string, string, error) {
var stdout, stderr string
var err error
for range RetryCount {
for i := 0; i < RetryCount; i++ {
newCmdObj := cmdObj.Clone()
stdout, stderr, err = self.innerRunner.RunWithOutputs(newCmdObj)
@@ -64,6 +64,6 @@ func (self *gitCmdObjRunner) RunWithOutputs(cmdObj *oscommands.CmdObj) (string,
}
// Retry logic not implemented here, but these commands typically don't need to obtain a lock.
func (self *gitCmdObjRunner) RunAndProcessLines(cmdObj *oscommands.CmdObj, 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)
}
+16 -29
View File
@@ -19,16 +19,12 @@ func NewBisectCommands(gitCommon *GitCommon) *BisectCommands {
// This command is pretty cheap to run so we're not storing the result anywhere.
// But if it becomes problematic we can chang that.
func (self *BisectCommands) GetInfo() *BisectInfo {
return self.GetInfoForGitDir(self.repoPaths.WorktreeGitDirPath())
}
func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
var err error
info := &BisectInfo{started: false, log: self.Log, newTerm: "bad", oldTerm: "good"}
// we return nil if we're not in a git bisect session.
// we know we're in a session by the presence of a .git/BISECT_START file
bisectStartPath := filepath.Join(gitDir, "BISECT_START")
bisectStartPath := filepath.Join(self.dotGitDir, "BISECT_START")
exists, err := self.os.FileExists(bisectStartPath)
if err != nil {
self.Log.Infof("error getting git bisect info: %s", err.Error())
@@ -48,7 +44,7 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
info.started = true
info.start = strings.TrimSpace(string(startContent))
termsContent, err := os.ReadFile(filepath.Join(gitDir, "BISECT_TERMS"))
termsContent, err := os.ReadFile(filepath.Join(self.dotGitDir, "BISECT_TERMS"))
if err != nil {
// old git versions won't have this file so we default to bad/good
} else {
@@ -57,7 +53,7 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
info.oldTerm = splitContent[1]
}
bisectRefsDir := filepath.Join(gitDir, "refs", "bisect")
bisectRefsDir := filepath.Join(self.dotGitDir, "refs", "bisect")
files, err := os.ReadDir(bisectRefsDir)
if err != nil {
self.Log.Infof("error getting git bisect info: %s", err.Error())
@@ -76,7 +72,7 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
return info
}
hash := strings.TrimSpace(string(fileContent))
sha := strings.TrimSpace(string(fileContent))
if name == info.newTerm {
status = BisectStatusNew
@@ -86,16 +82,16 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
status = BisectStatusSkipped
}
info.statusMap[hash] = status
info.statusMap[sha] = status
}
currentContent, err := os.ReadFile(filepath.Join(gitDir, "BISECT_EXPECTED_REV"))
currentContent, err := os.ReadFile(filepath.Join(self.dotGitDir, "BISECT_EXPECTED_REV"))
if err != nil {
self.Log.Infof("error getting git bisect info: %s", err.Error())
return info
}
currentHash := strings.TrimSpace(string(currentContent))
info.current = currentHash
currentSha := strings.TrimSpace(string(currentContent))
info.current = currentSha
return info
}
@@ -125,17 +121,8 @@ func (self *BisectCommands) Start() error {
return self.cmd.New(cmdArgs).StreamOutput().Run()
}
func (self *BisectCommands) StartWithTerms(oldTerm string, newTerm string) error {
cmdArgs := NewGitCmd("bisect").Arg("start").
Arg("--term-old=" + oldTerm).
Arg("--term-new=" + newTerm).
ToArgv()
return self.cmd.New(cmdArgs).StreamOutput().Run()
}
// tells us whether we've found our problem commit(s). We return a string slice of
// commit hashes if we're done, and that slice may have more that one item if
// commit sha's if we're done, and that slice may have more that one item if
// skipped commits are involved.
func (self *BisectCommands) IsDone() (bool, []string, error) {
info := self.GetInfo()
@@ -143,8 +130,8 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
return false, nil, nil
}
newHash := info.GetNewHash()
if newHash == "" {
newSha := info.GetNewSha()
if newSha == "" {
return false, nil, nil
}
@@ -153,14 +140,14 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
done := false
candidates := []string{}
cmdArgs := NewGitCmd("rev-list").Arg(newHash).ToArgv()
cmdArgs := NewGitCmd("rev-list").Arg(newSha).ToArgv()
err := self.cmd.New(cmdArgs).RunAndProcessLines(func(line string) (bool, error) {
hash := strings.TrimSpace(line)
sha := strings.TrimSpace(line)
if status, ok := info.statusMap[hash]; ok {
if status, ok := info.statusMap[sha]; ok {
switch status {
case BisectStatusSkipped, BisectStatusNew:
candidates = append(candidates, hash)
candidates = append(candidates, sha)
return false, nil
case BisectStatusOld:
done = true
@@ -185,7 +172,7 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
// render the commits from the bad commit.
func (self *BisectCommands) ReachableFromStart(bisectInfo *BisectInfo) bool {
cmdArgs := NewGitCmd("merge-base").
Arg("--is-ancestor", bisectInfo.GetNewHash(), bisectInfo.GetStartHash()).
Arg("--is-ancestor", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()).
ToArgv()
err := self.cmd.New(cmdArgs).DontLog().Run()
+12 -12
View File
@@ -2,7 +2,7 @@ package git_commands
import (
"github.com/jesseduffield/generics/maps"
"github.com/samber/lo"
"github.com/jesseduffield/generics/slices"
"github.com/sirupsen/logrus"
)
@@ -29,10 +29,10 @@ type BisectInfo struct {
newTerm string // 'bad' by default
oldTerm string // 'good' by default
// map of commit hashes to their status
// map of commit sha's to their status
statusMap map[string]BisectStatus
// the hash of the commit that's under test
// the sha of the commit that's under test
current string
}
@@ -49,26 +49,26 @@ func NewNullBisectInfo() *BisectInfo {
return &BisectInfo{started: false}
}
func (self *BisectInfo) GetNewHash() string {
for hash, status := range self.statusMap {
func (self *BisectInfo) GetNewSha() string {
for sha, status := range self.statusMap {
if status == BisectStatusNew {
return hash
return sha
}
}
return ""
}
func (self *BisectInfo) GetCurrentHash() string {
func (self *BisectInfo) GetCurrentSha() string {
return self.current
}
func (self *BisectInfo) GetStartHash() string {
func (self *BisectInfo) GetStartSha() string {
return self.start
}
func (self *BisectInfo) Status(commitHash string) (BisectStatus, bool) {
status, ok := self.statusMap[commitHash]
func (self *BisectInfo) Status(commitSha string) (BisectStatus, bool) {
status, ok := self.statusMap[commitSha]
return status, ok
}
@@ -93,9 +93,9 @@ func (self *BisectInfo) Bisecting() bool {
return false
}
if self.GetNewHash() == "" {
if self.GetNewSha() == "" {
return false
}
return lo.Contains(maps.Values(self.statusMap), BisectStatusOld)
return slices.Contains(maps.Values(self.statusMap), BisectStatusOld)
}
-33
View File
@@ -1,33 +0,0 @@
package git_commands
import (
"fmt"
)
type BlameCommands struct {
*GitCommon
}
func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
return &BlameCommands{
GitCommon: gitCommon,
}
}
// Blame a range of lines. For each line, output the hash of the commit where
// the line last changed, then a space, then a description of the commit (author
// and date), another space, and then the line. For example:
//
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 11) func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 12) return &BlameCommands{
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 13) GitCommon: gitCommon,
func (self *BlameCommands) BlameLineRange(filename string, commit string, firstLine int, numLines int) (string, error) {
cmdArgs := NewGitCmd("blame").
Arg("-l").
Arg(fmt.Sprintf("-L%d,+%d", firstLine, numLines)).
Arg(commit).
Arg("--").
Arg(filename)
return self.cmd.New(cmdArgs.ToArgv()).RunWithOutput()
}
+15 -163
View File
@@ -4,16 +4,13 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/mgutz/str"
"github.com/samber/lo"
)
type BranchCommands struct {
*GitCommon
allBranchesLogCmdIndex int // keeps track of current all branches log command
}
func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
@@ -31,35 +28,6 @@ func (self *BranchCommands) New(name string, base string) error {
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) NewWithoutTracking(name string, base string) error {
cmdArgs := NewGitCmd("checkout").
Arg("-b", name, base).
Arg("--no-track").
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// NewWithoutCheckout creates a new branch without checking it out
func (self *BranchCommands) NewWithoutCheckout(name string, base string) error {
cmdArgs := NewGitCmd("branch").
Arg(name, base).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CreateWithUpstream creates a new branch with a given upstream, but without
// checking it out
func (self *BranchCommands) CreateWithUpstream(name string, upstream string) error {
cmdArgs := NewGitCmd("branch").
Arg("--track").
Arg(name, upstream).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CurrentBranchInfo get the current branch information.
func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
branchName, err := self.cmd.New(
@@ -86,10 +54,10 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
for _, line := range utils.SplitLines(output) {
split := strings.Split(strings.TrimRight(line, "\r\n"), "\x00")
if len(split) == 3 && split[0] == "*" {
hash := split[1]
sha := split[1]
displayName := split[2]
return BranchInfo{
RefName: hash,
RefName: sha,
DisplayName: displayName,
DetachedHead: true,
}, nil
@@ -102,41 +70,11 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
}, nil
}
// CurrentBranchName get name of current branch. Returns empty string if HEAD is detached.
func (self *BranchCommands) CurrentBranchName() (string, error) {
cmdArgs := NewGitCmd("branch").
Arg("--show-current").
ToArgv()
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return "", err
}
return strings.TrimSpace(output), nil
}
// Gets the full ref name of the previously checked out branch. Can return an empty string (but no
// error) e.g. when the previously checked out thing was a detached head.
func (self *BranchCommands) PreviousRef() (string, error) {
cmdArgs := NewGitCmd("rev-parse").
Arg("--symbolic-full-name").
Arg("@{-1}").
ToArgv()
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return "", err
}
return strings.TrimSpace(output), nil
}
// LocalDelete delete branch locally
func (self *BranchCommands) LocalDelete(branches []string, force bool) error {
// Delete delete branch
func (self *BranchCommands) Delete(branch string, force bool) error {
cmdArgs := NewGitCmd("branch").
ArgIfElse(force, "-D", "-d").
Arg(branches...).
Arg(branch).
ToArgv()
return self.cmd.New(cmdArgs).Run()
@@ -169,8 +107,8 @@ func (self *BranchCommands) GetGraph(branchName string) (string, error) {
return self.GetGraphCmdObj(branchName).DontLog().RunWithOutput()
}
func (self *BranchCommands) GetGraphCmdObj(branchName string) *oscommands.CmdObj {
branchLogCmdTemplate := self.UserConfig().Git.BranchLogCmd
func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj {
branchLogCmdTemplate := self.UserConfig.Git.BranchLogCmd
templateValues := map[string]string{
"branchName": self.cmd.Quote(branchName),
}
@@ -250,107 +188,21 @@ func (self *BranchCommands) Rename(oldName string, newName string) error {
return self.cmd.New(cmdArgs).Run()
}
type MergeVariant int
const (
MERGE_VARIANT_REGULAR MergeVariant = iota
MERGE_VARIANT_FAST_FORWARD
MERGE_VARIANT_NON_FAST_FORWARD
MERGE_VARIANT_SQUASH
)
func (self *BranchCommands) Merge(branchName string, variant MergeVariant) error {
extraArgs := func() []string {
switch variant {
case MERGE_VARIANT_REGULAR:
return []string{}
case MERGE_VARIANT_FAST_FORWARD:
return []string{"--ff"}
case MERGE_VARIANT_NON_FAST_FORWARD:
return []string{"--no-ff"}
case MERGE_VARIANT_SQUASH:
return []string{"--squash", "--ff"}
}
panic("shouldn't get here")
}()
type MergeOpts struct {
FastForwardOnly bool
}
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
cmdArgs := NewGitCmd("merge").
Arg("--no-edit").
Arg(strings.Fields(self.UserConfig().Git.Merging.Args)...).
Arg(extraArgs...).
ArgIf(self.UserConfig.Git.Merging.Args != "", self.UserConfig.Git.Merging.Args).
ArgIf(opts.FastForwardOnly, "--ff-only").
Arg(branchName).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// Returns whether refName can be fast-forward merged into the current branch
func (self *BranchCommands) CanDoFastForwardMerge(refName string) bool {
cmdArgs := NewGitCmd("merge-base").
Arg("--is-ancestor").
Arg("HEAD", refName).
ToArgv()
err := self.cmd.New(cmdArgs).DontLog().Run()
return err == nil
}
// Only choose between non-empty, non-identical commands
func (self *BranchCommands) allBranchesLogCandidates() []string {
return lo.Uniq(lo.WithoutEmpty(self.UserConfig().Git.AllBranchesLogCmds))
}
func (self *BranchCommands) AllBranchesLogCmdObj() *oscommands.CmdObj {
candidates := self.allBranchesLogCandidates()
if self.allBranchesLogCmdIndex >= len(candidates) {
self.allBranchesLogCmdIndex = 0
}
i := self.allBranchesLogCmdIndex
return self.cmd.New(str.ToArgv(candidates[i])).DontLog()
}
func (self *BranchCommands) RotateAllBranchesLogIdx() {
n := len(self.allBranchesLogCandidates())
i := self.allBranchesLogCmdIndex
self.allBranchesLogCmdIndex = (i + 1) % n
}
func (self *BranchCommands) GetAllBranchesLogIdxAndCount() (int, int) {
n := len(self.allBranchesLogCandidates())
i := self.allBranchesLogCmdIndex
return i, n
}
func (self *BranchCommands) IsBranchMerged(branch *models.Branch, mainBranches *MainBranches) (bool, error) {
branchesToCheckAgainst := []string{"HEAD"}
if branch.RemoteBranchStoredLocally() {
branchesToCheckAgainst = append(branchesToCheckAgainst, fmt.Sprintf("%s@{upstream}", branch.Name))
}
branchesToCheckAgainst = append(branchesToCheckAgainst, mainBranches.Get()...)
cmdArgs := NewGitCmd("rev-list").
Arg("--max-count=1").
Arg(branch.Name).
Arg(lo.Map(branchesToCheckAgainst, func(branch string, _ int) string {
return fmt.Sprintf("^%s", branch)
})...).
Arg("--").
ToArgv()
stdout, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
if err != nil {
return false, err
}
return stdout == "", nil
}
func (self *BranchCommands) UpdateBranchRefs(updateCommands string) error {
cmdArgs := NewGitCmd("update-ref").
Arg("--stdin").
ToArgv()
return self.cmd.New(cmdArgs).SetStdin(updateCommands).Run()
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
return self.cmd.New(str.ToArgv(self.UserConfig.Git.AllBranchesLogCmd)).DontLog()
}
+41 -182
View File
@@ -3,19 +3,16 @@ package git_commands
import (
"fmt"
"regexp"
"slices"
"strconv"
"strings"
"time"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/go-git/v5/config"
"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/utils"
"github.com/samber/lo"
"golang.org/x/sync/errgroup"
)
// context:
@@ -42,7 +39,6 @@ type BranchInfo struct {
// BranchLoader returns a list of Branch objects for the current repo
type BranchLoader struct {
*common.Common
*GitCommon
cmd oscommands.ICmdObjBuilder
getCurrentBranchInfo func() (BranchInfo, error)
config BranchLoaderConfigCommands
@@ -50,14 +46,12 @@ type BranchLoader struct {
func NewBranchLoader(
cmn *common.Common,
gitCommon *GitCommon,
cmd oscommands.ICmdObjBuilder,
getCurrentBranchInfo func() (BranchInfo, error),
config BranchLoaderConfigCommands,
) *BranchLoader {
return &BranchLoader{
Common: cmn,
GitCommon: gitCommon,
cmd: cmd,
getCurrentBranchInfo: getCurrentBranchInfo,
config: config,
@@ -65,49 +59,36 @@ func NewBranchLoader(
}
// Load the list of branches for the current repo
func (self *BranchLoader) Load(reflogCommits []*models.Commit,
mainBranches *MainBranches,
oldBranches []*models.Branch,
loadBehindCounts bool,
onWorker func(func() error),
renderFunc func(),
) ([]*models.Branch, error) {
func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch, error) {
branches := self.obtainBranches()
if self.UserConfig().Git.LocalBranchSortOrder == "recency" {
reflogBranches := self.obtainReflogBranches(reflogCommits)
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
branchesWithRecency := make([]*models.Branch, 0)
outer:
for _, reflogBranch := range reflogBranches {
for j, branch := range branches {
if branch.Head {
continue
}
if strings.EqualFold(reflogBranch.Name, branch.Name) {
branch.Recency = reflogBranch.Recency
branchesWithRecency = append(branchesWithRecency, branch)
branches = utils.Remove(branches, j)
continue outer
}
reflogBranches := self.obtainReflogBranches(reflogCommits)
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
branchesWithRecency := make([]*models.Branch, 0)
outer:
for _, reflogBranch := range reflogBranches {
for j, branch := range branches {
if branch.Head {
continue
}
if strings.EqualFold(reflogBranch.Name, branch.Name) {
branch.Recency = reflogBranch.Recency
branchesWithRecency = append(branchesWithRecency, branch)
branches = slices.Remove(branches, j)
continue outer
}
}
// Sort branches that don't have a recency value alphabetically
// (we're really doing this for the sake of deterministic behaviour across git versions)
slices.SortFunc(branches, func(a *models.Branch, b *models.Branch) int {
return strings.Compare(a.Name, b.Name)
})
branches = utils.Prepend(branches, branchesWithRecency...)
}
branches = slices.Prepend(branches, branchesWithRecency...)
foundHead := false
for i, branch := range branches {
if branch.Head {
foundHead = true
branch.Recency = " *"
branches = utils.Move(branches, i, 0)
branches = slices.Move(branches, i, 0)
break
}
}
@@ -116,7 +97,7 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit,
if err != nil {
return nil, err
}
branches = utils.Prepend(branches, &models.Branch{Name: info.RefName, DisplayName: info.DisplayName, Head: true, DetachedHead: info.DetachedHead, Recency: " *"})
branches = slices.Prepend(branches, &models.Branch{Name: info.RefName, DisplayName: info.DisplayName, Head: true, DetachedHead: info.DetachedHead, Recency: " *"})
}
configBranches, err := self.config.Branches()
@@ -130,108 +111,11 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit,
branch.UpstreamRemote = match.Remote
branch.UpstreamBranch = match.Merge.Short()
}
// If the branch already existed, take over its BehindBaseBranch value
// to reduce flicker
if oldBranch, found := lo.Find(oldBranches, func(b *models.Branch) bool {
return b.Name == branch.Name
}); found {
branch.BehindBaseBranch.Store(oldBranch.BehindBaseBranch.Load())
}
}
if loadBehindCounts && self.UserConfig().Gui.ShowDivergenceFromBaseBranch != "none" {
onWorker(func() error {
return self.GetBehindBaseBranchValuesForAllBranches(branches, mainBranches, renderFunc)
})
}
return branches, nil
}
func (self *BranchLoader) GetBehindBaseBranchValuesForAllBranches(
branches []*models.Branch,
mainBranches *MainBranches,
renderFunc func(),
) error {
mainBranchRefs := mainBranches.Get()
if len(mainBranchRefs) == 0 {
return nil
}
t := time.Now()
errg := errgroup.Group{}
for _, branch := range branches {
errg.Go(func() error {
baseBranch, err := self.GetBaseBranch(branch, mainBranches)
if err != nil {
return err
}
behind := 0 // prime it in case something below fails
if baseBranch != "" {
output, err := self.cmd.New(
NewGitCmd("rev-list").
Arg("--left-right").
Arg("--count").
Arg(fmt.Sprintf("%s...%s", branch.FullRefName(), baseBranch)).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return err
}
// The format of the output is "<ahead>\t<behind>"
aheadBehindStr := strings.Split(strings.TrimSpace(output), "\t")
if len(aheadBehindStr) == 2 {
if value, err := strconv.Atoi(aheadBehindStr[1]); err == nil {
behind = value
}
}
}
branch.BehindBaseBranch.Store(int32(behind))
return nil
})
}
err := errg.Wait()
self.Log.Debugf("time to get behind base branch values for all branches: %s", time.Since(t))
renderFunc()
return err
}
// Find the base branch for the given branch (i.e. the main branch that the
// given branch was forked off of)
//
// Note that this function may return an empty string even if the returned error
// is nil, e.g. when none of the configured main branches exist. This is not
// considered an error condition, so callers need to check both the returned
// error and whether the returned base branch is empty (and possibly react
// differently in both cases).
func (self *BranchLoader) GetBaseBranch(branch *models.Branch, mainBranches *MainBranches) (string, error) {
mergeBase := mainBranches.GetMergeBase(branch.FullRefName())
if mergeBase == "" {
return "", nil
}
output, err := self.cmd.New(
NewGitCmd("for-each-ref").
Arg("--contains").
Arg(mergeBase).
Arg("--format=%(refname)").
Arg(mainBranches.Get()...).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return "", err
}
trimmedOutput := strings.TrimSpace(output)
split := strings.Split(trimmedOutput, "\n")
if len(split) == 0 || split[0] == "" {
return "", nil
}
return split[0], nil
}
func (self *BranchLoader) obtainBranches() []*models.Branch {
output, err := self.getRawBranches()
if err != nil {
@@ -241,7 +125,7 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
trimmedOutput := strings.TrimSpace(output)
outputLines := strings.Split(trimmedOutput, "\n")
return lo.FilterMap(outputLines, func(line string, _ int) (*models.Branch, bool) {
return slices.FilterMap(outputLines, func(line string) (*models.Branch, bool) {
if line == "" {
return nil, false
}
@@ -254,8 +138,7 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
return nil, false
}
storeCommitDateAsRecency := self.UserConfig().Git.LocalBranchSortOrder != "recency"
return obtainBranch(split, storeCommitDateAsRecency), true
return obtainBranch(split), true
})
}
@@ -267,18 +150,8 @@ func (self *BranchLoader) getRawBranches() (string, error) {
"%00",
)
var sortOrder string
switch strings.ToLower(self.UserConfig().Git.LocalBranchSortOrder) {
case "recency", "date":
sortOrder = "-committerdate"
case "alphabetical":
sortOrder = "refname"
default:
sortOrder = "refname"
}
cmdArgs := NewGitCmd("for-each-ref").
Arg(fmt.Sprintf("--sort=%s", sortOrder)).
Arg("--sort=-committerdate").
Arg(fmt.Sprintf("--format=%s", format)).
Arg("refs/heads").
ToArgv()
@@ -291,45 +164,30 @@ var branchFields = []string{
"refname:short",
"upstream:short",
"upstream:track",
"push:track",
"subject",
"objectname",
"committerdate:unix",
fmt.Sprintf("objectname:short=%d", utils.COMMIT_HASH_SHORT_SIZE),
}
// Obtain branch information from parsed line output of getRawBranches()
func obtainBranch(split []string, storeCommitDateAsRecency bool) *models.Branch {
func obtainBranch(split []string) *models.Branch {
headMarker := split[0]
fullName := split[1]
upstreamName := split[2]
track := split[3]
pushTrack := split[4]
subject := split[5]
commitHash := split[6]
commitDate := split[7]
subject := split[4]
commitHash := split[5]
name := strings.TrimPrefix(fullName, "heads/")
aheadForPull, behindForPull, gone := parseUpstreamInfo(upstreamName, track)
aheadForPush, behindForPush, _ := parseUpstreamInfo(upstreamName, pushTrack)
recency := ""
if storeCommitDateAsRecency {
if unixTimestamp, err := strconv.ParseInt(commitDate, 10, 64); err == nil {
recency = utils.UnixToTimeAgo(unixTimestamp)
}
}
pushables, pullables, gone := parseUpstreamInfo(upstreamName, track)
return &models.Branch{
Name: name,
Recency: recency,
AheadForPull: aheadForPull,
BehindForPull: behindForPull,
AheadForPush: aheadForPush,
BehindForPush: behindForPush,
UpstreamGone: gone,
Head: headMarker == "*",
Subject: subject,
CommitHash: commitHash,
Name: name,
Pushables: pushables,
Pullables: pullables,
UpstreamGone: gone,
Head: headMarker == "*",
Subject: subject,
CommitHash: commitHash,
}
}
@@ -345,10 +203,10 @@ func parseUpstreamInfo(upstreamName string, track string) (string, string, bool)
return "?", "?", true
}
ahead := parseDifference(track, `ahead (\d+)`)
behind := parseDifference(track, `behind (\d+)`)
pushables := parseDifference(track, `ahead (\d+)`)
pullables := parseDifference(track, `behind (\d+)`)
return ahead, behind, false
return pushables, pullables, false
}
func parseDifference(track string, regexStr string) string {
@@ -356,8 +214,9 @@ func parseDifference(track string, regexStr string) string {
match := re.FindStringSubmatch(track)
if len(match) > 1 {
return match[1]
} else {
return "0"
}
return "0"
}
// TODO: only look at the new reflog commits, and otherwise store the recencies in
+45 -83
View File
@@ -2,9 +2,7 @@ package git_commands
// "*|feat/detect-purge|origin/feat/detect-purge|[ahead 1]"
import (
"strconv"
"testing"
"time"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/stretchr/testify/assert"
@@ -12,114 +10,78 @@ import (
func TestObtainBranch(t *testing.T) {
type scenario struct {
testName string
input []string
storeCommitDateAsRecency bool
expectedBranch *models.Branch
testName string
input []string
expectedBranch *models.Branch
}
// Use a time stamp of 2 1/2 hours ago, resulting in a recency string of "2h"
now := time.Now().Unix()
timeStamp := strconv.Itoa(int(now - 2.5*60*60))
scenarios := []scenario{
{
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "NoUpstream",
input: []string{"", "a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
testName: "NoUpstream",
input: []string{"", "a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "IsHead",
input: []string{"*", "a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
testName: "IsHead",
input: []string{"*", "a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: true,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: true,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "IsBehindAndAhead",
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "[behind 2, ahead 3]", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
testName: "IsBehindAndAhead",
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
AheadForPull: "3",
BehindForPull: "2",
AheadForPush: "3",
BehindForPush: "2",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
Pushables: "3",
Pullables: "2",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "[gone]", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
UpstreamGone: true,
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "WithCommitDateAsRecency",
input: []string{"", "a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: true,
expectedBranch: &models.Branch{
Name: "a_branch",
Recency: "2h",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
UpstreamGone: true,
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
branch := obtainBranch(s.input, s.storeCommitDateAsRecency)
branch := obtainBranch(s.input)
assert.EqualValues(t, s.expectedBranch, branch)
})
}
+17 -60
View File
@@ -41,6 +41,7 @@ func TestBranchGetCommitDifferences(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
pushables, pullables := instance.GetCommitDifferences("HEAD", "@{u}")
@@ -62,57 +63,37 @@ func TestBranchNewBranch(t *testing.T) {
func TestBranchDeleteBranch(t *testing.T) {
type scenario struct {
testName string
branchNames []string
force bool
runner *oscommands.FakeCmdObjRunner
test func(error)
testName string
force bool
runner *oscommands.FakeCmdObjRunner
test func(error)
}
scenarios := []scenario{
{
"Delete a branch",
[]string{"test"},
false,
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-d", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
},
{
"Delete multiple branches",
[]string{"test1", "test2", "test3"},
false,
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-d", "test1", "test2", "test3"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
},
{
"Force delete a branch",
[]string{"test"},
true,
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-D", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
},
{
"Force delete multiple branches",
[]string{"test1", "test2", "test3"},
true,
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-D", "test1", "test2", "test3"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
s.test(instance.LocalDelete(s.branchNames, s.force))
s.test(instance.Delete("test", s.force))
s.runner.CheckForMissingCalls()
})
}
@@ -122,14 +103,14 @@ func TestBranchMerge(t *testing.T) {
scenarios := []struct {
testName string
userConfig *config.UserConfig
variant MergeVariant
opts MergeOpts
branchName string
expected []string
}{
{
testName: "basic",
userConfig: &config.UserConfig{},
variant: MERGE_VARIANT_REGULAR,
opts: MergeOpts{},
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "mybranch"},
},
@@ -142,53 +123,27 @@ func TestBranchMerge(t *testing.T) {
},
},
},
variant: MERGE_VARIANT_REGULAR,
opts: MergeOpts{},
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "--merging-args", "mybranch"},
},
{
testName: "multiple merging args",
userConfig: &config.UserConfig{
Git: config.GitConfig{
Merging: config.MergingConfig{
Args: "--arg1 --arg2", // it's up to the user what they put here
},
},
},
variant: MERGE_VARIANT_REGULAR,
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "--arg1", "--arg2", "mybranch"},
},
{
testName: "fast-forward merge",
testName: "fast forward only",
userConfig: &config.UserConfig{},
variant: MERGE_VARIANT_FAST_FORWARD,
opts: MergeOpts{FastForwardOnly: true},
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "--ff", "mybranch"},
},
{
testName: "non-fast-forward merge",
userConfig: &config.UserConfig{},
variant: MERGE_VARIANT_NON_FAST_FORWARD,
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "--no-ff", "mybranch"},
},
{
testName: "squash merge",
userConfig: &config.UserConfig{},
variant: MERGE_VARIANT_SQUASH,
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "--squash", "--ff", "mybranch"},
expected: []string{"merge", "--no-edit", "--ff-only", "mybranch"},
},
}
for _, s := range scenarios {
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.variant))
assert.NoError(t, instance.Merge(s.branchName, s.opts))
runner.CheckForMissingCalls()
})
}
@@ -222,6 +177,7 @@ func TestBranchCheckout(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
s.test(instance.Checkout("test", CheckoutOptions{Force: s.force}))
@@ -310,6 +266,7 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
s.test(instance.CurrentBranchInfo())
+58 -148
View File
@@ -23,7 +23,7 @@ func NewCommitCommands(gitCommon *GitCommon) *CommitCommands {
// ResetAuthor resets the author of the topmost commit
func (self *CommitCommands) ResetAuthor() error {
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--allow-empty-message", "--only", "--no-edit", "--amend", "--reset-author").
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--reset-author").
ToArgv()
return self.cmd.New(cmdArgs).Run()
@@ -32,50 +32,15 @@ func (self *CommitCommands) ResetAuthor() error {
// 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 {
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--allow-empty-message", "--only", "--no-edit", "--amend", "--author="+value).
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--author="+value).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// Add a commit's coauthor using Github/Gitlab Co-authored-by metadata. Value is expected to be of the form 'Name <Email>'
func (self *CommitCommands) AddCoAuthor(hash string, author string) error {
message, err := self.GetCommitMessage(hash)
if err != nil {
return err
}
message = AddCoAuthorToMessage(message, author)
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only", "-m", message).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
func AddCoAuthorToMessage(message string, author string) string {
subject, body, _ := strings.Cut(message, "\n")
return strings.TrimSpace(subject) + "\n\n" + AddCoAuthorToDescription(strings.TrimSpace(body), author)
}
func AddCoAuthorToDescription(description string, author string) string {
if description != "" {
lines := strings.Split(description, "\n")
if strings.HasPrefix(lines[len(lines)-1], "Co-authored-by:") {
description += "\n"
} else {
description += "\n\n"
}
}
return description + fmt.Sprintf("Co-authored-by: %s", author)
}
// ResetToCommit reset to commit
func (self *CommitCommands) ResetToCommit(hash string, strength string, envVars []string) error {
cmdArgs := NewGitCmd("reset").Arg("--"+strength, hash).ToArgv()
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
cmdArgs := NewGitCmd("reset").Arg("--"+strength, sha).ToArgv()
return self.cmd.New(cmdArgs).
// prevents git from prompting us for input which would freeze the program
@@ -85,11 +50,13 @@ func (self *CommitCommands) ResetToCommit(hash string, strength string, envVars
Run()
}
func (self *CommitCommands) CommitCmdObj(summary string, description string, forceSkipHooks bool) *oscommands.CmdObj {
messageArgs := self.commitMessageArgs(summary, description)
skipHookPrefix := self.UserConfig().Git.SkipHookPrefix
func (self *CommitCommands) CommitCmdObj(message string) oscommands.ICmdObj {
messageArgs := self.commitMessageArgs(message)
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
cmdArgs := NewGitCmd("commit").
ArgIf(forceSkipHooks || (skipHookPrefix != "" && strings.HasPrefix(summary, skipHookPrefix)), "--no-verify").
ArgIf(skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix), "--no-verify").
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
Arg(messageArgs...).
ToArgv()
@@ -97,38 +64,25 @@ func (self *CommitCommands) CommitCmdObj(summary string, description string, for
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) RewordLastCommitInEditorCmdObj() *oscommands.CmdObj {
func (self *CommitCommands) RewordLastCommitInEditorCmdObj() oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
}
func (self *CommitCommands) RewordLastCommitInEditorWithMessageFileCmdObj(tmpMessageFile string) *oscommands.CmdObj {
return self.cmd.New(NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only", "--edit", "--file="+tmpMessageFile).ToArgv())
}
func (self *CommitCommands) CommitInEditorWithMessageFileCmdObj(tmpMessageFile string, forceSkipHooks bool) *oscommands.CmdObj {
return self.cmd.New(NewGitCmd("commit").
ArgIf(forceSkipHooks, "--no-verify").
Arg("--edit").
Arg("--file="+tmpMessageFile).
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
ToArgv())
}
// RewordLastCommit rewords the topmost commit with the given message
func (self *CommitCommands) RewordLastCommit(summary string, description string) *oscommands.CmdObj {
messageArgs := self.commitMessageArgs(summary, description)
func (self *CommitCommands) RewordLastCommit(message string) error {
messageArgs := self.commitMessageArgs(message)
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only").
Arg(messageArgs...).
ToArgv()
return self.cmd.New(cmdArgs)
return self.cmd.New(cmdArgs).Run()
}
func (self *CommitCommands) commitMessageArgs(summary string, description string) []string {
args := []string{"-m", summary}
func (self *CommitCommands) commitMessageArgs(message string) []string {
msg, description, _ := strings.Cut(message, "\n")
args := []string{"-m", msg}
if description != "" {
args = append(args, "-m", description)
@@ -138,7 +92,7 @@ func (self *CommitCommands) commitMessageArgs(summary string, description string
}
// runs git commit without the -m argument meaning it will invoke the user's editor
func (self *CommitCommands) CommitEditorCmdObj() *oscommands.CmdObj {
func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
cmdArgs := NewGitCmd("commit").
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
ToArgv()
@@ -147,34 +101,33 @@ func (self *CommitCommands) CommitEditorCmdObj() *oscommands.CmdObj {
}
func (self *CommitCommands) signoffFlag() string {
if self.UserConfig().Git.Commit.SignOff {
if self.UserConfig.Git.Commit.SignOff {
return "--signoff"
} else {
return ""
}
return ""
}
func (self *CommitCommands) GetCommitMessage(commitHash string) (string, error) {
cmdArgs := NewGitCmd("log").
Arg("--format=%B", "--max-count=1", commitHash).
Config("log.showsignature=false").
ToArgv()
// Get the subject of the HEAD commit
func (self *CommitCommands) GetHeadCommitMessage() (string, error) {
cmdArgs := NewGitCmd("log").Arg("-1", "--pretty=%s").ToArgv()
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return strings.ReplaceAll(strings.TrimSpace(message), "\r\n", "\n"), err
return strings.TrimSpace(message), err
}
func (self *CommitCommands) GetCommitSubject(commitHash string) (string, error) {
cmdArgs := NewGitCmd("log").
Arg("--format=%s", "--max-count=1", commitHash).
Config("log.showsignature=false").
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
cmdArgs := NewGitCmd("rev-list").
Arg("--format=%B", "--max-count=1", commitSha).
ToArgv()
subject, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return strings.TrimSpace(subject), err
messageWithHeader, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
return strings.TrimSpace(message), err
}
func (self *CommitCommands) GetCommitDiff(commitHash string) (string, error) {
cmdArgs := NewGitCmd("show").Arg("--no-color", commitHash).ToArgv()
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
cmdArgs := NewGitCmd("show").Arg("--no-color", commitSha).ToArgv()
diff, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return diff, err
@@ -185,9 +138,9 @@ type Author struct {
Email string
}
func (self *CommitCommands) GetCommitAuthor(commitHash string) (Author, error) {
func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%an%x00%ae", commitHash).
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitSha).
ToArgv()
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
@@ -204,37 +157,23 @@ func (self *CommitCommands) GetCommitAuthor(commitHash string) (Author, error) {
return author, err
}
func (self *CommitCommands) GetCommitMessageFirstLine(hash string) (string, error) {
return self.GetCommitMessagesFirstLine([]string{hash})
func (self *CommitCommands) GetCommitMessageFirstLine(sha string) (string, error) {
return self.GetCommitMessagesFirstLine([]string{sha})
}
func (self *CommitCommands) GetCommitMessagesFirstLine(hashes []string) (string, error) {
func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%s").
Arg(hashes...).
Arg(shas...).
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
// Example output:
//
// cd50c79ae Preserve the commit message correctly even if the description has blank lines
// 3ebba5f32 Add test demonstrating a bug with preserving the commit message
// 9a423c388 Remove unused function
func (self *CommitCommands) GetHashesAndCommitMessagesFirstLine(hashes []string) (string, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%h %s").
Arg(hashes...).
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
func (self *CommitCommands) GetCommitsOneline(hashes []string) (string, error) {
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--oneline").
Arg(hashes...).
Arg(shas...).
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
@@ -245,78 +184,49 @@ func (self *CommitCommands) AmendHead() error {
return self.AmendHeadCmdObj().Run()
}
func (self *CommitCommands) AmendHeadCmdObj() *oscommands.CmdObj {
func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
cmdArgs := NewGitCmd("commit").
Arg("--amend", "--no-edit", "--allow-empty", "--allow-empty-message").
Arg("--amend", "--no-edit", "--allow-empty").
ToArgv()
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) ShowCmdObj(hash string, filterPaths []string) *oscommands.CmdObj {
contextSize := self.UserConfig().Git.DiffContextSize
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
contextSize := self.UserConfig.Git.DiffContextSize
extDiffCmd := self.pagerConfig.GetExternalDiffCommand()
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig()
cmdArgs := NewGitCmd("show").
Config("diff.noprefix=false").
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
ArgIfElse(extDiffCmd != "" || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
Arg("--submodule").
Arg("--color="+self.pagerConfig.GetColorArg()).
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
Arg(fmt.Sprintf("--unified=%d", contextSize)).
Arg("--stat").
Arg("--decorate").
Arg("-p").
Arg(hash).
ArgIf(self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg(fmt.Sprintf("--find-renames=%d%%", self.UserConfig().Git.RenameSimilarityThreshold)).
Arg("--").
Arg(filterPaths...).
Dir(self.repoPaths.worktreePath).
Arg(sha).
ArgIf(ignoreWhitespace, "--ignore-all-space").
ArgIf(filterPath != "", "--", filterPath).
ToArgv()
return self.cmd.New(cmdArgs).DontLog()
}
func (self *CommitCommands) ShowFileContentCmdObj(hash string, filePath string) *oscommands.CmdObj {
cmdArgs := NewGitCmd("show").
Arg(fmt.Sprintf("%s:%s", hash, filePath)).
ToArgv()
return self.cmd.New(cmdArgs).DontLog()
// Revert reverts the selected commit by sha
func (self *CommitCommands) Revert(sha string) error {
cmdArgs := NewGitCmd("revert").Arg(sha).ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// Revert reverts the selected commits by hash. If isMerge is true, we'll pass -m 1
// to say we want to revert the first parent of the merge commit, which is the one
// people want in 99.9% of cases. In current git versions we could unconditionally
// pass -m 1 even for non-merge commits, but older versions of git choke on it.
func (self *CommitCommands) Revert(hashes []string, isMerge bool) error {
cmdArgs := NewGitCmd("revert").
ArgIf(isMerge, "-m", "1").
Arg(hashes...).
func (self *CommitCommands) RevertMerge(sha string, parentNumber int) error {
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
func (self *CommitCommands) CreateFixupCommit(hash string) error {
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + hash).ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CreateAmendCommit creates a commit that changes the commit message of a previous commit
func (self *CommitCommands) CreateAmendCommit(originalSubject, newSubject, newDescription string, includeFileChanges bool) error {
description := newSubject
if newDescription != "" {
description += "\n\n" + newDescription
}
cmdArgs := NewGitCmd("commit").
Arg("-m", "amend! "+originalSubject).
Arg("-m", description).
ArgIf(!includeFileChanges, "--only", "--allow-empty").
ToArgv()
func (self *CommitCommands) CreateFixupCommit(sha string) error {
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + sha).ToArgv()
return self.cmd.New(cmdArgs).Run()
}
@@ -3,6 +3,7 @@ package git_commands
import (
"strings"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
@@ -24,7 +25,6 @@ func NewCommitFileLoader(common *common.Common, cmd oscommands.ICmdObjBuilder) *
// GetFilesInDiff get the specified commit files
func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) {
cmdArgs := NewGitCmd("diff").
Config("diff.noprefix=false").
Arg("--submodule").
Arg("--no-ext-diff").
Arg("--name-status").
@@ -52,10 +52,10 @@ func getCommitFilesFromFilenames(filenames string) []*models.CommitFile {
}
// typical result looks like 'A my_file' meaning my_file was added
return lo.Map(lo.Chunk(lines, 2), func(chunk []string, _ int) *models.CommitFile {
return slices.Map(lo.Chunk(lines, 2), func(chunk []string) *models.CommitFile {
return &models.CommitFile{
ChangeStatus: chunk[0],
Path: chunk[1],
Name: chunk[1],
}
})
}
@@ -23,7 +23,7 @@ func TestGetCommitFilesFromFilenames(t *testing.T) {
input: "MM\x00Myfile\x00",
output: []*models.CommitFile{
{
Path: "Myfile",
Name: "Myfile",
ChangeStatus: "MM",
},
},
@@ -33,11 +33,11 @@ func TestGetCommitFilesFromFilenames(t *testing.T) {
input: "MM\x00Myfile\x00M \x00MyOtherFile\x00",
output: []*models.CommitFile{
{
Path: "Myfile",
Name: "Myfile",
ChangeStatus: "MM",
},
{
Path: "MyOtherFile",
Name: "MyOtherFile",
ChangeStatus: "M ",
},
},
@@ -47,15 +47,15 @@ func TestGetCommitFilesFromFilenames(t *testing.T) {
input: "MM\x00Myfile\x00M \x00MyOtherFile\x00 M\x00YetAnother\x00",
output: []*models.CommitFile{
{
Path: "Myfile",
Name: "Myfile",
ChangeStatus: "MM",
},
{
Path: "MyOtherFile",
Name: "MyOtherFile",
ChangeStatus: "M ",
},
{
Path: "YetAnother",
Name: "YetAnother",
ChangeStatus: " M",
},
},
+287 -299
View File
@@ -6,18 +6,16 @@ import (
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"github.com/jesseduffield/generics/set"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
// context:
@@ -31,10 +29,15 @@ type CommitLoader struct {
*common.Common
cmd oscommands.ICmdObjBuilder
getWorkingTreeState func() models.WorkingTreeState
readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string
getRebaseMode func() (enums.RebaseMode, error)
readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string
// List of main branches that exist in the repo.
// We use these to obtain the merge base of the branch.
// When nil, we're yet to obtain the list of existing main branches.
// When an empty slice, we've obtained the list and it's empty.
mainBranches []string
*GitCommon
}
@@ -42,117 +45,75 @@ type CommitLoader struct {
func NewCommitLoader(
cmn *common.Common,
cmd oscommands.ICmdObjBuilder,
getWorkingTreeState func() models.WorkingTreeState,
dotGitDir string,
getRebaseMode func() (enums.RebaseMode, error),
gitCommon *GitCommon,
) *CommitLoader {
return &CommitLoader{
Common: cmn,
cmd: cmd,
getWorkingTreeState: getWorkingTreeState,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
GitCommon: gitCommon,
Common: cmn,
cmd: cmd,
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
dotGitDir: dotGitDir,
mainBranches: nil,
GitCommon: gitCommon,
}
}
type GetCommitsOptions struct {
Limit bool
FilterPath string
FilterAuthor string
IncludeRebaseCommits bool
RefName string // e.g. "HEAD" or "my_branch"
RefForPushedStatus models.Ref // the ref to use for determining pushed/unpushed status
RefName string // e.g. "HEAD" or "my_branch"
// determines if we show the whole git graph i.e. pass the '--all' flag
All bool
// If non-empty, show divergence from this ref (left-right log)
RefToShowDivergenceFrom string
MainBranches *MainBranches
HashPool *utils.StringPool
}
// GetCommits obtains the commits of the current branch
func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit, error) {
commits := []*models.Commit{}
var rebasingCommits []*models.Commit
if opts.IncludeRebaseCommits && opts.FilterPath == "" {
var err error
commits, err = self.MergeRebasingCommits(opts.HashPool, commits)
rebasingCommits, err = self.MergeRebasingCommits(commits)
if err != nil {
return nil, err
}
commits = append(commits, rebasingCommits...)
}
wg := sync.WaitGroup{}
wg.Add(2)
var logErr error
go utils.Safe(func() {
defer wg.Done()
var realCommits []*models.Commit
realCommits, logErr = loadCommits(self.getLogCmd(opts), opts.FilterPath, func(line string) (*models.Commit, bool) {
return self.extractCommitFromLine(opts.HashPool, line, opts.RefToShowDivergenceFrom != ""), false
})
if logErr == nil {
commits = append(commits, realCommits...)
}
})
var unmergedCommitHashes *set.Set[string]
var remoteUnmergedCommitHashes *set.Set[string]
mainBranches := opts.MainBranches.Get()
go utils.Safe(func() {
defer wg.Done()
if len(mainBranches) > 0 {
unmergedCommitHashes = self.getReachableHashes(opts.RefName, mainBranches)
if opts.RefToShowDivergenceFrom != "" {
remoteUnmergedCommitHashes = self.getReachableHashes(opts.RefToShowDivergenceFrom, mainBranches)
}
}
})
var unpushedCommitHashes *set.Set[string]
if opts.RefForPushedStatus != nil {
unpushedCommitHashes = self.getReachableHashes(opts.RefForPushedStatus.FullRefName(),
append([]string{opts.RefForPushedStatus.RefName() + "@{u}"}, mainBranches...))
passedFirstPushedCommit := false
firstPushedCommit, err := self.getFirstPushedCommit(opts.RefName)
if err != nil {
// must have no upstream branch so we'll consider everything as pushed
passedFirstPushedCommit = true
}
wg.Wait()
if logErr != nil {
return nil, logErr
err = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
commit := self.extractCommitFromLine(line)
if commit.Sha == firstPushedCommit {
passedFirstPushedCommit = true
}
commit.Status = map[bool]models.CommitStatus{true: models.StatusUnpushed, false: models.StatusPushed}[!passedFirstPushedCommit]
commits = append(commits, commit)
return false, nil
})
if err != nil {
return nil, err
}
if len(commits) == 0 {
return commits, nil
}
if opts.RefToShowDivergenceFrom != "" {
sort.SliceStable(commits, func(i, j int) bool {
// In the divergence view we want incoming commits to come first
return commits[i].Divergence > commits[j].Divergence
})
_, localSectionStart, found := lo.FindIndexOf(commits, func(commit *models.Commit) bool {
return commit.Divergence == models.DivergenceLeft
})
if !found {
localSectionStart = len(commits)
}
setCommitStatuses(unpushedCommitHashes, remoteUnmergedCommitHashes, commits[:localSectionStart])
setCommitStatuses(unpushedCommitHashes, unmergedCommitHashes, commits[localSectionStart:])
} else {
setCommitStatuses(unpushedCommitHashes, unmergedCommitHashes, commits)
}
commits = self.setCommitMergedStatuses(opts.RefName, commits)
return commits, nil
}
func (self *CommitLoader) MergeRebasingCommits(hashPool *utils.StringPool, commits []*models.Commit) ([]*models.Commit, error) {
func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*models.Commit, error) {
// chances are we have as many commits as last time so we'll set the capacity to be the old length
result := make([]*models.Commit, 0, len(commits))
for i, commit := range commits {
@@ -162,60 +123,43 @@ func (self *CommitLoader) MergeRebasingCommits(hashPool *utils.StringPool, commi
}
}
workingTreeState := self.getWorkingTreeState()
addConflictedRebasingCommit := true
if workingTreeState.CherryPicking || workingTreeState.Reverting {
sequencerCommits, err := self.getHydratedSequencerCommits(hashPool, workingTreeState)
if err != nil {
return nil, err
}
result = append(sequencerCommits, result...)
addConflictedRebasingCommit = false
rebaseMode, err := self.getRebaseMode()
if err != nil {
return nil, err
}
if workingTreeState.Rebasing {
rebasingCommits, err := self.getHydratedRebasingCommits(hashPool, addConflictedRebasingCommit)
if err != nil {
return nil, err
}
if len(rebasingCommits) > 0 {
result = append(rebasingCommits, result...)
}
if rebaseMode == enums.REBASE_MODE_NONE {
// not in rebase mode so return original commits
return result, nil
}
rebasingCommits, err := self.getHydratedRebasingCommits(rebaseMode)
if err != nil {
return nil, err
}
if len(rebasingCommits) > 0 {
result = append(rebasingCommits, result...)
}
return result, nil
}
// extractCommitFromLine takes a line from a git log and extracts the hash, message, date, and tag if present
// extractCommitFromLine takes a line from a git log and extracts the sha, message, date, and tag if present
// then puts them into a commit object
// example input:
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line string, showDivergence bool) *models.Commit {
split := strings.SplitN(line, "\x00", 8)
func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
split := strings.SplitN(line, "\x00", 7)
// Ensure we have the minimum required fields (at least 7 for basic functionality)
if len(split) < 7 {
self.Log.Warnf("Malformed git log line: expected at least 7 fields, got %d. Line: %s", len(split), line)
return nil
}
hash := split[0]
sha := split[0]
unixTimestamp := split[1]
authorName := split[2]
authorEmail := split[3]
parentHashes := split[4]
divergence := models.DivergenceNone
if showDivergence {
divergence = lo.Ternary(split[5] == "<", models.DivergenceLeft, models.DivergenceRight)
}
extraInfo := strings.TrimSpace(split[6])
extraInfo := strings.TrimSpace(split[4])
parentHashes := split[5]
message := split[6]
// message (and the \x00 before it) might not be present if extraInfo is extremely long
message := ""
if len(split) > 7 {
message = split[7]
}
var tags []string
tags := []string{}
if extraInfo != "" {
extraInfoFields := strings.Split(extraInfo, ",")
@@ -238,8 +182,8 @@ func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line
parents = strings.Split(parentHashes, " ")
}
return models.NewCommit(hashPool, models.NewCommitOpts{
Hash: hash,
return &models.Commit{
Sha: sha,
Name: message,
Tags: tags,
ExtraInfo: extraInfo,
@@ -247,40 +191,21 @@ func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line
AuthorName: authorName,
AuthorEmail: authorEmail,
Parents: parents,
Divergence: divergence,
})
}
}
func (self *CommitLoader) getHydratedRebasingCommits(hashPool *utils.StringPool, addConflictingCommit bool) ([]*models.Commit, error) {
return self.getHydratedTodoCommits(hashPool, self.getRebasingCommits(hashPool, addConflictingCommit), false)
}
func (self *CommitLoader) getHydratedSequencerCommits(hashPool *utils.StringPool, workingTreeState models.WorkingTreeState) ([]*models.Commit, error) {
commits := self.getSequencerCommits(hashPool)
if len(commits) > 0 {
// If we have any commits in .git/sequencer/todo, then the last one of
// those is the conflicting one.
commits[len(commits)-1].Status = models.StatusConflicted
} else {
// For single-commit cherry-picks and reverts, git apparently doesn't
// use the sequencer; in that case, CHERRY_PICK_HEAD or REVERT_HEAD is
// our conflicting commit, so synthesize it here.
conflicedCommit := self.getConflictedSequencerCommit(hashPool, workingTreeState)
if conflicedCommit != nil {
commits = append(commits, conflicedCommit)
}
func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode) ([]*models.Commit, error) {
commits, err := self.getRebasingCommits(rebaseMode)
if err != nil {
return nil, err
}
return self.getHydratedTodoCommits(hashPool, commits, true)
}
func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, todoCommits []*models.Commit, todoFileHasShortHashes bool) ([]*models.Commit, error) {
if len(todoCommits) == 0 {
if len(commits) == 0 {
return nil, nil
}
commitHashes := lo.FilterMap(todoCommits, func(commit *models.Commit, _ int) (string, bool) {
return commit.Hash(), commit.Hash() != ""
commitShas := slices.FilterMap(commits, func(commit *models.Commit) (string, bool) {
return commit.Sha, commit.Sha != ""
})
// note that we're not filtering these as we do non-rebasing commits just because
@@ -289,41 +214,38 @@ func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, tod
NewGitCmd("show").
Config("log.showSignature=false").
Arg("--no-patch", "--oneline", "--abbrev=20", prettyFormat).
Arg(commitHashes...).
Arg(commitShas...).
ToArgv(),
).DontLog()
fullCommits := map[string]*models.Commit{}
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
if line == "" || line[0] != '+' {
return false, nil
}
commit := self.extractCommitFromLine(hashPool, line[1:], false)
fullCommits[commit.Hash()] = commit
err = cmdObj.RunAndProcessLines(func(line string) (bool, error) {
commit := self.extractCommitFromLine(line)
fullCommits[commit.Sha] = commit
return false, nil
})
if err != nil {
return nil, err
}
findFullCommit := lo.Ternary(todoFileHasShortHashes,
func(hash string) *models.Commit {
findFullCommit := lo.Ternary(self.version.IsOlderThan(2, 25, 2),
func(sha string) *models.Commit {
for s, c := range fullCommits {
if strings.HasPrefix(s, hash) {
if strings.HasPrefix(s, sha) {
return c
}
}
return nil
},
func(hash string) *models.Commit {
return fullCommits[hash]
func(sha string) *models.Commit {
return fullCommits[sha]
})
hydratedCommits := make([]*models.Commit, 0, len(todoCommits))
for _, rebasingCommit := range todoCommits {
if rebasingCommit.Hash() == "" {
hydratedCommits := make([]*models.Commit, 0, len(commits))
for _, rebasingCommit := range commits {
if rebasingCommit.Sha == "" {
hydratedCommits = append(hydratedCommits, rebasingCommit)
} else if commit := findFullCommit(rebasingCommit.Hash()); commit != nil {
} else if commit := findFullCommit(rebasingCommit.Sha); commit != nil {
commit.Action = rebasingCommit.Action
commit.Status = rebasingCommit.Status
hydratedCommits = append(hydratedCommits, commit)
@@ -333,16 +255,73 @@ func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, tod
}
// getRebasingCommits obtains the commits that we're in the process of rebasing
func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) ([]*models.Commit, error) {
switch rebaseMode {
case enums.REBASE_MODE_MERGING:
return self.getNormalRebasingCommits()
case enums.REBASE_MODE_INTERACTIVE:
return self.getInteractiveRebasingCommits()
default:
return nil, nil
}
}
func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) {
rewrittenCount := 0
bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-apply/rewritten"))
if err == nil {
content := string(bytesContent)
rewrittenCount = len(strings.Split(content, "\n"))
}
// we know we're rebasing, so lets get all the files whose names have numbers
commits := []*models.Commit{}
err = self.walkFiles(filepath.Join(self.dotGitDir, "rebase-apply"), func(path string, f os.FileInfo, err error) error {
if rewrittenCount > 0 {
rewrittenCount--
return nil
}
if err != nil {
return err
}
re := regexp.MustCompile(`^\d+$`)
if !re.MatchString(f.Name()) {
return nil
}
bytesContent, err := self.readFile(path)
if err != nil {
return err
}
content := string(bytesContent)
commit := self.commitFromPatch(content)
commits = append([]*models.Commit{commit}, commits...)
return nil
})
if err != nil {
return nil, err
}
return commits, nil
}
// git-rebase-todo example:
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
func (self *CommitLoader) getRebasingCommits(hashPool *utils.StringPool, addConflictingCommit bool) []*models.Commit {
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"))
// git-rebase-todo.backup example:
// pick 49cbba374296938ea86bbd4bf4fee2f6ba5cccf6 third commit on master
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 blah commit on master
// pick afb893148791a2fbd8091aeb81deba4930c73031 fourth commit on master
// getInteractiveRebasingCommits takes our git-rebase-todo and our git-rebase-todo.backup files
// and extracts out the sha and names of commits that we still have to go
// in the rebase:
func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, error) {
bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"))
if err != nil {
self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
// we assume an error means the file doesn't exist so we just return
return nil
return nil, nil
}
commits := []*models.Commit{}
@@ -350,65 +329,68 @@ func (self *CommitLoader) getRebasingCommits(hashPool *utils.StringPool, addConf
todos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar())
if err != nil {
self.Log.Error(fmt.Sprintf("error occurred while parsing git-rebase-todo file: %s", err.Error()))
return 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 addConflictingCommit {
if conflictedCommit := self.getConflictedCommit(hashPool, todos); conflictedCommit != nil {
commits = append(commits, conflictedCommit)
}
if conflictedCommitSha := self.getConflictedCommit(todos); conflictedCommitSha != "" {
commits = append(commits, &models.Commit{
Sha: conflictedCommitSha,
Name: "",
Status: models.StatusRebasing,
Action: models.ActionConflict,
})
}
for _, t := range todos {
if t.Command == todo.UpdateRef {
t.Msg = t.Ref
} else if t.Command == todo.Exec {
t.Msg = t.ExecCommand
t.Msg = strings.TrimPrefix(t.Ref, "refs/heads/")
} else if t.Commit == "" {
// Command does not have a commit associated, skip
continue
}
commits = utils.Prepend(commits, models.NewCommit(hashPool, models.NewCommitOpts{
Hash: t.Commit,
commits = slices.Prepend(commits, &models.Commit{
Sha: t.Commit,
Name: t.Msg,
Status: models.StatusRebasing,
Action: t.Command,
}))
})
}
return commits
return commits, nil
}
func (self *CommitLoader) getConflictedCommit(hashPool *utils.StringPool, todos []todo.Todo) *models.Commit {
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/done"))
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 nil
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 nil
return ""
}
amendFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/amend"))
messageFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/message"))
amendFileExists := false
if _, err := os.Stat(filepath.Join(self.dotGitDir, "rebase-merge/amend")); err == nil {
amendFileExists = true
}
return self.getConflictedCommitImpl(hashPool, todos, doneTodos, amendFileExists, messageFileExists)
return self.getConflictedCommitImpl(todos, doneTodos, amendFileExists)
}
func (self *CommitLoader) getConflictedCommitImpl(hashPool *utils.StringPool, todos []todo.Todo, doneTodos []todo.Todo, amendFileExists bool, messageFileExists bool) *models.Commit {
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 nil
return ""
}
lastTodo := doneTodos[len(doneTodos)-1]
if lastTodo.Command == todo.Break || lastTodo.Command == todo.Exec || lastTodo.Command == todo.Reword {
return nil
return ""
}
// In certain cases, git reschedules commands that failed. One example is if
@@ -419,7 +401,7 @@ func (self *CommitLoader) getConflictedCommitImpl(hashPool *utils.StringPool, to
// 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 nil
return ""
}
// Older versions of git have a bug whereby, if a command is rescheduled,
@@ -444,158 +426,164 @@ func (self *CommitLoader) getConflictedCommitImpl(hashPool *utils.StringPool, to
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 nil
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 nil
}
if !messageFileExists {
// As an additional check, see if the "message" file exists; if it
// doesn't, it must be because a multi-commit cherry-pick or revert
// was performed in the meantime, which deleted both the amend file
// and the message file.
return nil
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 nil
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 models.NewCommit(hashPool, models.NewCommitOpts{
Hash: lastTodo.Commit,
Action: lastTodo.Command,
Status: models.StatusConflicted,
})
return lastTodo.Commit
}
func (self *CommitLoader) getSequencerCommits(hashPool *utils.StringPool) []*models.Commit {
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "sequencer/todo"))
if err != nil {
self.Log.Error(fmt.Sprintf("error occurred reading sequencer/todo: %s", err.Error()))
// we assume an error means the file doesn't exist so we just return
return nil
// assuming the file starts like this:
// From e93d4193e6dd45ca9cf3a5a273d7ba6cd8b8fb20 Mon Sep 17 00:00:00 2001
// From: Lazygit Tester <test@example.com>
// Date: Wed, 5 Dec 2018 21:03:23 +1100
// Subject: second commit on master
func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
lines := strings.Split(content, "\n")
sha := strings.Split(lines[0], " ")[1]
name := strings.TrimPrefix(lines[3], "Subject: ")
return &models.Commit{
Sha: sha,
Name: name,
Status: models.StatusRebasing,
}
commits := []*models.Commit{}
todos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar())
if err != nil {
self.Log.Error(fmt.Sprintf("error occurred while parsing sequencer/todo file: %s", err.Error()))
return nil
}
for _, t := range todos {
if t.Commit == "" {
// Command does not have a commit associated, skip
continue
}
commits = utils.Prepend(commits, models.NewCommit(hashPool, models.NewCommitOpts{
Hash: t.Commit,
Name: t.Msg,
Status: models.StatusCherryPickingOrReverting,
Action: t.Command,
}))
}
return commits
}
func (self *CommitLoader) getConflictedSequencerCommit(hashPool *utils.StringPool, workingTreeState models.WorkingTreeState) *models.Commit {
var shaFile string
var action todo.TodoCommand
if workingTreeState.CherryPicking {
shaFile = "CHERRY_PICK_HEAD"
action = todo.Pick
} else if workingTreeState.Reverting {
shaFile = "REVERT_HEAD"
action = todo.Revert
} else {
return nil
func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*models.Commit) []*models.Commit {
ancestor := self.getMergeBase(refName)
if ancestor == "" {
return commits
}
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), shaFile))
if err != nil {
self.Log.Error(fmt.Sprintf("error occurred reading %s: %s", shaFile, err.Error()))
// we assume an error means the file doesn't exist so we just return
return nil
}
lines := strings.Split(string(bytesContent), "\n")
if len(lines) == 0 {
return nil
}
return models.NewCommit(hashPool, models.NewCommitOpts{
Hash: lines[0],
Status: models.StatusConflicted,
Action: action,
})
}
func setCommitStatuses(unpushedCommitHashes *set.Set[string], unmergedCommitHashes *set.Set[string], commits []*models.Commit) {
passedAncestor := false
for i, commit := range commits {
if commit.IsTODO() {
if strings.HasPrefix(ancestor, commit.Sha) {
passedAncestor = true
}
if commit.Status != models.StatusPushed && commit.Status != models.StatusUnpushed {
continue
}
if unmergedCommitHashes == nil || unmergedCommitHashes.Includes(commit.Hash()) {
if unpushedCommitHashes != nil && unpushedCommitHashes.Includes(commit.Hash()) {
commits[i].Status = models.StatusUnpushed
} else {
commits[i].Status = models.StatusPushed
}
} else {
if passedAncestor {
commits[i].Status = models.StatusMerged
}
}
return commits
}
func (self *CommitLoader) getReachableHashes(refName string, notRefNames []string) *set.Set[string] {
output, _, err := self.cmd.New(
NewGitCmd("rev-list").
func (self *CommitLoader) getMergeBase(refName string) string {
if self.mainBranches == nil {
self.mainBranches = self.getExistingMainBranches()
}
if len(self.mainBranches) == 0 {
return ""
}
// We pass all configured main branches to the merge-base call; git will
// return the base commit for the closest one.
output, err := self.cmd.New(
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
// If there's an error, it must be because one of the main branches that
// used to exist when we called getExistingMainBranches() was deleted
// meanwhile. To fix this for next time, throw away our cache.
self.mainBranches = nil
}
return ignoringWarnings(output)
}
func (self *CommitLoader) getExistingMainBranches() []string {
return lo.FilterMap(self.UserConfig.Git.MainBranches,
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
}
// If this failed, a local branch for this main branch doesn't exist or it
// has no upstream configured. Try looking for one in the "origin" remote.
ref := "refs/remotes/origin/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
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 {
trimmedOutput := strings.TrimSpace(commandOutput)
split := strings.Split(trimmedOutput, "\n")
// need to get last line in case the first line is a warning about how the error is ambiguous.
// At some point we should find a way to make it unambiguous
lastLine := split[len(split)-1]
return lastLine
}
// 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.
func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
output, err := self.cmd.New(
NewGitCmd("merge-base").
Arg(refName).
Arg(lo.Map(notRefNames, func(name string, _ int) string {
return "^" + name
})...).
Arg(strings.TrimPrefix(refName, "refs/heads/") + "@{u}").
ToArgv(),
).
DontLog().
RunWithOutputs()
RunWithOutput()
if err != nil {
return set.New[string]()
return "", err
}
return set.NewFromSlice(utils.SplitLines(output))
return ignoringWarnings(output), nil
}
// getLog gets the git log.
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) *oscommands.CmdObj {
gitLogOrder := self.UserConfig().Git.Log.Order
refSpec := opts.RefName
if opts.RefToShowDivergenceFrom != "" {
refSpec += "..." + opts.RefToShowDivergenceFrom
}
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
config := self.UserConfig.Git.Log
cmdArgs := NewGitCmd("log").
Arg(refSpec).
ArgIf(gitLogOrder != "default", "--"+gitLogOrder).
Arg(opts.RefName).
ArgIf(config.Order != "default", "--"+config.Order).
ArgIf(opts.All, "--all").
Arg("--oneline").
Arg(prettyFormat).
Arg("--abbrev=40").
ArgIf(opts.FilterAuthor != "", "--author="+opts.FilterAuthor).
ArgIf(opts.Limit, "-300").
ArgIf(opts.FilterPath != "", "--follow", "--name-status").
ArgIf(opts.FilterPath != "", "--follow").
Arg("--no-show-signature").
ArgIf(opts.RefToShowDivergenceFrom != "", "--left-right").
Arg("--").
ArgIf(opts.FilterPath != "", opts.FilterPath).
ToArgv()
@@ -603,4 +591,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) *oscommands.CmdObj {
return self.cmd.New(cmdArgs).DontLog()
}
const prettyFormat = `--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%s`
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s`
+126 -414
View File
@@ -1,76 +1,78 @@
package git_commands
import (
"errors"
"path/filepath"
"strings"
"testing"
"github.com/go-errors/errors"
"github.com/jesseduffield/generics/set"
"github.com/fsmiamoto/git-todo-parser/todo"
"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/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
"github.com/stretchr/testify/assert"
)
var commitsOutput = strings.ReplaceAll(`+0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|b21997d6b4cbdf84b149|>|HEAD -> better-tests|better typing for rebase mode
+b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164|1640824515|Jesse Duffield|jessedduffield@gmail.com|e94e8fc5b6fab4cb755f|>|origin/better-tests|fix logging
+e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com|d8084cd558925eb7c9c3|>|tag: 123, tag: 456|refactor
+d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com|65f910ebd85283b5cce9|>||WIP
+65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com|26c07b1ab33860a1a759|>||WIP
+26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com|3d4470a6c072208722e5|>||WIP
+3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com|053a66a7be3da43aacdc|>||WIP
+053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com|985fe482e806b172aea4|>||refactoring the config struct`, "|", "\x00")
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
e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com|tag: 123, tag: 456|d8084cd558925eb7c9c3|refactor
d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com||65f910ebd85283b5cce9|WIP
65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com||26c07b1ab33860a1a759|WIP
26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com||3d4470a6c072208722e5|WIP
3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|WIP
053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|refactoring the config struct`, "|", "\x00", -1)
var singleCommitOutput = strings.ReplaceAll(`+0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|b21997d6b4cbdf84b149|>|HEAD -> better-tests|better typing for rebase mode`, "|", "\x00")
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) {
type scenario struct {
testName string
runner *oscommands.FakeCmdObjRunner
expectedCommitOpts []models.NewCommitOpts
expectedError error
logOrder string
opts GetCommitsOptions
mainBranches []string
testName string
runner *oscommands.FakeCmdObjRunner
expectedCommits []*models.Commit
expectedError error
logOrder string
rebaseMode enums.RebaseMode
opts GetCommitsOptions
mainBranches []string
}
scenarios := []scenario{
{
testName: "should return no commits if there are none",
logOrder: "topo-order",
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: &models.Branch{Name: "mybranch"}, IncludeRebaseCommits: false},
testName: "should return no commits if there are none",
logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"rev-list", "refs/heads/mybranch", "^mybranch@{u}"}, "", nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", 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),
expectedCommitOpts: []models.NewCommitOpts{},
expectedError: nil,
expectedCommits: []*models.Commit{},
expectedError: nil,
},
{
testName: "should use proper upstream name for branch",
logOrder: "topo-order",
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: &models.Branch{Name: "mybranch"}, IncludeRebaseCommits: false},
testName: "should use proper upstream name for branch",
logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"rev-list", "refs/heads/mybranch", "^mybranch@{u}"}, "", nil).
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", 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),
expectedCommitOpts: []models.NewCommitOpts{},
expectedError: nil,
expectedCommits: []*models.Commit{},
expectedError: nil,
},
{
testName: "should return commits if they are present",
logOrder: "topo-order",
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: &models.Branch{Name: "mybranch"}, IncludeRebaseCommits: false},
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main", "develop"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"rev-list", "refs/heads/mybranch", "^mybranch@{u}", "^refs/remotes/origin/master", "^refs/remotes/origin/main"}, "0eea75e8c631fba6b58135697835d58ba4c18dbc\n", 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%P%x00%m%x00%D%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
@@ -78,17 +80,16 @@ func TestGetCommits(t *testing.T) {
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 which of our commits are not on any of the main branches yet
ExpectGitArgs([]string{"rev-list", "HEAD", "^refs/remotes/origin/master", "^refs/remotes/origin/main"},
"0eea75e8c631fba6b58135697835d58ba4c18dbc\nb21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164\ne94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c\nd8084cd558925eb7c9c38afeed5725c21653ab90\n65f910ebd85283b5cce9bf67d03d3f1a9ea3813a\n", 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/main"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
expectedCommitOpts: []models.NewCommitOpts{
expectedCommits: []*models.Commit{
{
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "(HEAD -> better-tests)",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -98,11 +99,11 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164",
Sha: "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164",
Name: "fix logging",
Status: models.StatusPushed,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "(origin/better-tests)",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -112,7 +113,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c",
Sha: "e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c",
Name: "refactor",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -126,11 +127,11 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "d8084cd558925eb7c9c38afeed5725c21653ab90",
Sha: "d8084cd558925eb7c9c38afeed5725c21653ab90",
Name: "WIP",
Status: models.StatusPushed,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -140,11 +141,11 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "65f910ebd85283b5cce9bf67d03d3f1a9ea3813a",
Sha: "65f910ebd85283b5cce9bf67d03d3f1a9ea3813a",
Name: "WIP",
Status: models.StatusPushed,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -154,11 +155,11 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "26c07b1ab33860a1a7591a0638f9925ccf497ffa",
Sha: "26c07b1ab33860a1a7591a0638f9925ccf497ffa",
Name: "WIP",
Status: models.StatusMerged,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -168,11 +169,11 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "3d4470a6c072208722e5ae9a54bcb9634959a1c5",
Sha: "3d4470a6c072208722e5ae9a54bcb9634959a1c5",
Name: "WIP",
Status: models.StatusMerged,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -182,11 +183,11 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "053a66a7be3da43aacdc7aa78e1fe757b82c4dd2",
Sha: "053a66a7be3da43aacdc7aa78e1fe757b82c4dd2",
Name: "refactoring the config struct",
Status: models.StatusMerged,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -199,15 +200,16 @@ func TestGetCommits(t *testing.T) {
expectedError: nil,
},
{
testName: "should not call rev-list for mainBranches if none exist",
testName: "should not call merge-base for mainBranches if none exist",
logOrder: "topo-order",
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: &models.Branch{Name: "mybranch"}, IncludeRebaseCommits: false},
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"rev-list", "refs/heads/mybranch", "^mybranch@{u}"}, "0eea75e8c631fba6b58135697835d58ba4c18dbc\n", 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%P%x00%m%x00%D%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, 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", "--"}, 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")).
@@ -216,13 +218,13 @@ func TestGetCommits(t *testing.T) {
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")),
expectedCommitOpts: []models.NewCommitOpts{
expectedCommits: []*models.Commit{
{
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "(HEAD -> better-tests)",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -235,15 +237,16 @@ func TestGetCommits(t *testing.T) {
expectedError: nil,
},
{
testName: "should call rev-list with all main branches that exist",
testName: "should call merge-base for all main branches that exist",
logOrder: "topo-order",
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: &models.Branch{Name: "mybranch"}, IncludeRebaseCommits: false},
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{"rev-list", "refs/heads/mybranch", "^mybranch@{u}", "^refs/remotes/origin/master", "^refs/remotes/origin/develop", "^refs/remotes/origin/1.0-hotfixes"}, "0eea75e8c631fba6b58135697835d58ba4c18dbc\n", 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%P%x00%m%x00%D%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, 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", "--"}, 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")).
@@ -251,16 +254,16 @@ func TestGetCommits(t *testing.T) {
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 which of our commits are not on any of the main branches yet
ExpectGitArgs([]string{"rev-list", "HEAD", "^refs/remotes/origin/master", "^refs/remotes/origin/develop", "^refs/remotes/origin/1.0-hotfixes"}, "0eea75e8c631fba6b58135697835d58ba4c18dbc\n", 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),
expectedCommitOpts: []models.NewCommitOpts{
expectedCommits: []*models.Commit{
{
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
Tags: nil,
Tags: []string{},
ExtraInfo: "(HEAD -> better-tests)",
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
@@ -273,40 +276,42 @@ func TestGetCommits(t *testing.T) {
expectedError: nil,
},
{
testName: "should not specify order if `log.order` is `default`",
logOrder: "default",
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: &models.Branch{Name: "mybranch"}, IncludeRebaseCommits: false},
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{"rev-list", "refs/heads/mybranch", "^mybranch@{u}"}, "", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
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),
expectedCommitOpts: []models.NewCommitOpts{},
expectedError: nil,
expectedCommits: []*models.Commit{},
expectedError: nil,
},
{
testName: "should set filter path",
logOrder: "default",
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: &models.Branch{Name: "mybranch"}, FilterPath: "src"},
testName: "should set filter path",
logOrder: "default",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"rev-list", "refs/heads/mybranch", "^mybranch@{u}"}, "", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%s", "--abbrev=40", "--follow", "--name-status", "--no-show-signature", "--", "src"}, "", nil),
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", "--follow", "--no-show-signature", "--", "src"}, "", nil),
expectedCommitOpts: []models.NewCommitOpts{},
expectedError: nil,
expectedCommits: []*models.Commit{},
expectedError: nil,
},
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.testName, func(t *testing.T) {
common := common.NewDummyCommon()
common.UserConfig().Git.Log.Order = scenario.logOrder
cmd := oscommands.NewDummyCmdObjBuilder(scenario.runner)
common := utils.NewDummyCommon()
common.UserConfig.Git.Log.Order = scenario.logOrder
builder := &CommitLoader{
Common: common,
cmd: cmd,
getWorkingTreeState: func() models.WorkingTreeState { return models.WorkingTreeState{} },
dotGitDir: ".git",
Common: common,
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) {
return []byte(""), nil
},
@@ -315,17 +320,10 @@ func TestGetCommits(t *testing.T) {
},
}
hashPool := &utils.StringPool{}
common.UserConfig.Git.MainBranches = scenario.mainBranches
commits, err := builder.GetCommits(scenario.opts)
common.UserConfig().Git.MainBranches = scenario.mainBranches
opts := scenario.opts
opts.MainBranches = NewMainBranches(common, cmd)
opts.HashPool = hashPool
commits, err := builder.GetCommits(opts)
expectedCommits := lo.Map(scenario.expectedCommitOpts,
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
assert.Equal(t, expectedCommits, commits)
assert.Equal(t, scenario.expectedCommits, commits)
assert.Equal(t, scenario.expectedError, err)
scenario.runner.CheckForMissingCalls()
@@ -334,22 +332,19 @@ func TestGetCommits(t *testing.T) {
}
func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
hashPool := &utils.StringPool{}
scenarios := []struct {
testName string
todos []todo.Todo
doneTodos []todo.Todo
amendFileExists bool
messageFileExists bool
expectedResult *models.Commit
testName string
todos []todo.Todo
doneTodos []todo.Todo
amendFileExists bool
expectedSha string
}{
{
testName: "no done todos",
todos: []todo.Todo{},
doneTodos: []todo.Todo{},
amendFileExists: false,
expectedResult: nil,
expectedSha: "",
},
{
testName: "common case (conflict)",
@@ -365,11 +360,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "fa1afe1",
Action: todo.Pick,
Status: models.StatusConflicted,
}),
expectedSha: "fa1afe1",
},
{
testName: "last command was 'break'",
@@ -378,7 +369,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
{Command: todo.Break},
},
amendFileExists: false,
expectedResult: nil,
expectedSha: "",
},
{
testName: "last command was 'exec'",
@@ -390,7 +381,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedResult: nil,
expectedSha: "",
},
{
testName: "last command was 'reword'",
@@ -399,7 +390,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
{Command: todo.Reword},
},
amendFileExists: false,
expectedResult: nil,
expectedSha: "",
},
{
testName: "'pick' was rescheduled",
@@ -416,7 +407,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedResult: nil,
expectedSha: "",
},
{
testName: "'pick' was rescheduled, buggy git version",
@@ -441,7 +432,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedResult: nil,
expectedSha: "",
},
{
testName: "conflicting 'pick' after 'exec'",
@@ -466,11 +457,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "fa1afe1",
Action: todo.Pick,
Status: models.StatusConflicted,
}),
expectedSha: "fa1afe1",
},
{
testName: "'edit' with amend file",
@@ -482,10 +469,10 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: true,
expectedResult: nil,
expectedSha: "",
},
{
testName: "'edit' without amend file but message file",
testName: "'edit' without amend file",
todos: []todo.Todo{},
doneTodos: []todo.Todo{
{
@@ -493,37 +480,19 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
Commit: "fa1afe1",
},
},
amendFileExists: false,
messageFileExists: true,
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "fa1afe1",
Action: todo.Edit,
Status: models.StatusConflicted,
}),
},
{
testName: "'edit' without amend and without message file",
todos: []todo.Todo{},
doneTodos: []todo.Todo{
{
Command: todo.Edit,
Commit: "fa1afe1",
},
},
amendFileExists: false,
messageFileExists: false,
expectedResult: nil,
amendFileExists: false,
expectedSha: "fa1afe1",
},
}
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
common := common.NewDummyCommon()
common := utils.NewDummyCommon()
builder := &CommitLoader{
Common: common,
cmd: oscommands.NewDummyCmdObjBuilder(oscommands.NewFakeRunner(t)),
getWorkingTreeState: func() models.WorkingTreeState { return models.WorkingTreeState{Rebasing: true} },
dotGitDir: ".git",
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
},
@@ -532,265 +501,8 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
}
hash := builder.getConflictedCommitImpl(hashPool, scenario.todos, scenario.doneTodos, scenario.amendFileExists, scenario.messageFileExists)
assert.Equal(t, scenario.expectedResult, hash)
})
}
}
func TestCommitLoader_setCommitStatuses(t *testing.T) {
type scenario struct {
testName string
commitOpts []models.NewCommitOpts
unmergedCommits []string
unpushedCommits []string
expectedCommitOpts []models.NewCommitOpts
}
scenarios := []scenario{
{
testName: "basic",
commitOpts: []models.NewCommitOpts{
{Hash: "12345", Name: "1", Action: models.ActionNone},
{Hash: "67890", Name: "2", Action: models.ActionNone},
{Hash: "abcde", Name: "3", Action: models.ActionNone},
},
unmergedCommits: []string{"12345"},
expectedCommitOpts: []models.NewCommitOpts{
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusPushed},
{Hash: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusMerged},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusMerged},
},
},
{
testName: "with update-ref",
commitOpts: []models.NewCommitOpts{
{Hash: "12345", Name: "1", Action: models.ActionNone},
{Hash: "", Name: "", Action: todo.UpdateRef},
{Hash: "abcde", Name: "3", Action: models.ActionNone},
},
unmergedCommits: []string{"12345", "abcde"},
unpushedCommits: []string{"12345"},
expectedCommitOpts: []models.NewCommitOpts{
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Hash: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
},
},
}
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
hashPool := &utils.StringPool{}
commits := lo.Map(scenario.commitOpts,
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
setCommitStatuses(set.NewFromSlice(scenario.unpushedCommits), set.NewFromSlice(scenario.unmergedCommits), commits)
expectedCommits := lo.Map(scenario.expectedCommitOpts,
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
assert.Equal(t, expectedCommits, commits)
})
}
}
func TestCommitLoader_extractCommitFromLine(t *testing.T) {
common := common.NewDummyCommon()
hashPool := &utils.StringPool{}
loader := &CommitLoader{
Common: common,
}
scenarios := []struct {
testName string
line string
showDivergence bool
expectedCommit *models.Commit
}{
{
testName: "normal commit line with all fields",
line: "0eea75e8c631fba6b58135697835d58ba4c18dbc\x001640826609\x00Jesse Duffield\x00jessedduffield@gmail.com\x00b21997d6b4cbdf84b149\x00>\x00HEAD -> better-tests\x00better typing for rebase mode",
showDivergence: false,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Tags: nil,
ExtraInfo: "(HEAD -> better-tests)",
UnixTimestamp: 1640826609,
AuthorName: "Jesse Duffield",
AuthorEmail: "jessedduffield@gmail.com",
Parents: []string{"b21997d6b4cbdf84b149"},
Divergence: models.DivergenceNone,
}),
},
{
testName: "normal commit line with left divergence",
line: "hash123\x001234567890\x00John Doe\x00john@example.com\x00parent1 parent2\x00<\x00origin/main\x00commit message",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "hash123",
Name: "commit message",
Tags: nil,
ExtraInfo: "(origin/main)",
UnixTimestamp: 1234567890,
AuthorName: "John Doe",
AuthorEmail: "john@example.com",
Parents: []string{"parent1", "parent2"},
Divergence: models.DivergenceLeft,
}),
},
{
testName: "commit line with tags in extraInfo",
line: "abc123\x001640000000\x00Jane Smith\x00jane@example.com\x00parenthash\x00>\x00tag: v1.0, tag: release\x00tagged release",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "abc123",
Name: "tagged release",
Tags: []string{"v1.0", "release"},
ExtraInfo: "(tag: v1.0, tag: release)",
UnixTimestamp: 1640000000,
AuthorName: "Jane Smith",
AuthorEmail: "jane@example.com",
Parents: []string{"parenthash"},
Divergence: models.DivergenceRight,
}),
},
{
testName: "commit line with empty extraInfo",
line: "def456\x001640000000\x00Bob Wilson\x00bob@example.com\x00parenthash\x00>\x00\x00simple commit",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "def456",
Name: "simple commit",
Tags: nil,
ExtraInfo: "",
UnixTimestamp: 1640000000,
AuthorName: "Bob Wilson",
AuthorEmail: "bob@example.com",
Parents: []string{"parenthash"},
Divergence: models.DivergenceRight,
}),
},
{
testName: "commit line with no parents (root commit)",
line: "root123\x001640000000\x00Alice Cooper\x00alice@example.com\x00\x00>\x00\x00initial commit",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "root123",
Name: "initial commit",
Tags: nil,
ExtraInfo: "",
UnixTimestamp: 1640000000,
AuthorName: "Alice Cooper",
AuthorEmail: "alice@example.com",
Parents: nil,
Divergence: models.DivergenceRight,
}),
},
{
testName: "malformed line with only 3 fields",
line: "hash\x00timestamp\x00author",
showDivergence: false,
expectedCommit: nil,
},
{
testName: "malformed line with only 4 fields",
line: "hash\x00timestamp\x00author\x00email",
showDivergence: false,
expectedCommit: nil,
},
{
testName: "malformed line with only 5 fields",
line: "hash\x00timestamp\x00author\x00email\x00parents",
showDivergence: false,
expectedCommit: nil,
},
{
testName: "malformed line with only 6 fields",
line: "hash\x00timestamp\x00author\x00email\x00parents\x00<",
showDivergence: true,
expectedCommit: nil,
},
{
testName: "minimal valid line with 7 fields (no message)",
line: "hash\x00timestamp\x00author\x00email\x00parents\x00>\x00extraInfo",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "hash",
Name: "",
Tags: nil,
ExtraInfo: "(extraInfo)",
UnixTimestamp: 0,
AuthorName: "author",
AuthorEmail: "email",
Parents: []string{"parents"},
Divergence: models.DivergenceRight,
}),
},
{
testName: "minimal valid line with 7 fields (empty extraInfo)",
line: "hash\x00timestamp\x00author\x00email\x00parents\x00>\x00",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "hash",
Name: "",
Tags: nil,
ExtraInfo: "",
UnixTimestamp: 0,
AuthorName: "author",
AuthorEmail: "email",
Parents: []string{"parents"},
Divergence: models.DivergenceRight,
}),
},
{
testName: "valid line with 8 fields (complete)",
line: "hash\x00timestamp\x00author\x00email\x00parents\x00<\x00extraInfo\x00message",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "hash",
Name: "message",
Tags: nil,
ExtraInfo: "(extraInfo)",
UnixTimestamp: 0,
AuthorName: "author",
AuthorEmail: "email",
Parents: []string{"parents"},
Divergence: models.DivergenceLeft,
}),
},
{
testName: "empty line",
line: "",
showDivergence: false,
expectedCommit: nil,
},
{
testName: "line with special characters in commit message",
line: "special123\x001640000000\x00Dev User\x00dev@example.com\x00parenthash\x00>\x00\x00fix: handle \x00 null bytes and 'quotes'",
showDivergence: true,
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "special123",
Name: "fix: handle \x00 null bytes and 'quotes'",
Tags: nil,
ExtraInfo: "",
UnixTimestamp: 1640000000,
AuthorName: "Dev User",
AuthorEmail: "dev@example.com",
Parents: []string{"parenthash"},
Divergence: models.DivergenceRight,
}),
},
}
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
result := loader.extractCommitFromLine(hashPool, scenario.line, scenario.showDivergence)
if scenario.expectedCommit == nil {
assert.Nil(t, result)
} else {
assert.Equal(t, scenario.expectedCommit, result)
}
sha := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists)
assert.Equal(t, scenario.expectedSha, sha)
})
}
}
@@ -1,74 +0,0 @@
package git_commands
import (
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/samber/lo"
)
func loadCommits(
cmd *oscommands.CmdObj,
filterPath string,
parseLogLine func(string) (*models.Commit, bool),
) ([]*models.Commit, error) {
commits := []*models.Commit{}
var commit *models.Commit
var filterPaths []string
// A string pool that stores interned strings to reduce memory usage
pool := make(map[string]string)
finishLastCommit := func() {
if commit != nil {
// Only set the filter paths if we have one that is not contained in the original
// filter path. When filtering on a directory, all file paths will start with that
// directory, so we needn't bother storing the individual paths. Likewise, if we
// filter on a file and the file path hasn't changed, we needn't store it either.
// Only if a file has been moved or renamed do we need to store the paths, but then
// we need them all so that we can properly render a diff for the rename.
if lo.SomeBy(filterPaths, func(path string) bool {
return !strings.HasPrefix(path, filterPath)
}) {
commit.FilterPaths = lo.Map(filterPaths, func(path string, _ int) string {
if v, ok := pool[path]; ok {
return v
}
pool[path] = path
return path
})
}
commits = append(commits, commit)
commit = nil
filterPaths = nil
}
}
err := cmd.RunAndProcessLines(func(line string) (bool, error) {
if line == "" {
return false, nil
}
if line[0] == '+' {
finishLastCommit()
var stop bool
commit, stop = parseLogLine(line[1:])
if stop {
commit = nil
return true, nil
}
} else if commit != nil && filterPath != "" {
// We are filtering by path, and this line is the output of the --name-status flag
fields := strings.Split(line, "\t")
// We don't bother looking at the first field (it will be 'A', 'M', 'R072' or a bunch of others).
// All we care about is the path(s), and there will be one for 'M' and 'A', and two for 'R' or 'C',
// in which case we want them both so that we can show the diff between the two.
if len(fields) > 1 {
filterPaths = append(filterPaths, fields[1:]...)
}
}
return false, nil
})
finishLastCommit()
return commits, err
}
+59 -238
View File
@@ -10,30 +10,28 @@ import (
func TestCommitRewordCommit(t *testing.T) {
type scenario struct {
testName string
runner *oscommands.FakeCmdObjRunner
summary string
description string
testName string
runner *oscommands.FakeCmdObjRunner
input string
}
scenarios := []scenario{
{
"Single line reword",
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"commit", "--allow-empty", "--amend", "--only", "-m", "test"}, "", nil),
"test",
"",
},
{
"Multi line reword",
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"commit", "--allow-empty", "--amend", "--only", "-m", "test", "-m", "line 2\nline 3"}, "", nil),
"test",
"line 2\nline 3",
"test\nline 2\nline 3",
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
assert.NoError(t, instance.RewordLastCommit(s.summary, s.description).Run())
assert.NoError(t, instance.RewordLastCommit(s.input))
s.runner.CheckForMissingCalls()
})
}
@@ -52,9 +50,7 @@ func TestCommitResetToCommit(t *testing.T) {
func TestCommitCommitCmdObj(t *testing.T) {
type scenario struct {
testName string
summary string
forceSkipHooks bool
description string
message string
configSignoff bool
configSkipHookPrefix string
expectedArgs []string
@@ -63,57 +59,35 @@ func TestCommitCommitCmdObj(t *testing.T) {
scenarios := []scenario{
{
testName: "Commit",
summary: "test",
forceSkipHooks: false,
message: "test",
configSignoff: false,
configSkipHookPrefix: "",
expectedArgs: []string{"commit", "-m", "test"},
},
{
testName: "Commit with --no-verify flag < only prefix",
summary: "WIP: test",
forceSkipHooks: false,
testName: "Commit with --no-verify flag",
message: "WIP: test",
configSignoff: false,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
},
{
testName: "Commit with --no-verify flag < skip flag and prefix",
summary: "WIP: test",
forceSkipHooks: true,
configSignoff: false,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
},
{
testName: "Commit with --no-verify flag < skip flag no prefix",
summary: "test",
forceSkipHooks: true,
configSignoff: false,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "-m", "test"},
},
{
testName: "Commit with multiline message",
summary: "line1",
forceSkipHooks: false,
description: "line2",
message: "line1\nline2",
configSignoff: false,
configSkipHookPrefix: "",
expectedArgs: []string{"commit", "-m", "line1", "-m", "line2"},
},
{
testName: "Commit with signoff",
summary: "test",
forceSkipHooks: false,
message: "test",
configSignoff: true,
configSkipHookPrefix: "",
expectedArgs: []string{"commit", "--signoff", "-m", "test"},
},
{
testName: "Commit with signoff and no-verify",
summary: "WIP: test",
forceSkipHooks: true,
message: "WIP: test",
configSignoff: true,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "--signoff", "-m", "WIP: test"},
@@ -121,6 +95,7 @@ func TestCommitCommitCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.Commit.SignOff = s.configSignoff
@@ -129,7 +104,7 @@ func TestCommitCommitCmdObj(t *testing.T) {
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expectedArgs, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
assert.NoError(t, instance.CommitCmdObj(s.summary, s.description, s.forceSkipHooks).Run())
assert.NoError(t, instance.CommitCmdObj(s.message).Run())
runner.CheckForMissingCalls()
})
}
@@ -156,6 +131,7 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.Commit.SignOff = s.configSignoff
@@ -172,7 +148,7 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
func TestCommitCreateFixupCommit(t *testing.T) {
type scenario struct {
testName string
hash string
sha string
runner *oscommands.FakeCmdObjRunner
test func(error)
}
@@ -180,7 +156,7 @@ func TestCommitCreateFixupCommit(t *testing.T) {
scenarios := []scenario{
{
testName: "valid case",
hash: "12345",
sha: "12345",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "--fixup=12345"}, "", nil),
test: func(err error) {
@@ -190,59 +166,10 @@ func TestCommitCreateFixupCommit(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
s.test(instance.CreateFixupCommit(s.hash))
s.runner.CheckForMissingCalls()
})
}
}
func TestCommitCreateAmendCommit(t *testing.T) {
type scenario struct {
testName string
originalSubject string
newSubject string
newDescription string
includeFileChanges bool
runner *oscommands.FakeCmdObjRunner
}
scenarios := []scenario{
{
testName: "subject only",
originalSubject: "original subject",
newSubject: "new subject",
newDescription: "",
includeFileChanges: true,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject"}, "", nil),
},
{
testName: "subject and description",
originalSubject: "original subject",
newSubject: "new subject",
newDescription: "new description",
includeFileChanges: true,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject\n\nnew description"}, "", nil),
},
{
testName: "without file changes",
originalSubject: "original subject",
newSubject: "new subject",
newDescription: "",
includeFileChanges: false,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject", "--only", "--allow-empty"}, "", nil),
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
err := instance.CreateAmendCommit(s.originalSubject, s.newSubject, s.newDescription, s.includeFileChanges)
assert.NoError(t, err)
s.test(instance.CreateFixupCommit(s.sha))
s.runner.CheckForMissingCalls()
})
}
@@ -250,98 +177,54 @@ func TestCommitCreateAmendCommit(t *testing.T) {
func TestCommitShowCmdObj(t *testing.T) {
type scenario struct {
testName string
filterPaths []string
contextSize uint64
similarityThreshold int
ignoreWhitespace bool
pagerConfig *config.PagingConfig
expected []string
testName string
filterPath string
contextSize int
ignoreWhitespace bool
expected []string
}
scenarios := []scenario{
{
testName: "Default case without filter path",
filterPaths: []string{},
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
pagerConfig: nil,
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
testName: "Default case without filter path",
filterPath: "",
contextSize: 3,
ignoreWhitespace: false,
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
},
{
testName: "Default case with filter path",
filterPaths: []string{"file.txt"},
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
pagerConfig: nil,
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--", "file.txt"},
testName: "Default case with filter path",
filterPath: "file.txt",
contextSize: 3,
ignoreWhitespace: false,
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
},
{
testName: "Show diff with custom context size",
filterPaths: []string{},
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: false,
pagerConfig: nil,
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
testName: "Show diff with custom context size",
filterPath: "",
contextSize: 77,
ignoreWhitespace: false,
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
},
{
testName: "Show diff with custom similarity threshold",
filterPaths: []string{},
contextSize: 3,
similarityThreshold: 33,
ignoreWhitespace: false,
pagerConfig: nil,
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%", "--"},
},
{
testName: "Show diff, ignoring whitespace",
filterPaths: []string{},
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: true,
pagerConfig: nil,
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%", "--"},
},
{
testName: "Show diff with external diff command",
filterPaths: []string{},
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
pagerConfig: &config.PagingConfig{ExternalDiffCommand: "difft --color=always"},
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
},
{
testName: "Show diff using git's external diff config",
filterPaths: []string{},
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
pagerConfig: &config.PagingConfig{UseExternalDiffGitConfig: true},
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
testName: "Show diff, ignoring whitespace",
filterPath: "",
contextSize: 77,
ignoreWhitespace: true,
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
if s.pagerConfig != nil {
userConfig.Git.Pagers = []config.PagingConfig{*s.pagerConfig}
}
userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
userConfig.Git.DiffContextSize = s.contextSize
userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
repoPaths := RepoPaths{
worktreePath: "/path/to/worktree",
}
instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: &config.AppState{}, runner: runner, repoPaths: &repoPaths})
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPaths).Run())
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).Run())
runner.CheckForMissingCalls()
})
}
@@ -356,17 +239,19 @@ func TestGetCommitMsg(t *testing.T) {
scenarios := []scenario{
{
"empty",
``,
` commit deadbeef`,
``,
},
{
"no line breaks (single line)",
`use generics to DRY up context code`,
`commit deadbeef
use generics to DRY up context code`,
`use generics to DRY up context code`,
},
{
"with line breaks",
`Merge pull request #1750 from mark2185/fix-issue-template
`commit deadbeef
Merge pull request #1750 from mark2185/fix-issue-template
'git-rev parse' should be 'git rev-parse'`,
`Merge pull request #1750 from mark2185/fix-issue-template
@@ -376,9 +261,10 @@ func TestGetCommitMsg(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"-c", "log.showsignature=false", "log", "--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")
@@ -399,14 +285,15 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
scenarios := []scenario{
{
"Empty message",
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"-c", "log.showsignature=false", "log", "--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) {
assert.Error(t, err)
},
},
{
"Default case to retrieve a commit in history",
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "hash3 \n", nil).ExpectGitArgs([]string{"-c", "log.showsignature=false", "log", "--format=%B", "--max-count=1", "hash3"}, `use generics to DRY up context code`, nil),
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),
func(output string, err error) {
assert.NoError(t, err)
assert.Equal(t, "use generics to DRY up context code", output)
@@ -415,6 +302,7 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
@@ -424,70 +312,3 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
})
}
}
func TestAddCoAuthorToMessage(t *testing.T) {
scenarios := []struct {
name string
message string
expectedResult string
}{
{
// This never happens, I think it isn't possible to create a commit
// with an empty message. Just including it for completeness.
name: "Empty message",
message: "",
expectedResult: "\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Just a subject, no body",
message: "Subject",
expectedResult: "Subject\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Subject and body",
message: "Subject\n\nBody",
expectedResult: "Subject\n\nBody\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Body already ending with a Co-authored-by line",
message: "Subject\n\nBody\n\nCo-authored-by: Jane Smith <jane@smith.com>",
expectedResult: "Subject\n\nBody\n\nCo-authored-by: Jane Smith <jane@smith.com>\nCo-authored-by: John Doe <john@doe.com>",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
result := AddCoAuthorToMessage(s.message, "John Doe <john@doe.com>")
assert.Equal(t, s.expectedResult, result)
})
}
}
func TestAddCoAuthorToDescription(t *testing.T) {
scenarios := []struct {
name string
description string
expectedResult string
}{
{
name: "Empty description",
description: "",
expectedResult: "Co-authored-by: John Doe <john@doe.com>",
},
{
name: "Non-empty description",
description: "Body",
expectedResult: "Body\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Description already ending with a Co-authored-by line",
description: "Body\n\nCo-authored-by: Jane Smith <jane@smith.com>",
expectedResult: "Body\n\nCo-authored-by: Jane Smith <jane@smith.com>\nCo-authored-by: John Doe <john@doe.com>",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
result := AddCoAuthorToDescription(s.description, "John Doe <john@doe.com>")
assert.Equal(t, s.expectedResult, result)
})
}
}
+19 -18
View File
@@ -4,18 +4,19 @@ import (
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/sasha-s/go-deadlock"
)
type GitCommon struct {
*common.Common
version *GitVersion
cmd oscommands.ICmdObjBuilder
os *oscommands.OSCommand
repoPaths *RepoPaths
repo *gogit.Repository
config *ConfigCommands
pagerConfig *config.PagerConfig
version *GitVersion
cmd oscommands.ICmdObjBuilder
os *oscommands.OSCommand
dotGitDir string
repo *gogit.Repository
config *ConfigCommands
// mutex for doing things like push/pull/fetch
syncMutex *deadlock.Mutex
}
func NewGitCommon(
@@ -23,19 +24,19 @@ func NewGitCommon(
version *GitVersion,
cmd oscommands.ICmdObjBuilder,
osCommand *oscommands.OSCommand,
repoPaths *RepoPaths,
dotGitDir string,
repo *gogit.Repository,
config *ConfigCommands,
pagerConfig *config.PagerConfig,
syncMutex *deadlock.Mutex,
) *GitCommon {
return &GitCommon{
Common: cmn,
version: version,
cmd: cmd,
os: osCommand,
repoPaths: repoPaths,
repo: repo,
config: config,
pagerConfig: pagerConfig,
Common: cmn,
version: version,
cmd: cmd,
os: osCommand,
dotGitDir: dotGitDir,
repo: repo,
config: config,
syncMutex: syncMutex,
}
}
+34 -31
View File
@@ -1,10 +1,15 @@
package git_commands
import (
"os"
"strconv"
"strings"
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/go-git/v5/config"
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type ConfigCommands struct {
@@ -26,31 +31,41 @@ func NewConfigCommands(
}
}
type GpgConfigKey string
func (self *ConfigCommands) ConfiguredPager() string {
if os.Getenv("GIT_PAGER") != "" {
return os.Getenv("GIT_PAGER")
}
if os.Getenv("PAGER") != "" {
return os.Getenv("PAGER")
}
output := self.gitConfig.Get("core.pager")
return strings.Split(output, "\n")[0]
}
const (
CommitGpgSign GpgConfigKey = "commit.gpgSign"
TagGpgSign GpgConfigKey = "tag.gpgSign"
)
func (self *ConfigCommands) GetPager(width int) string {
useConfig := self.UserConfig.Git.Paging.UseConfig
if useConfig {
pager := self.ConfiguredPager()
return strings.Split(pager, "| less")[0]
}
// NeedsGpgSubprocess tells us whether the user has gpg enabled for the specified action type
// and needs a subprocess because they have a process where they manually
// enter their password every time a GPG action is taken
func (self *ConfigCommands) NeedsGpgSubprocess(key GpgConfigKey) bool {
overrideGpg := self.UserConfig().Git.OverrideGpg
templateValues := map[string]string{
"columnWidth": strconv.Itoa(width/2 - 6),
}
pagerTemplate := self.UserConfig.Git.Paging.Pager
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
}
// UsingGpg tells us whether the user has gpg enabled so that we can know
// whether we need to run a subprocess to allow them to enter their password
func (self *ConfigCommands) UsingGpg() bool {
overrideGpg := self.UserConfig.Git.OverrideGpg
if overrideGpg {
return false
}
return self.gitConfig.GetBool(string(key))
}
func (self *ConfigCommands) NeedsGpgSubprocessForCommit() bool {
return self.NeedsGpgSubprocess(CommitGpgSign)
}
func (self *ConfigCommands) GetGpgTagSign() bool {
return self.gitConfig.GetBool(string(TagGpgSign))
return self.gitConfig.GetBool("commit.gpgsign")
}
func (self *ConfigCommands) GetCoreEditor() string {
@@ -92,15 +107,3 @@ func (self *ConfigCommands) GetCoreCommentChar() byte {
return '#'
}
func (self *ConfigCommands) GetRebaseUpdateRefs() bool {
return self.gitConfig.GetBool("rebase.updateRefs")
}
func (self *ConfigCommands) GetMergeFF() string {
return self.gitConfig.Get("merge.ff")
}
func (self *ConfigCommands) DropConfigCache() {
self.gitConfig.DropCache()
}
+1 -21
View File
@@ -1,11 +1,6 @@
package git_commands
import (
"fmt"
"strings"
"github.com/mgutz/str"
)
import "github.com/mgutz/str"
type CustomCommands struct {
*GitCommon
@@ -23,18 +18,3 @@ func NewCustomCommands(gitCommon *GitCommon) *CustomCommands {
func (self *CustomCommands) RunWithOutput(cmdStr string) (string, error) {
return self.cmd.New(str.ToArgv(cmdStr)).RunWithOutput()
}
// A function that can be used as a "runCommand" entry in the template.FuncMap of templates.
func (self *CustomCommands) TemplateFunctionRunCommand(cmdStr string) (string, error) {
output, err := self.RunWithOutput(cmdStr)
if err != nil {
return "", err
}
output = strings.TrimRight(output, "\r\n")
if strings.Contains(output, "\r\n") {
return "", fmt.Errorf("command output contains newlines: %s", output)
}
return output, nil
}
+34 -24
View File
@@ -7,23 +7,22 @@ import (
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
"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/config"
"github.com/spf13/afero"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type commonDeps struct {
runner *oscommands.FakeCmdObjRunner
userConfig *config.UserConfig
appState *config.AppState
gitVersion *GitVersion
gitConfig *git_config.FakeGitConfig
getenv func(string) string
removeFile func(string) error
dotGitDir string
common *common.Common
cmd *oscommands.CmdObjBuilder
fs afero.Fs
repoPaths *RepoPaths
}
func buildGitCommon(deps commonDeps) *GitCommon {
@@ -31,17 +30,7 @@ func buildGitCommon(deps commonDeps) *GitCommon {
gitCommon.Common = deps.common
if gitCommon.Common == nil {
gitCommon.Common = common.NewDummyCommonWithUserConfigAndAppState(deps.userConfig, deps.appState)
}
if deps.fs != nil {
gitCommon.Fs = deps.fs
}
if deps.repoPaths != nil {
gitCommon.repoPaths = deps.repoPaths
} else {
gitCommon.repoPaths = MockRepoPaths(".git")
gitCommon.Common = utils.NewDummyCommonWithUserConfig(deps.userConfig)
}
runner := deps.runner
@@ -56,15 +45,11 @@ func buildGitCommon(deps commonDeps) *GitCommon {
}
gitCommon.cmd = cmd
gitCommon.Common.SetUserConfig(deps.userConfig)
if gitCommon.Common.UserConfig() == nil {
gitCommon.Common.SetUserConfig(config.GetDefaultConfig())
gitCommon.Common.UserConfig = deps.userConfig
if gitCommon.Common.UserConfig == nil {
gitCommon.Common.UserConfig = config.GetDefaultConfig()
}
gitCommon.pagerConfig = config.NewPagerConfig(func() *config.UserConfig {
return gitCommon.Common.UserConfig()
})
gitCommon.version = deps.gitVersion
if gitCommon.version == nil {
gitCommon.version = &GitVersion{2, 0, 0, ""}
@@ -96,17 +81,22 @@ func buildGitCommon(deps commonDeps) *GitCommon {
TempDir: os.TempDir(),
})
gitCommon.dotGitDir = deps.dotGitDir
if gitCommon.dotGitDir == "" {
gitCommon.dotGitDir = ".git"
}
return gitCommon
}
func buildRepo() *gogit.Repository {
// TODO: think of a way to actually mock this out
var repo *gogit.Repository
var repo *gogit.Repository = nil
return repo
}
func buildFileLoader(gitCommon *GitCommon) *FileLoader {
return NewFileLoader(gitCommon, gitCommon.cmd, gitCommon.config)
return NewFileLoader(gitCommon.Common, gitCommon.cmd, gitCommon.config)
}
func buildSubmoduleCommands(deps commonDeps) *SubmoduleCommands {
@@ -128,6 +118,26 @@ func buildWorkingTreeCommands(deps commonDeps) *WorkingTreeCommands {
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 {
gitCommon := buildGitCommon(deps)
fileLoader := buildFileLoader(gitCommon)
+3 -90
View File
@@ -1,10 +1,6 @@
package git_commands
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
import "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
type DiffCommands struct {
*GitCommon
@@ -16,91 +12,8 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
}
}
// This is for generating diffs to be shown in the UI (e.g. rendering a range
// diff to the main view). It uses a custom pager if one is configured.
func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj {
extDiffCmd := self.pagerConfig.GetExternalDiffCommand()
useExtDiff := extDiffCmd != ""
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig()
ignoreWhitespace := self.UserConfig().Git.IgnoreWhitespaceInDiffView
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
return self.cmd.New(
NewGitCmd("diff").
Config("diff.noprefix=false").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
Arg("--submodule").
Arg(fmt.Sprintf("--color=%s", self.pagerConfig.GetColorArg())).
ArgIf(ignoreWhitespace, "--ignore-all-space").
Arg(fmt.Sprintf("--unified=%d", self.UserConfig().Git.DiffContextSize)).
Arg(diffArgs...).
Dir(self.repoPaths.worktreePath).
ToArgv(),
)
}
// This is a basic generic diff command that can be used for any diff operation
// (e.g. copying a diff to the clipboard). It will not use a custom pager, and
// does not use user configs such as ignore whitespace.
// If you want to diff specific refs (one or two), you need to add them yourself
// in additionalArgs; it is recommended to also pass `--` after that. If you
// want to restrict the diff to specific paths, pass them in additionalArgs
// after the `--`.
func (self *DiffCommands) GetDiff(staged bool, additionalArgs ...string) (string, error) {
return self.cmd.New(
NewGitCmd("diff").
Config("diff.noprefix=false").
Arg("--no-ext-diff", "--no-color").
ArgIf(staged, "--staged").
Dir(self.repoPaths.worktreePath).
Arg(additionalArgs...).
ToArgv(),
).RunWithOutput()
}
type DiffToolCmdOptions struct {
// The path to show a diff for. Pass "." for the entire repo.
Filepath string
// The commit against which to show the diff. Leave empty to show a diff of
// the working copy.
FromCommit string
// The commit to diff against FromCommit. Leave empty to diff the working
// copy against FromCommit. Leave both FromCommit and ToCommit empty to show
// the diff of the unstaged working copy changes against the index if Staged
// is false, or the staged changes against HEAD if Staged is true.
ToCommit string
// Whether to reverse the left and right sides of the diff.
Reverse bool
// Whether the given Filepath is a directory. We'll pass --dir-diff to
// git-difftool in that case.
IsDirectory bool
// Whether to show the staged or the unstaged changes. Must be false if both
// FromCommit and ToCommit are non-empty.
Staged bool
}
func (self *DiffCommands) OpenDiffToolCmdObj(opts DiffToolCmdOptions) *oscommands.CmdObj {
return self.cmd.New(NewGitCmd("difftool").
Arg("--no-prompt").
ArgIf(opts.IsDirectory, "--dir-diff").
ArgIf(opts.Staged, "--cached").
ArgIf(opts.FromCommit != "", opts.FromCommit).
ArgIf(opts.ToCommit != "", opts.ToCommit).
ArgIf(opts.Reverse, "-R").
Arg("--", opts.Filepath).
ToArgv())
}
func (self *DiffCommands) DiffIndexCmdObj(diffArgs ...string) *oscommands.CmdObj {
return self.cmd.New(
NewGitCmd("diff-index").
Config("diff.noprefix=false").
Arg("--submodule", "--no-ext-diff", "--no-color", "--patch").
Arg(diffArgs...).ToArgv(),
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
)
}
+75 -20
View File
@@ -5,9 +5,9 @@ import (
"strconv"
"strings"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
type FileCommands struct {
@@ -29,20 +29,79 @@ func (self *FileCommands) Cat(fileName string) (string, error) {
return string(buf), nil
}
func (self *FileCommands) GetEditCmdStr(filenames []string) (string, bool) {
template, suspend := config.GetEditTemplate(self.os.Platform.Shell, &self.UserConfig().OS, self.guessDefaultEditor)
quotedFilenames := lo.Map(filenames, func(filename string, _ int) string { return self.cmd.Quote(filename) })
func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (string, error) {
editor := self.UserConfig.OS.EditCommand
if editor == "" {
editor = self.config.GetCoreEditor()
}
if editor == "" {
editor = self.os.Getenv("GIT_EDITOR")
}
if editor == "" {
editor = self.os.Getenv("VISUAL")
}
if editor == "" {
editor = self.os.Getenv("EDITOR")
}
if editor == "" {
if err := self.cmd.New([]string{"which", "vi"}).DontLog().Run(); err == nil {
editor = "vi"
}
}
if editor == "" {
return "", errors.New("No editor defined in config file, $GIT_EDITOR, $VISUAL, $EDITOR, or git config")
}
templateValues := map[string]string{
"filename": strings.Join(quotedFilenames, " "),
"editor": editor,
"filename": self.cmd.Quote(filename),
"line": strconv.Itoa(lineNumber),
}
editCmdTemplate := self.UserConfig.OS.EditCommandTemplate
if len(editCmdTemplate) == 0 {
switch editor {
case "emacs", "nano", "vi", "vim", "nvim":
editCmdTemplate = "{{editor}} +{{line}} -- {{filename}}"
case "subl":
editCmdTemplate = "{{editor}} -- {{filename}}:{{line}}"
case "code":
editCmdTemplate = "{{editor}} -r --goto -- {{filename}}:{{line}}"
default:
editCmdTemplate = "{{editor}} -- {{filename}}"
}
}
return utils.ResolvePlaceholderString(editCmdTemplate, templateValues), nil
}
func (self *FileCommands) GetEditCmdStr(filename string) (string, bool) {
// Legacy support for old config; to be removed at some point
if self.UserConfig.OS.Edit == "" && self.UserConfig.OS.EditCommandTemplate != "" {
if cmdStr, err := self.GetEditCmdStrLegacy(filename, 1); err == nil {
return cmdStr, true
}
}
template, editInTerminal := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr, suspend
return cmdStr, editInTerminal
}
func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (string, bool) {
template, suspend := config.GetEditAtLineTemplate(self.os.Platform.Shell, &self.UserConfig().OS, self.guessDefaultEditor)
// Legacy support for old config; to be removed at some point
if self.UserConfig.OS.EditAtLine == "" && self.UserConfig.OS.EditCommandTemplate != "" {
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
return cmdStr, true
}
}
template, editInTerminal := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
@@ -50,11 +109,18 @@ func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr, suspend
return cmdStr, editInTerminal
}
func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber int) string {
template := config.GetEditAtLineAndWaitTemplate(self.os.Platform.Shell, &self.UserConfig().OS, self.guessDefaultEditor)
// Legacy support for old config; to be removed at some point
if self.UserConfig.OS.EditAtLineAndWait == "" && self.UserConfig.OS.EditCommandTemplate != "" {
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
return cmdStr
}
}
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
@@ -65,17 +131,6 @@ func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber
return cmdStr
}
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) (string, bool) {
template, suspend := config.GetOpenDirInEditorTemplate(self.os.Platform.Shell, &self.UserConfig().OS, self.guessDefaultEditor)
templateValues := map[string]string{
"dir": self.cmd.Quote(path),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr, suspend
}
func (self *FileCommands) guessDefaultEditor() string {
// Try to query a few places where editors get configured
editor := self.config.GetCoreEditor()
+19 -109
View File
@@ -2,12 +2,11 @@ package git_commands
import (
"fmt"
"path/filepath"
"strconv"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
)
type FileLoaderConfig interface {
@@ -15,15 +14,15 @@ type FileLoaderConfig interface {
}
type FileLoader struct {
*GitCommon
*common.Common
cmd oscommands.ICmdObjBuilder
config FileLoaderConfig
getFileType func(string) string
}
func NewFileLoader(gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, config FileLoaderConfig) *FileLoader {
func NewFileLoader(cmn *common.Common, cmd oscommands.ICmdObjBuilder, config FileLoaderConfig) *FileLoader {
return &FileLoader{
GitCommon: gitCommon,
Common: cmn,
cmd: cmd,
getFileType: oscommands.FileType,
config: config,
@@ -32,17 +31,13 @@ func NewFileLoader(gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, config F
type GetStatusFileOptions struct {
NoRenames bool
// If true, we'll show untracked files even if the user has set the config to hide them.
// This is useful for users with bare repos for dotfiles who default to hiding untracked files,
// but want to occasionally see them to `git add` a new file.
ForceShowUntracked bool
}
func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
// check if config wants us ignoring untracked files
untrackedFilesSetting := self.config.GetShowUntrackedFiles()
if opts.ForceShowUntracked || untrackedFilesSetting == "" {
if untrackedFilesSetting == "" {
untrackedFilesSetting = "all"
}
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
@@ -53,14 +48,6 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
}
files := []*models.File{}
fileDiffs := map[string]FileDiff{}
if self.GitCommon.Common.UserConfig().Gui.ShowNumstatInFilesView {
fileDiffs, err = self.getFileDiffs()
if err != nil {
self.Log.Error(err)
}
}
for _, status := range statuses {
if strings.HasPrefix(status.StatusString, "warning") {
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
@@ -68,82 +55,19 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
}
file := &models.File{
Path: status.Path,
PreviousPath: status.PreviousPath,
Name: status.Name,
PreviousName: status.PreviousName,
DisplayString: status.StatusString,
}
if diff, ok := fileDiffs[status.Path]; ok {
file.LinesAdded = diff.LinesAdded
file.LinesDeleted = diff.LinesDeleted
Type: self.getFileType(status.Name),
}
models.SetStatusFields(file, status.Change)
files = append(files, file)
}
// Go through the files to see if any of these files are actually worktrees
// so that we can render them correctly
worktreePaths := linkedWortkreePaths(self.Fs, self.repoPaths.RepoGitDirPath())
for _, file := range files {
for _, worktreePath := range worktreePaths {
absFilePath, err := filepath.Abs(file.Path)
if err != nil {
self.Log.Error(err)
continue
}
if absFilePath == worktreePath {
file.IsWorktree = true
// `git status` renders this worktree as a folder with a trailing slash but we'll represent it as a singular worktree
// If we include the slash, it will be rendered as a folder with a null file inside.
file.Path = strings.TrimSuffix(file.Path, "/")
break
}
}
}
return files
}
type FileDiff struct {
LinesAdded int
LinesDeleted int
}
func (self *FileLoader) getFileDiffs() (map[string]FileDiff, error) {
diffs, err := self.gitDiffNumStat()
if err != nil {
return nil, err
}
splitLines := strings.Split(diffs, "\x00")
fileDiffs := map[string]FileDiff{}
for _, line := range splitLines {
splitLine := strings.Split(line, "\t")
if len(splitLine) != 3 {
continue
}
linesAdded, err := strconv.Atoi(splitLine[0])
if err != nil {
continue
}
linesDeleted, err := strconv.Atoi(splitLine[1])
if err != nil {
continue
}
fileName := splitLine[2]
fileDiffs[fileName] = FileDiff{
LinesAdded: linesAdded,
LinesDeleted: linesDeleted,
}
}
return fileDiffs, nil
}
// GitStatus returns the file status of the repo
type GitStatusOptions struct {
NoRenames bool
@@ -153,33 +77,19 @@ type GitStatusOptions struct {
type FileStatus struct {
StatusString string
Change string // ??, MM, AM, ...
Path string
PreviousPath string
Name string
PreviousName string
}
func (self *FileLoader) gitDiffNumStat() (string, error) {
return self.cmd.New(
NewGitCmd("diff").
Arg("--numstat").
Arg("-z").
Arg("HEAD").
ToArgv(),
).DontLog().RunWithOutput()
}
func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
func (c *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
cmdArgs := NewGitCmd("status").
Arg(opts.UntrackedFilesArg).
Arg("--porcelain").
Arg("-z").
ArgIfElse(
opts.NoRenames,
"--no-renames",
fmt.Sprintf("--find-renames=%d%%", self.UserConfig().Git.RenameSimilarityThreshold),
).
ArgIf(opts.NoRenames, "--no-renames").
ToArgv()
statusLines, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
statusLines, _, err := c.cmd.New(cmdArgs).DontLog().RunWithOutputs()
if err != nil {
return []FileStatus{}, err
}
@@ -197,14 +107,14 @@ func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
status := FileStatus{
StatusString: original,
Change: original[:2],
Path: original[3:],
PreviousPath: "",
Name: original[3:],
PreviousName: "",
}
if strings.HasPrefix(status.Change, "R") || strings.HasPrefix(status.Change, "C") {
// if a line starts with 'R' (rename) or 'C' (copy) then the next line is the original file.
status.PreviousPath = splitLines[i+1]
status.StatusString = fmt.Sprintf("%s %s -> %s", status.Change, status.PreviousPath, status.Path)
if strings.HasPrefix(status.Change, "R") {
// if a line starts with 'R' then the next line is the original file.
status.PreviousName = splitLines[i+1]
status.StatusString = fmt.Sprintf("%s %s -> %s", status.Change, status.PreviousName, status.Name)
i++
}
+46 -99
View File
@@ -5,43 +5,34 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stretchr/testify/assert"
)
func TestFileGetStatusFiles(t *testing.T) {
type scenario struct {
testName string
similarityThreshold int
runner oscommands.ICmdObjRunner
showNumstatInFilesView bool
expectedFiles []*models.File
testName string
runner oscommands.ICmdObjRunner
expectedFiles []*models.File
}
scenarios := []scenario{
{
testName: "No files found",
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
expectedFiles: []*models.File{},
"No files found",
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "", nil),
[]*models.File{},
},
{
testName: "Several files found",
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
"Several files found",
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
nil,
).
ExpectGitArgs([]string{"diff", "--numstat", "-z", "HEAD"},
"4\t1\tfile1.txt\x001\t0\tfile2.txt\x002\t2\tfile3.txt\x000\t2\tfile4.txt\x002\t2\tfile5.txt",
nil,
),
showNumstatInFilesView: true,
expectedFiles: []*models.File{
[]*models.File{
{
Path: "file1.txt",
Name: "file1.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: true,
@@ -50,12 +41,11 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "MM file1.txt",
Type: "file",
ShortStatus: "MM",
LinesAdded: 4,
LinesDeleted: 1,
},
{
Path: "file3.txt",
Name: "file3.txt",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: false,
@@ -64,12 +54,11 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "A file3.txt",
Type: "file",
ShortStatus: "A ",
LinesAdded: 2,
LinesDeleted: 2,
},
{
Path: "file2.txt",
Name: "file2.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: false,
@@ -78,12 +67,11 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "AM file2.txt",
Type: "file",
ShortStatus: "AM",
LinesAdded: 1,
LinesDeleted: 0,
},
{
Path: "file4.txt",
Name: "file4.txt",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: false,
@@ -92,12 +80,11 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "?? file4.txt",
Type: "file",
ShortStatus: "??",
LinesAdded: 0,
LinesDeleted: 2,
},
{
Path: "file5.txt",
Name: "file5.txt",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: true,
@@ -106,20 +93,18 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: true,
HasInlineMergeConflicts: true,
DisplayString: "UU file5.txt",
Type: "file",
ShortStatus: "UU",
LinesAdded: 2,
LinesDeleted: 2,
},
},
},
{
testName: "File with new line char",
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
expectedFiles: []*models.File{
"File with new line char",
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "MM a\nb.txt", nil),
[]*models.File{
{
Path: "a\nb.txt",
Name: "a\nb.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: true,
@@ -128,22 +113,22 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "MM a\nb.txt",
Type: "file",
ShortStatus: "MM",
},
},
},
{
testName: "Renamed files",
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
"Renamed files",
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
nil,
),
expectedFiles: []*models.File{
[]*models.File{
{
Path: "after1.txt",
PreviousPath: "before1.txt",
Name: "after1.txt",
PreviousName: "before1.txt",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: true,
@@ -152,11 +137,12 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "R before1.txt -> after1.txt",
Type: "file",
ShortStatus: "R ",
},
{
Path: "after2.txt",
PreviousPath: "before2.txt",
Name: "after2.txt",
PreviousName: "before2.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: true,
@@ -165,21 +151,21 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "RM before2.txt -> after2.txt",
Type: "file",
ShortStatus: "RM",
},
},
},
{
testName: "File with arrow in name",
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
"File with arrow in name",
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
`?? a -> b.txt`,
nil,
),
expectedFiles: []*models.File{
[]*models.File{
{
Path: "a -> b.txt",
Name: "a -> b.txt",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: false,
@@ -188,59 +174,20 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "?? a -> b.txt",
Type: "file",
ShortStatus: "??",
},
},
},
{
testName: "Copied files",
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
"C copy1.txt\x00original.txt\x00CM copy2.txt\x00original.txt",
nil,
),
expectedFiles: []*models.File{
{
Path: "copy1.txt",
PreviousPath: "original.txt",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: true,
Added: false,
Deleted: false,
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "C original.txt -> copy1.txt",
ShortStatus: "C ",
},
{
Path: "copy2.txt",
PreviousPath: "original.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: true,
Added: false,
Deleted: false,
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "CM original.txt -> copy2.txt",
ShortStatus: "CM",
},
},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)
userConfig := &config.UserConfig{}
userConfig.Gui.ShowNumstatInFilesView = s.showNumstatInFilesView
userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold
loader := &FileLoader{
GitCommon: buildGitCommon(commonDeps{appState: &config.AppState{}, userConfig: userConfig}),
Common: utils.NewDummyCommon(),
cmd: cmd,
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
getFileType: func(string) string { return "file" },
+202 -41
View File
@@ -3,49 +3,210 @@ package git_commands
import (
"testing"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/stretchr/testify/assert"
)
func TestEditFilesCmd(t *testing.T) {
func TestEditFileCmdStrLegacy(t *testing.T) {
type scenario struct {
filenames []string
osConfig config.OSConfig
expectedCmdStr string
suspend bool
filename string
configEditCommand string
configEditCommandTemplate string
runner *oscommands.FakeCmdObjRunner
getenv func(string) string
gitConfigMockResponses map[string]string
test func(string, error)
}
scenarios := []scenario{
{
filenames: []string{"test"},
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
suspend: true,
filename: "test",
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
ExpectArgs([]string{"which", "vi"}, "", errors.New("error")),
getenv: func(env string) string {
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.EqualError(t, err, "No editor defined in config file, $GIT_EDITOR, $VISUAL, $EDITOR, or git config")
},
},
{
filenames: []string{"test"},
filename: "test",
configEditCommand: "nano",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t),
getenv: func(env string) string {
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `nano "test"`, cmdStr)
},
},
{
filename: "test",
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t),
getenv: func(env string) string {
return ""
},
gitConfigMockResponses: map[string]string{"core.editor": "nano"},
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `nano "test"`, cmdStr)
},
},
{
filename: "test",
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t),
getenv: func(env string) string {
if env == "VISUAL" {
return "nano"
}
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `nano "test"`, cmdStr)
},
},
{
filename: "test",
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t),
getenv: func(env string) string {
if env == "EDITOR" {
return "emacs"
}
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `emacs "test"`, cmdStr)
},
},
{
filename: "test",
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
getenv: func(env string) string {
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `vi "test"`, cmdStr)
},
},
{
filename: "file/with space",
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
getenv: func(env string) string {
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `vi "file/with space"`, cmdStr)
},
},
{
filename: "open file/at line",
configEditCommand: "vim",
configEditCommandTemplate: "{{editor}} +{{line}} {{filename}}",
runner: oscommands.NewFakeRunner(t),
getenv: func(env string) string {
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `vim +1 "open file/at line"`, cmdStr)
},
},
{
filename: "default edit command template",
configEditCommand: "vim",
configEditCommandTemplate: "",
runner: oscommands.NewFakeRunner(t),
getenv: func(env string) string {
return ""
},
gitConfigMockResponses: nil,
test: func(cmdStr string, err error) {
assert.NoError(t, err)
assert.Equal(t, `vim +1 -- "default edit command template"`, cmdStr)
},
},
}
for _, s := range scenarios {
userConfig := config.GetDefaultConfig()
userConfig.OS.EditCommand = s.configEditCommand
userConfig.OS.EditCommandTemplate = s.configEditCommandTemplate
instance := buildFileCommands(commonDeps{
runner: s.runner,
userConfig: userConfig,
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
getenv: s.getenv,
})
s.test(instance.GetEditCmdStrLegacy(s.filename, 1))
s.runner.CheckForMissingCalls()
}
}
func TestEditFileCmd(t *testing.T) {
type scenario struct {
filename string
osConfig config.OSConfig
expectedCmdStr string
expectedEditInTerminal bool
}
scenarios := []scenario{
{
filename: "test",
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
expectedEditInTerminal: true,
},
{
filename: "test",
osConfig: config.OSConfig{
Edit: "nano {{filename}}",
},
expectedCmdStr: `nano "test"`,
suspend: true,
expectedCmdStr: `nano "test"`,
expectedEditInTerminal: true,
},
{
filenames: []string{"file/with space"},
filename: "file/with space",
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "file/with space"`,
suspend: false,
},
{
filenames: []string{"multiple", "files"},
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "multiple" "files"`,
suspend: false,
expectedCmdStr: `subl -- "file/with space"`,
expectedEditInTerminal: false,
},
}
@@ -57,28 +218,28 @@ func TestEditFilesCmd(t *testing.T) {
userConfig: userConfig,
})
cmdStr, suspend := instance.GetEditCmdStr(s.filenames)
cmdStr, editInTerminal := instance.GetEditCmdStr(s.filename)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.suspend, suspend)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
}
}
func TestEditFileAtLineCmd(t *testing.T) {
type scenario struct {
filename string
lineNumber int
osConfig config.OSConfig
expectedCmdStr string
suspend bool
filename string
lineNumber int
osConfig config.OSConfig
expectedCmdStr string
expectedEditInTerminal bool
}
scenarios := []scenario{
{
filename: "test",
lineNumber: 42,
osConfig: config.OSConfig{},
expectedCmdStr: `vim +42 -- "test"`,
suspend: true,
filename: "test",
lineNumber: 42,
osConfig: config.OSConfig{},
expectedCmdStr: `vim +42 -- "test"`,
expectedEditInTerminal: true,
},
{
filename: "test",
@@ -86,8 +247,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
osConfig: config.OSConfig{
EditAtLine: "nano +{{line}} {{filename}}",
},
expectedCmdStr: `nano +35 "test"`,
suspend: true,
expectedCmdStr: `nano +35 "test"`,
expectedEditInTerminal: true,
},
{
filename: "file/with space",
@@ -95,8 +256,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "file/with space":12`,
suspend: false,
expectedCmdStr: `subl -- "file/with space":12`,
expectedEditInTerminal: false,
},
}
@@ -108,9 +269,9 @@ func TestEditFileAtLineCmd(t *testing.T) {
userConfig: userConfig,
})
cmdStr, suspend := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
cmdStr, editInTerminal := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.suspend, suspend)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
}
}
+2 -2
View File
@@ -24,7 +24,7 @@ func (self *FlowCommands) GitFlowEnabled() bool {
return self.config.GetGitFlowPrefixes() != ""
}
func (self *FlowCommands) FinishCmdObj(branchName string) (*oscommands.CmdObj, error) {
func (self *FlowCommands) FinishCmdObj(branchName string) (oscommands.ICmdObj, error) {
prefixes := self.config.GetGitFlowPrefixes()
// need to find out what kind of branch this is
@@ -54,7 +54,7 @@ func (self *FlowCommands) FinishCmdObj(branchName string) (*oscommands.CmdObj, e
return self.cmd.New(cmdArgs), nil
}
func (self *FlowCommands) StartCmdObj(branchType string, name string) *oscommands.CmdObj {
func (self *FlowCommands) StartCmdObj(branchType string, name string) oscommands.ICmdObj {
cmdArgs := NewGitCmd("flow").Arg(branchType, "start", name).ToArgv()
return self.cmd.New(cmdArgs)

Some files were not shown because too many files have changed in this diff Show More