Compare commits

..
Author SHA1 Message Date
Jesse Duffield 4a07e03701 Local-ise remote branches when checking out by name
The idea here is that in the git CLI you will often want to checkout a
remote branch like origin/blah by doing `git checkout blah`. That will
automatically create a local branch named blah that tracks origin/blah.

Previously in the suggestions view when checking out a branch by name,
we would only show local branches and remote branches like origin/blah
but wouldn't show blah as an option (assuming no local branch existed
with that name). This meant users would checkout origin/blah and git
would check out that branch as a detached head which is rarely what you
actually want.

Now we give them both options. The alternative approach we could have
taken is to still show the branch as origin/blah but then ask if the
user wants to check out the branch as detached or as a local branch
tracking the remote branch. That approach is certainly more versatile,
but it's also something you can do already by going to the remote branch
directly via the remotes view. Admittedly, my approach involves less
work.
2024-03-09 13:53:14 +11:00
516 changed files with 23122 additions and 28456 deletions
-3
View File
@@ -1,3 +0,0 @@
*.go text
*.md text eol=lf
*.json text eol=lf
+1 -1
View File
@@ -6,7 +6,7 @@
* [ ] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide)
* [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] 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
<!--
+4 -4
View File
@@ -3,20 +3,20 @@ name: Continuous Delivery
on:
push:
tags:
- "v*"
- 'v*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Unshallow repo
run: git fetch --prune --unshallow
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: 1.22.x
go-version: 1.21.x
- name: Run goreleaser
uses: goreleaser/goreleaser-action@v4
with:
+20 -26
View File
@@ -1,7 +1,7 @@
name: Continuous Integration
env:
GO_VERSION: 1.22
GO_VERSION: 1.21
on:
push:
@@ -28,11 +28,11 @@ 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@v4
with:
go-version: 1.22.x
go-version: 1.21.x
- name: Test code
# we're passing -short so that we skip the integration tests, which will be run in parallel below
run: |
@@ -61,11 +61,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}}
@@ -82,14 +82,14 @@ 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@v4
with:
go-version: 1.22.x
go-version: 1.21.x
- name: Print git version
run: git --version
- name: Test code
@@ -111,11 +111,11 @@ 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@v4
with:
go-version: 1.22.x
go-version: 1.21.x
- name: Build linux binary
run: |
GOOS=linux go build
@@ -138,11 +138,11 @@ 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@v4
with:
go-version: 1.22.x
go-version: 1.21.x
- name: Check Vendor Directory
# ensure our vendor directory matches up with our go modules
run: |
@@ -164,15 +164,15 @@ 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@v4
with:
go-version: 1.22.x
go-version: 1.21.x
- name: Lint
uses: golangci/golangci-lint-action@v3.7.0
with:
version: v1.58
version: latest
- name: errors
run: golangci-lint run
if: ${{ failure() }}
@@ -188,16 +188,10 @@ jobs:
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.22.x
uses: actions/checkout@v3
- name: Download all coverage artifacts
uses: actions/download-artifact@v3
+10 -10
View File
@@ -9,20 +9,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Generate Sponsors 💖
uses: JamesIves/github-sponsors-readme-action@v1.2.2
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@v6
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
- 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
+3 -1
View File
@@ -6,6 +6,9 @@
# Hidden
.*
# TODO
TODO.*
# Notes
*.notes
@@ -19,7 +22,6 @@ lazygit.exe
# Exceptions
!.gitignore
!.gitattributes
!.goreleaser.yml
!.golangci.yml
!.circleci/
+2 -7
View File
@@ -14,15 +14,10 @@ linters:
- exhaustive
- makezero
- nakedret
- copyloopvar
# - goconst # TODO: enable and fix issues
fast: false
linters-settings:
copyloopvar:
# Check all assigning the loop variable to another variable.
# Default: false
# If true, an assignment like `a := x` will be detected as an error.
check-alias: true
exhaustive:
default-signifies-exhaustive: true
staticcheck:
@@ -35,5 +30,5 @@ linters-settings:
max-func-lines: 0
run:
go: '1.22'
go: '1.21'
timeout: 10m
+25 -1
View File
@@ -154,7 +154,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.22 as build
FROM golang:1.21 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 ./
-4
View File
@@ -38,10 +38,6 @@ generate:
format:
gofumpt -l -w .
.PHONY: lint
lint:
golangci-lint run
# For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md.
.PHONY: integration-test-tui
integration-test-tui:
+2 -38
View File
File diff suppressed because one or more lines are too long
-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)
}
}
+188 -559
View File
@@ -19,7 +19,7 @@ If you want to change the config directory:
JSON schema is available for `config.yml` so that IntelliSense in Visual Studio Code (completion and error checking) is automatically enabled when the [YAML Red Hat][yaml] extension is installed. However, note that automatic schema detection only works if your config file is in one of the standard paths mentioned above. If you override the path to the file, you can still make IntelliSense work by adding
```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/jesseduffield/lazygit/master/schema/config.json
# yaml-language-server: $schema=https://json.schemastore.org/lazygit.json
```
to the top of your config file or via [Visual Studio Code settings.json config][settings].
@@ -29,587 +29,252 @@ to the top of your config file or via [Visual Studio Code settings.json config][
## Default
<!-- START CONFIG YAML: AUTOMATICALLY GENERATED with `go generate ./..., DO NOT UPDATE MANUALLY -->
```yaml
# Config relating to the Lazygit UI
gui:
# The number of lines you scroll by when scrolling the main window
scrollHeight: 2
# If true, allow scrolling past the bottom of the content in the main window
scrollPastBottom: true
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#scroll-off-margin
scrollOffMargin: 2
# One of: 'margin' (default) | 'jump'
scrollOffBehavior: margin
# If true, capture mouse events.
# When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.
mouseEvents: true
# If true, do not show a warning when discarding changes in the staging view.
skipDiscardChangeWarning: false
# If true, do not show warning when applying/popping the stash
skipStashWarning: false
# If true, do not show a warning when attempting to commit without any staged files; instead stage all unstaged files.
skipNoStagedFilesWarning: false
# If true, do not show a warning when rewording a commit via an external editor
skipRewordInEditorWarning: false
# Fraction of the total screen width to use for the left side section. You may want to pick a small number (e.g. 0.2) if you're using a narrow screen, so that you can see more of the main section.
# Number from 0 to 1.0.
sidePanelWidth: 0.3333
# If true, increase the height of the focused side window; creating an accordion effect.
# stuff relating to the UI
windowSize: 'normal' # one of 'normal' | 'half' | 'full' default is 'normal'
scrollHeight: 2 # how many lines you scroll by
scrollPastBottom: true # enable scrolling past the bottom
scrollOffMargin: 2 # how many lines to keep before/after the cursor when it reaches the top/bottom of the view; see 'Scroll-off Margin' section below
scrollOffBehavior: 'margin' # one of 'margin' | 'jump'; see 'Scroll-off Margin' section below
sidePanelWidth: 0.3333 # number from 0 to 1
expandFocusedSidePanel: false
# The weight of the expanded side panel, relative to the other panels. 2 means
# twice as tall as the other panels. Only relevant if `expandFocusedSidePanel` is true.
expandedSidePanelWeight: 2
# Sometimes the main window is split in two (e.g. when the selected file has both staged and unstaged changes). This setting controls how the two sections are split.
# Options are:
# - 'horizontal': split the window horizontally
# - 'vertical': split the window vertically
# - 'flexible': (default) split the window horizontally if the window is wide enough, otherwise split vertically
mainPanelSplitMode: flexible
# How the window is split when in half screen mode (i.e. after hitting '+' once).
# Possible values:
# - 'left': split the window horizontally (side panel on the left, main view on the right)
# - 'top': split the window vertically (side panel on top, main view below)
enlargedSideViewLocation: left
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
language: auto
# Format used when displaying time e.g. commit time.
# Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
timeFormat: 02 Jan 06
# Format used when displaying time if the time is less than 24 hours ago.
# Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
shortTimeFormat: 3:04PM
# Config relating to colors and styles.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
enlargedSideViewLocation: 'left' # one of 'left' | 'top'
language: 'auto' # one of 'auto' | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
timeFormat: '02 Jan 06' # https://pkg.go.dev/time#Time.Format
shortTimeFormat: '3:04PM'
theme:
# Border color of focused window
activeBorderColor:
- green
- bold
# Border color of non-focused windows
inactiveBorderColor:
- default
# Border color of focused window when searching in that window
- white
searchingActiveBorderColor:
- cyan
- bold
# Color of keybindings help text in the bottom line
optionsTextColor:
- blue
# Background color of selected line.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
selectedLineBgColor:
- blue
# Background color of selected line when view doesn't have focus.
inactiveViewSelectedLineBgColor:
- bold
# Foreground color of copied commit
cherryPickedCommitFgColor:
- blue
# Background color of copied commit
- blue # set to `default` to have no background colour
cherryPickedCommitBgColor:
- cyan
# Foreground color of marked base commit (for rebase)
markedBaseCommitFgColor:
cherryPickedCommitFgColor:
- blue
# Background color of marked base commit (for rebase)
markedBaseCommitBgColor:
- yellow
# Color for file with unstaged changes
unstagedChangesColor:
- red
# Default text color
defaultFgColor:
- default
# Config relating to the commit length indicator
commitLength:
# If true, show an indicator of commit message length
show: true
# If true, show the '5 of 20' footer at the bottom of list views
showListFooter: true
# If true, display the files in the file views as a tree. If false, display the files as a flat list.
# This can be toggled from within Lazygit with the '~' key, but that will not change the default.
showFileTree: true
# If true, show a random tip in the command log when Lazygit starts
mouseEvents: true
skipDiscardChangeWarning: false
skipStashWarning: false
showFileTree: true # for rendering changes files in a tree format
showListFooter: true # for seeing the '5 of 20' message in list panels
showRandomTip: true
# If true, show the command log
showBranchCommitHash: false # show commit hashes alongside branch names
showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
showPanelJumps: true # for showing the jump-to-panel keybindings as panel subtitles
showCommandLog: true
# If true, show the bottom line that contains keybinding info and useful buttons. If false, this line will be hidden except to display a loader for an in-progress action.
showBottomLine: true
# If true, show jump-to-window keybindings in window titles.
showPanelJumps: true
# Deprecated: use nerdFontsVersion instead
showIcons: false
# Nerd fonts version to use.
# One of: '2' | '3' | empty string (default)
# If empty, do not show icons.
nerdFontsVersion: ""
# If true (default), file icons are shown in the file views. Only relevant if NerdFontsVersion is not empty.
showFileIcons: true
# Length of author name in (non-expanded) commits view. 2 means show initials only.
commitAuthorShortLength: 2
# Length of author name in expanded commits view. 2 means show initials only.
commitAuthorLongLength: 17
# Length of commit hash in commits view. 0 shows '*' if NF icons aren't on.
commitHashLength: 8
# If true, show commit hashes alongside branch names in the branches view.
showBranchCommitHash: false
# Whether to show the divergence from the base branch in the branches view.
# One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
showDivergenceFromBaseBranch: none
# Height of the command log view
showIcons: false # deprecated: use nerdFontsVersion instead
nerdFontsVersion: "" # nerd fonts version to use ("2" or "3"); empty means don't show nerd font icons
showFileIcons: true # for hiding file icons in the file views
commandLogSize: 8
# Whether to split the main window when viewing file changes.
# One of: 'auto' | 'always'
# If 'auto', only split the main window when a file has both staged and unstaged changes
splitDiff: auto
# Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).
# One of: 'normal' (default) | 'half' | 'full'
windowSize: normal
# Window border style.
# One of 'rounded' (default) | 'single' | 'double' | 'hidden'
border: rounded
# If true, show a seriously epic explosion animation when nuking the working tree.
animateExplosion: true
# Whether to stack UI components on top of each other.
# One of 'auto' (default) | 'always' | 'never'
portraitMode: auto
# How things are filtered when typing '/'.
# One of 'substring' (default) | 'fuzzy'
filterMode: substring
# Config relating to the spinner.
spinner:
# The frames of the spinner animation.
frames:
- '|'
- /
- '-'
- \
# The "speed" of the spinner in milliseconds.
rate: 50
# Status panel view.
# One of 'dashboard' (default) | 'allBranchesLog'
statusPanelView: dashboard
# Config relating to git
splitDiff: 'auto' # one of 'auto' | 'always'
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
border: 'rounded' # one of 'single' | 'double' | 'rounded' | 'hidden'
animateExplosion: true # shows an explosion animation when nuking the working tree
portraitMode: 'auto' # one of 'auto' | 'never' | 'always'
git:
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
paging:
# Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never'
colorArg: always
# e.g.
# diff-so-fancy
# delta --dark --paging=never
# ydiff -p cat -s --wrap --width={{columnWidth}}
pager: ""
# If true, Lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
useConfig: false
# e.g. 'difft --color=always'
externalDiffCommand: ""
# Config relating to committing
commit:
# If true, pass '--signoff' flag when committing
signOff: false
# Automatic WYSIWYG wrapping of the commit message as you type
autoWrapCommitMessage: true
# If autoWrapCommitMessage is true, the width to wrap to
autoWrapWidth: 72
# Config relating to merging
merging:
# If true, run merges in a subprocess so that if a commit message is required, Lazygit will not hang
# Only applicable to unix users.
# only applicable to unix users
manualCommit: false
# Extra args passed to `git merge`, e.g. --no-ff
args: ""
# The commit message to use for a squash merge commit. Can contain "{{selectedRef}}" and "{{currentBranch}}" placeholders.
squashMergeMessage: Squash merge {{selectedRef}} into {{currentBranch}}
# list of branches that are considered 'main' branches, used when displaying commits
mainBranches:
- master
- main
# Prefix to use when skipping hooks. E.g. if set to 'WIP', then pre-commit hooks will be skipped when the commit message starts with 'WIP'
skipHookPrefix: WIP
# If true, periodically fetch from remote
autoFetch: true
# If true, periodically refresh files and submodules
autoRefresh: true
# If true, pass the --all arg to git fetch
fetchAll: true
# Command used when displaying the current branch git log in the main window
branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --
# Command used to display git log of all branches in the main window.
# Deprecated: User `allBranchesLogCmds` instead.
allBranchesLogCmd: git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium
# If true, do not spawn a separate process when using GPG
overrideGpg: false
# If true, do not allow force pushes
disableForcePushing: false
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix
commitPrefix:
# pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*"
pattern: ""
# Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] "
replace: ""
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-branch-name-prefix
branchPrefix: ""
# If true, parse emoji strings in commit messages e.g. render :rocket: as 🚀
# (This should really be under 'gui', not 'git')
parseEmoji: false
# Config for showing the log in the commits view
# extra args passed to `git merge`, e.g. --no-ff
args: ''
log:
# One of: 'date-order' | 'author-date-order' | 'topo-order' | 'default'
# 'topo-order' makes it easier to read the git log graph, but commits may not
# appear chronologically. See https://git-scm.com/docs/
# one of date-order, author-date-order, topo-order or default.
# topo-order makes it easier to read the git log graph, but commits may not
# appear chronologically. See https://git-scm.com/docs/git-log#_commit_ordering
#
# Deprecated: Configure this with `Log menu -> Commit sort order` (<c-l> in the commits window by default).
order: topo-order
# This determines whether the git graph is rendered in the commits panel
# One of 'always' | 'never' | 'when-maximised'
order: 'topo-order'
# one of always, never, when-maximised
# this determines whether the git graph is rendered in the commits panel
#
# Deprecated: Configure this with `Log menu -> Show git graph` (<c-l> in the commits window by default).
showGraph: always
# displays the whole git graph by default in the commits view (equivalent to passing the `--all` argument to `git log`)
showGraph: 'always'
# displays the whole git graph by default in the commits panel (equivalent to passing the `--all` argument to `git log`)
showWholeGraph: false
# When copying commit hashes to the clipboard, truncate them to this
# length. Set to 40 to disable truncation.
truncateCopiedCommitHashesTo: 12
# Periodic update checks
update:
# One of: 'prompt' (default) | 'background' | 'never'
method: prompt
# Period in days between update checks
days: 14
# Background refreshes
refresher:
# File/submodule refresh interval in seconds.
# Auto-refresh can be disabled via option 'git.autoRefresh'.
refreshInterval: 10
# Re-fetch interval in seconds.
# Auto-fetch can be disabled via option 'git.autoFetch'.
fetchInterval: 60
# If true, show a confirmation popup before quitting Lazygit
confirmOnQuit: false
# If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close
quitOnTopLevelReturn: false
# Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc
skipHookPrefix: WIP
# The main branches. We colour commits green if they belong to one of these branches,
# so that you can easily see which commits are unique to your branch (coloured in yellow)
mainBranches: [master, main]
autoFetch: true
autoRefresh: true
fetchAll: true # Pass --all flag when running git fetch. Set to false to fetch only origin (or the current branch's upstream remote if there is one)
branchLogCmd: 'git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --'
allBranchesLogCmd: 'git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium'
overrideGpg: false # prevents lazygit from spawning a separate process when using GPG
disableForcePushing: false
parseEmoji: false
os:
# Command for editing a file. Should contain "{{filename}}".
edit: ""
# Command for editing a file at a given line number. Should contain
# "{{filename}}", and may optionally contain "{{line}}".
editAtLine: ""
# Same as EditAtLine, except that the command needs to wait until the
# window is closed.
editAtLineAndWait: ""
# For opening a directory in an editor
openDirInEditor: ""
# A built-in preset that sets all of the above settings. Supported presets
# are defined in the getPreset function in editor_presets.go.
editPreset: ""
# Command for opening a file, as if the file is double-clicked. Should
# contain "{{filename}}", but doesn't support "{{line}}".
open: ""
# Command for opening a link. Should contain "{{link}}".
openLink: ""
# EditCommand is the command for editing a file.
# Deprecated: use Edit instead. Note that semantics are different:
# EditCommand is just the command itself, whereas Edit contains a
# "{{filename}}" variable.
editCommand: ""
# EditCommandTemplate is the command template for editing a file
# Deprecated: use EditAtLine instead.
editCommandTemplate: ""
# OpenCommand is the command for opening a file
# Deprecated: use Open instead.
openCommand: ""
# OpenLinkCommand is the command for opening a link
# Deprecated: use OpenLink instead.
openLinkCommand: ""
# CopyToClipboardCmd is the command for copying to clipboard.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
copyToClipboardCmd: ""
# ReadFromClipboardCmd is the command for reading the clipboard.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
readFromClipboardCmd: ""
# If true, don't display introductory popups upon opening Lazygit.
copyToClipboardCmd: '' # See 'Custom Command for Copying to Clipboard' section
editPreset: '' # see 'Configuring File Editing' section
edit: ''
editAtLine: ''
editAtLineAndWait: ''
open: ''
openLink: ''
refresher:
refreshInterval: 10 # File/submodule refresh interval in seconds. Auto-refresh can be disabled via option 'git.autoRefresh'.
fetchInterval: 60 # Re-fetch interval in seconds. Auto-fetch can be disabled via option 'git.autoFetch'.
update:
method: prompt # can be: prompt | background | never
days: 14 # how often an update is checked for
confirmOnQuit: false
# determines whether hitting 'esc' will quit the application when there is nothing to cancel/close
quitOnTopLevelReturn: false
disableStartupPopups: false
# What to do when opening Lazygit outside of a git repo.
# - 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo
# - 'create': initialize a new repo
# - 'skip': open most recent repo
# - 'quit': exit Lazygit
notARepository: prompt
# If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.
promptToReturnFromSubprocess: true
# Keybindings
notARepository: 'prompt' # one of: 'prompt' | 'create' | 'skip' | 'quit'
promptToReturnFromSubprocess: true # display confirmation when subprocess terminates
keybinding:
universal:
quit: q
quit-alt1: <c-c>
return: <esc>
quitWithoutChangingDirectory: Q
togglePanel: <tab>
prevItem: <up>
nextItem: <down>
prevItem-alt: k
nextItem-alt: j
prevPage: ','
nextPage: .
scrollLeft: H
scrollRight: L
gotoTop: <
gotoBottom: '>'
toggleRangeSelect: v
rangeSelectDown: <s-down>
rangeSelectUp: <s-up>
prevBlock: <left>
nextBlock: <right>
prevBlock-alt: h
nextBlock-alt: l
nextBlock-alt2: <tab>
prevBlock-alt2: <backtab>
jumpToBlock:
- "1"
- "2"
- "3"
- "4"
- "5"
nextMatch: "n"
prevMatch: "N"
startSearch: /
optionMenu: <disabled>
optionMenu-alt1: '?'
select: <space>
goInto: <enter>
confirm: <enter>
confirmInEditor: <a-enter>
remove: d
new: "n"
edit: e
openFile: o
scrollUpMain: <pgup>
scrollDownMain: <pgdown>
scrollUpMain-alt1: K
scrollDownMain-alt1: J
scrollUpMain-alt2: <c-u>
scrollDownMain-alt2: <c-d>
quit: 'q'
quit-alt1: '<c-c>' # alternative/alias of quit
return: '<esc>' # return to previous menu, will quit if there's nowhere to return
quitWithoutChangingDirectory: 'Q'
togglePanel: '<tab>' # goto the next panel
prevItem: '<up>' # go one line up
nextItem: '<down>' # go one line down
prevItem-alt: 'k' # go one line up
nextItem-alt: 'j' # go one line down
prevPage: ',' # go to next page in list
nextPage: '.' # go to previous page in list
gotoTop: '<' # go to top of list
gotoBottom: '>' # go to bottom of list
scrollLeft: 'H' # scroll left within list view
scrollRight: 'L' # scroll right within list view
prevBlock: '<left>' # goto the previous block / panel
nextBlock: '<right>' # goto the next block / panel
prevBlock-alt: 'h' # goto the previous block / panel
nextBlock-alt: 'l' # goto the next block / panel
jumpToBlock: ['1', '2', '3', '4', '5'] # goto the Nth block / panel
nextMatch: 'n'
prevMatch: 'N'
optionMenu: <disabled> # show help menu
optionMenu-alt1: '?' # show help menu
select: '<space>'
goInto: '<enter>'
openRecentRepos: '<c-r>'
confirm: '<enter>'
remove: 'd'
new: 'n'
edit: 'e'
openFile: 'o'
scrollUpMain: '<pgup>' # main panel scroll up
scrollDownMain: '<pgdown>' # main panel scroll down
scrollUpMain-alt1: 'K' # main panel scroll up
scrollDownMain-alt1: 'J' # main panel scroll down
scrollUpMain-alt2: '<c-u>' # main panel scroll up
scrollDownMain-alt2: '<c-d>' # main panel scroll down
executeCustomCommand: ':'
createRebaseOptionsMenu: m
# 'Files' appended for legacy reasons
pushFiles: P
# 'Files' appended for legacy reasons
pullFiles: p
refresh: R
createPatchOptionsMenu: <c-p>
createRebaseOptionsMenu: 'm'
pushFiles: 'P'
pullFiles: 'p'
refresh: 'R'
createPatchOptionsMenu: '<c-p>'
nextTab: ']'
prevTab: '['
nextScreenMode: +
prevScreenMode: _
undo: z
redo: <c-z>
filteringMenu: <c-s>
diffingMenu: W
diffingMenu-alt: <c-e>
copyToClipboard: <c-o>
openRecentRepos: <c-r>
submitEditorText: <enter>
nextScreenMode: '+'
prevScreenMode: '_'
undo: 'z'
redo: '<c-z>'
filteringMenu: '<c-s>'
diffingMenu: 'W'
diffingMenu-alt: '<c-e>' # deprecated
copyToClipboard: '<c-o>'
submitEditorText: '<enter>'
extrasMenu: '@'
toggleWhitespaceInDiffView: <c-w>
toggleWhitespaceInDiffView: '<c-w>'
increaseContextInDiffView: '}'
decreaseContextInDiffView: '{'
increaseRenameSimilarityThreshold: )
decreaseRenameSimilarityThreshold: (
openDiffTool: <c-t>
toggleRangeSelect: 'v'
rangeSelectUp: '<s-up>'
rangeSelectDown: '<s-down>'
status:
checkForUpdate: u
recentRepos: <enter>
allBranchesLogGraph: a
checkForUpdate: 'u'
recentRepos: '<enter>'
files:
commitChanges: c
commitChangesWithoutHook: w
amendLastCommit: A
commitChangesWithEditor: C
findBaseCommitForFixup: <c-f>
confirmDiscard: x
ignoreFile: i
refreshFiles: r
stashAllChanges: s
viewStashOptions: S
toggleStagedAll: a
viewResetOptions: D
fetch: f
commitChanges: 'c'
commitChangesWithoutHook: 'w' # commit changes without pre-commit hook
amendLastCommit: 'A'
commitChangesWithEditor: 'C'
findBaseCommitForFixup: '<c-f>'
confirmDiscard: 'x'
ignoreFile: 'i'
refreshFiles: 'r'
stashAllChanges: 's'
viewStashOptions: 'S'
toggleStagedAll: 'a' # stage/unstage all
viewResetOptions: 'D'
fetch: 'f'
toggleTreeView: '`'
openMergeTool: M
openStatusFilter: <c-b>
copyFileInfoToClipboard: "y"
openMergeTool: 'M'
openStatusFilter: '<c-b>'
branches:
createPullRequest: o
viewPullRequestOptions: O
copyPullRequestURL: <c-y>
checkoutBranchByName: c
forceCheckoutBranch: F
rebaseBranch: r
renameBranch: R
mergeIntoCurrentBranch: M
viewGitFlowOptions: i
fastForward: f
createTag: T
pushTag: P
setUpstream: u
fetchRemote: f
sortOrder: s
worktrees:
viewWorktreeOptions: w
createPullRequest: 'o'
viewPullRequestOptions: 'O'
checkoutBranchByName: 'c'
forceCheckoutBranch: 'F'
rebaseBranch: 'r'
renameBranch: 'R'
mergeIntoCurrentBranch: 'M'
viewGitFlowOptions: 'i'
fastForward: 'f' # fast-forward this branch from its upstream
createTag: 'T'
pushTag: 'P'
setUpstream: 'u' # set as upstream of checked-out branch
fetchRemote: 'f'
commits:
squashDown: s
renameCommit: r
renameCommitWithEditor: R
viewResetOptions: g
markCommitAsFixup: f
createFixupCommit: F
squashAboveCommits: S
moveDownCommit: <c-j>
moveUpCommit: <c-k>
amendToCommit: A
resetCommitAuthor: a
pickCommit: p
revertCommit: t
cherryPickCopy: C
pasteCommits: V
markCommitAsBaseForRebase: B
tagCommit: T
checkoutCommit: <space>
resetCherryPick: <c-R>
copyCommitAttributeToClipboard: "y"
openLogMenu: <c-l>
openInBrowser: o
viewBisectOptions: b
startInteractiveRebase: i
amendAttribute:
resetAuthor: a
setAuthor: A
addCoAuthor: c
squashDown: 's'
renameCommit: 'r'
renameCommitWithEditor: 'R'
viewResetOptions: 'g'
markCommitAsFixup: 'f'
createFixupCommit: 'F' # create fixup commit for this commit
squashAboveCommits: 'S'
moveDownCommit: '<c-j>' # move commit down one
moveUpCommit: '<c-k>' # move commit up one
amendToCommit: 'A'
pickCommit: 'p' # pick commit (when mid-rebase)
revertCommit: 't'
cherryPickCopy: 'C'
pasteCommits: 'V'
tagCommit: 'T'
checkoutCommit: '<space>'
resetCherryPick: '<c-R>'
copyCommitMessageToClipboard: '<c-y>'
openLogMenu: '<c-l>'
viewBisectOptions: 'b'
stash:
popStash: g
renameStash: r
popStash: 'g'
renameStash: 'r'
commitFiles:
checkoutCommitFile: c
checkoutCommitFile: 'c'
main:
toggleSelectHunk: a
pickBothHunks: b
editSelectHunk: E
toggleSelectHunk: 'a'
pickBothHunks: 'b'
submodules:
init: i
update: u
bulkMenu: b
commitMessage:
commitMenu: <c-o>
init: 'i'
update: 'u'
bulkMenu: 'b'
```
<!-- END CONFIG YAML -->
## Platform Defaults
@@ -634,7 +299,7 @@ os:
open: 'open {{filename}}'
```
## Custom Command for Copying to and Pasting from Clipboard
## Custom Command for Copying to Clipboard
```yaml
os:
copyToClipboardCmd: ''
@@ -647,12 +312,6 @@ os:
copyToClipboardCmd: printf "\033]52;c;$(printf {{text}} | base64)\a" > /dev/tty
```
A custom command for reading from the clipboard can be set using
```yaml
os:
readFromClipboardCmd: ''
```
It is used, for example, when pasting a commit message into the commit message panel. The command is supposed to output the clipboard content to stdout.
## Configuring File Editing
@@ -706,12 +365,6 @@ That's the behavior when `gui.scrollOffBehavior` is set to "margin" (the default
This setting applies both to all list views (e.g. commits and branches etc), and to the staging view.
## Filtering
We have two ways to filter things, substring matching (the default) and fuzzy searching. With substring matching, the text you enter gets searched for verbatim (usually case-insensitive, except when your filter string contains uppercase letters, in which case we search case-sensitively). You can search for multiple non-contiguous substrings by separating them with spaces; for example, "int test" will match "integration-testing". All substrings have to match, but not necessarily in the given order.
Fuzzy searching is smarter in that it allows every letter of the filter string to match anywhere in the text (only in order though), assigning a weight to the quality of the match and sorting by that order. This has the advantage that it allows typing "clt" to match "commit_loader_test" (letters at the beginning of subwords get more weight); but it has the disadvantage that it tends to return lots of irrelevant results, especially with short filter strings.
## Color Attributes
For color attributes you can choose an array of attributes (with max one color attribute)
@@ -874,15 +527,6 @@ Example:
- Branch name: feature/AB-123
- Commit message: [AB-123] Adding feature
```yaml
git:
commitPrefix:
pattern: "^\\w+\\/(\\w+-\\w+).*"
replace: '[$1] '
```
If you want repository-specific prefixes, you can map them with `commitPrefixes`. If you have both `commitPrefixes` defined and an entry in `commitPrefixes` for the current repo, the `commitPrefixes` entry is given higher precedence. Repository folder names must be an exact match.
```yaml
git:
commitPrefixes:
@@ -891,21 +535,6 @@ git:
replace: '[$1] '
```
## Predefined branch name prefix
In situations where certain naming pattern is used for branches, this can be used to populate new branch creation with a static prefix.
Example:
Some branches:
- jsmith/AB-123
- cwilson/AB-125
```yaml
git:
branchPrefix: "firstlast/"
```
## Custom git log command
You can override the `git log` command that's used to render the log of the selected branch like so:
+2 -3
View File
@@ -6,7 +6,7 @@ You can add custom command keybindings in your config.yml (accessible by pressin
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}}"
@@ -59,7 +59,6 @@ For a given custom command, here are the allowed fields:
| description | Label for the custom command when displayed in the keybindings menu | no |
| stream | Whether you want to stream the command's output to the Command Log panel | no |
| showOutput | Whether you want to show the command's output in a popup within Lazygit | no |
| outputTitle | The title to display in the popup panel if showOutput is true. If left unset, the command will be used as the title. | no |
| after | Actions to take after the command has completed | no |
Here are the options for the `after` key:
@@ -306,7 +305,7 @@ SelectedWorktree
CheckedOutBranch
```
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/services/custom_commands/models.go) (all the modelling lives in the same file).
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/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
+19 -20
View File
@@ -27,19 +27,6 @@ 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
@@ -56,10 +43,22 @@ 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.
This command works in many cases, and when it does it almost feels like magic,
but it's important to understand its limitations because it doesn't always work.
The way it works is that it looks at the deleted lines of your current
modifications, blames them to find out which commit those lines come from, and
if they all come from the same commit, it selects it. So here are cases where it
doesn't work:
- Your current diff has only added lines, but no deleted lines. In this case
there's no way for lazygit to know which commit you want to add them to.
- The deleted lines belong to multiple different commits. In this case you can
help lazygit by staging a set of files or hunks that all belong to the same
commit; if some changes are staged, the ctrl-f command works only on those.
- The found commit is already on master; in this case, lazygit refuses to select
it, because it doesn't make sense to create fixups for it, let alone amend to
it.
To sum it up: the command works great if you are changing code again that you
changed or added earlier in the same branch. This is a common enough case to
make the command useful.
+11 -11
View File
@@ -1,11 +1,11 @@
# Documentation Overview
* [Configuration](./Config.md).
* [Custom Commands](./Custom_Command_Keybindings.md)
* [Custom Pagers](./Custom_Pagers.md)
* [Dev docs](./dev)
* [Keybindings](./keybindings)
* [Undo/Redo](./Undoing.md)
* [Range Select](./Range_Select.md)
* [Searching/Filtering](./Searching.md)
* [Stacked Branches](./Stacked_Branches.md)
# Documentation Overview
* [Configuration](./Config.md).
* [Custom Commands](./Custom_Command_Keybindings.md)
* [Custom Pagers](./Custom_Pagers.md)
* [Dev docs](./dev)
* [Keybindings](./keybindings)
* [Undo/Redo](./Undoing.md)
* [Range Select](./Range_Select.md)
* [Searching/Filtering](./Searching.md)
* [Stacked Branches](./Stacked_Branches.md)
+1 -1
View File
@@ -13,6 +13,6 @@ 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
Lazygit visualizes the invidual 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)).
+1 -1
View File
@@ -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()`.
@@ -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, erro
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.
-2
View File
@@ -4,5 +4,3 @@
* [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)
+8 -13
View File
@@ -14,8 +14,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute custom command | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
@@ -78,11 +76,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <c-o> `` | Copy commit SHA 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. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded. |
| `` r `` | Reword | 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. |
@@ -164,14 +162,13 @@ If you would instead like to start an interactive rebase from the selected commi
| `` F `` | Force checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | Merge | Merge selected branch into currently checked out branch. |
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
| `` T `` | New tag | |
| `` s `` | Sort order | |
| `` g `` | Reset | |
| `` R `` | Rename branch | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -248,7 +245,7 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <c-o> `` | Copy commit SHA 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 | |
@@ -266,15 +263,14 @@ If you would instead like to start an interactive rebase from the selected commi
| 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. |
| `` <space> `` | Checkout | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` n `` | New branch | |
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | Merge | Merge selected branch into currently checked out branch. |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | Sort order | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -311,13 +307,13 @@ If you would instead like to start an interactive rebase from the selected commi
| `` e `` | Edit config file | Open file in external editor. |
| `` u `` | Check for update | |
| `` <enter> `` | Switch to a recent repo | |
| `` a `` | Show/cycle all branch logs | |
| `` a `` | Show all branch logs | |
## Sub-commits
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <c-o> `` | Copy commit SHA 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 | |
@@ -353,7 +349,6 @@ If you would instead like to start an interactive rebase from the selected commi
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
+8 -13
View File
@@ -14,8 +14,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` @ `` | コマンドログメニューを開く | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | カスタムコマンドを実行 | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
@@ -68,7 +66,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットのhashをクリップボードにコピー | |
| `` <c-o> `` | コミットのSHAをクリップボードにコピー | |
| `` <space> `` | チェックアウト | Checkout the selected commit as a detached HEAD. |
| `` y `` | コミットの情報をコピー | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | ブラウザでコミットを開く | |
@@ -95,11 +93,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットのhashをクリップボードにコピー | |
| `` <c-o> `` | コミットのSHAをクリップボードにコピー | |
| `` <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. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded. |
| `` r `` | コミットメッセージを変更 | Reword the selected commit's message. |
| `` 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. |
@@ -107,7 +105,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Mark the selected commit to be picked (when mid-rebase). This means that the commit will be retained upon continuing the rebase. |
| `` F `` | Fixupコミットを作成 | このコミットに対するfixupコミットを作成 |
| `` F `` | Create fixup commit | このコミットに対するfixupコミットを作成 |
| `` S `` | Apply fixup commits | Squash all 'fixup!' commits, either above the selected commit, or all in current branch (autosquash). |
| `` <c-j> `` | コミットを1つ下に移動 | |
| `` <c-k> `` | コミットを1つ上に移動 | |
@@ -185,7 +183,6 @@ If you would instead like to start an interactive rebase from the selected commi
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | タグをpush | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | コミットを閲覧 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -235,14 +232,13 @@ If you would instead like to start an interactive rebase from the selected commi
| `` F `` | Force checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 現在のブランチにマージ | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 現在のブランチにマージ | Merge selected branch into currently checked out branch. |
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
| `` T `` | タグを作成 | |
| `` s `` | 並び替え | |
| `` g `` | Reset | |
| `` R `` | ブランチ名を変更 | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | コミットを閲覧 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -331,15 +327,14 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | ブランチ名をクリップボードにコピー | |
| `` <space> `` | チェックアウト | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` <space> `` | チェックアウト | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` n `` | 新しいブランチを作成 | |
| `` M `` | 現在のブランチにマージ | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 現在のブランチにマージ | Merge selected branch into currently checked out branch. |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | 並び替え | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | コミットを閲覧 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -348,7 +343,7 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットのhashをクリップボードにコピー | |
| `` <c-o> `` | コミットのSHAをクリップボードにコピー | |
| `` <space> `` | チェックアウト | Checkout the selected commit as a detached HEAD. |
| `` y `` | コミットの情報をコピー | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | ブラウザでコミットを開く | |
+7 -12
View File
@@ -14,8 +14,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` @ `` | 명령어 로그 메뉴 열기 | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | 푸시 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | 업데이트 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기 | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기 | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute custom command | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
@@ -55,7 +53,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <c-o> `` | 커밋 SHA를 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
@@ -85,7 +83,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <c-o> `` | 커밋 SHA를 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
@@ -191,14 +189,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` F `` | 강제 체크아웃 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 현재 브랜치에 병합 | Merge selected branch into currently checked out branch. |
| `` f `` | Fast-forward this branch from its upstream | Fast-forward selected branch from its upstream. |
| `` T `` | 태그를 생성 | |
| `` s `` | Sort order | |
| `` g `` | View reset options | |
| `` R `` | 브랜치 이름 변경 | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -243,15 +240,14 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 브랜치명을 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` <space> `` | 체크아웃 | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` n `` | 새 브랜치 생성 | |
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 현재 브랜치에 병합 | Merge selected branch into currently checked out branch. |
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | Sort order | |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -260,11 +256,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <c-o> `` | 커밋 SHA를 클립보드에 복사 | |
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
| `` b `` | Bisect 옵션 보기 | |
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded. |
| `` r `` | 커밋메시지 변경 | Reword the selected commit's message. |
| `` 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. |
@@ -326,7 +322,6 @@ If you would instead like to start an interactive rebase from the selected commi
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | 태그를 push | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
+8 -13
View File
@@ -14,8 +14,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Voer aangepaste commando uit | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
@@ -103,14 +101,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` F `` | Forceer checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Merge in met huidige checked out branch | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | Merge in met huidige checked out branch | Merge selected branch into currently checked out branch. |
| `` f `` | Fast-forward deze branch vanaf zijn upstream | Fast-forward selected branch from its upstream. |
| `` T `` | Creëer tag | |
| `` s `` | Sort order | |
| `` g `` | Bekijk reset opties | |
| `` R `` | Hernoem branch | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -142,11 +139,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <c-o> `` | Kopieer commit SHA 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. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to fixup, 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. |
@@ -154,7 +151,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Kies commit (wanneer midden in rebase) |
| `` F `` | Creëer fixup commit | Creëer fixup commit |
| `` F `` | Create 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 | |
@@ -226,7 +223,7 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <c-o> `` | Kopieer commit SHA 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 | |
@@ -244,15 +241,14 @@ If you would instead like to start an interactive rebase from the selected commi
| 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. |
| `` <space> `` | Uitchecken | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` 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) |
| `` M `` | Merge in met huidige checked out branch | Merge selected branch into currently checked out branch. |
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Stel in als upstream van uitgecheckte branch |
| `` s `` | Sort order | |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -317,7 +313,7 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <c-o> `` | Kopieer commit SHA 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 | |
@@ -353,7 +349,6 @@ If you would instead like to start an interactive rebase from the selected commi
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
+280 -285
View File
@@ -1,145 +1,146 @@
_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 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. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Zwiększ rozmiar kontekstu w widoku różnic | Zwiększ ilość kontekstu pokazywanego wokół zmian w widoku różnic. |
| `` { `` | Zmniejsz rozmiar kontekstu w widoku różnic | Zmniejsz ilość kontekstu pokazywanego wokół zmian w widoku różnic. |
| `` : `` | Wykonaj polecenie niestandardowe | Wyświetl monit, w którym możesz wprowadzić polecenie powłoki do wykonania. Nie należy mylić z wcześniej skonfigurowanymi poleceniami niestandardowymi. |
| `` <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 | |
| `` ? `` | 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-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 diff context size | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Wykonaj własną komendę | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
| `` <c-p> `` | View custom patch options | |
| `` m `` | Widok scalenia/opcje zmiany bazy | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | Odśwież | 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 | |
| `` ? `` | 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 | |
| `` <esc> `` | Anuluj | |
| `` <c-w> `` | Przełącz białe znaki | Przełącz czy zmiany białych znaków są pokazywane w widoku różnic. |
| `` 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. |
| `` <c-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. |
| `` <c-w> `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view. |
| `` 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. |
| `` <c-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. |
## Nawigacja panelu listy
## List panel navigation
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Poprzednia strona | |
| `` . `` | Następna strona | |
| `` < `` | Przewiń do góry | |
| `` > `` | 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 | |
| `` , `` | Previous page | |
| `` . `` | Next page | |
| `` < `` | Scroll to top | |
| `` > `` | 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 | |
## Commit summary
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij | |
## 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.
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 | |
| `` 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) | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
| `` <c-o> `` | Copy commit SHA to clipboard | |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` b `` | View bisect options | |
| `` s `` | Spłaszcz | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Napraw | Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded. |
| `` r `` | Zmień nazwę commita | Reword the selected commit's message. |
| `` R `` | Zmień nazwę commita w edytorze | |
| `` d `` | Usuń 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) | Edytuj 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.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Wybierz commit (podczas zmiany bazy) |
| `` F `` | Create fixup commit | Utwórz commit naprawczy dla tego commita |
| `` S `` | Apply fixup commits | Spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash) |
| `` <c-j> `` | Przenieś commit 1 w dół | |
| `` <c-k> `` | Przenieś commit 1 w górę | |
| `` V `` | Wklej commity (przebieranie) | |
| `` 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 | Popraw commit zmianami z poczekalni |
| `` 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> `` | Przełącz | 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 | |
| `` g `` | Wyświetl opcje resetu | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopiuj commit (przebieranie) | 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) | |
| `` <enter> `` | Przeglądaj pliki commita | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
## Drzewa pracy
## Confirmation panel
| 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 | |
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij | |
## Główny panel (budowanie łatki)
## Local branches
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Idź do poprzedniego fragmentu | |
| `` <right> `` | Idź do następnego fragmentu | |
| `` v `` | Przełącz zaznaczenie zakresu | |
| `` a `` | Zaznacz fragment | Przełącz tryb zaznaczania fragmentu. |
| `` <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 | |
## 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. |
| `` <c-o> `` | Copy branch name to clipboard | |
| `` i `` | Show git-flow options | |
| `` <space> `` | Przełącz | Checkout selected item. |
| `` n `` | Nowa gałąź | |
| `` 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łąź. |
| `` 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) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
| `` o `` | Utwórz żądanie pobrania | |
| `` O `` | Utwórz opcje żądania ściągnięcia | |
| `` <c-y> `` | Skopiuj adres URL żądania pobrania do schowka | |
| `` c `` | Przełącz używając nazwy | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
| `` F `` | Wymuś przełączenie | 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 `` | Zmiana bazy gałęzi | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Scal do obecnej gałęzi | Merge selected branch into currently checked out branch. |
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
| `` T `` | New tag | |
| `` s `` | Sort order | |
| `` g `` | Wyświetl opcje resetu | |
| `` 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. |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Main panel (patch building)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Poprzedni kawałek | |
| `` <right> `` | Następny kawałek | |
| `` v `` | Toggle range select | |
| `` a `` | Select hunk | Toggle hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` o `` | Otwórz plik | Open file in default application. |
| `` e `` | Edytuj plik | Open file in external editor. |
| `` <space> `` | Toggle lines in patch | |
| `` <esc> `` | Wyście z trybu "linia po linii" | |
| `` / `` | Search the current view by text | |
## Menu
@@ -147,223 +148,217 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
|-----|--------|-------------|
| `` <enter> `` | Wykonaj | |
| `` <esc> `` | Zamknij | |
| `` / `` | 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ę | |
## 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 `` | Otwórz zewnętrzne narzędzie scalania | Uruchom `git mergetool`. |
| `` <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 `` | Zaznacz fragment | Przełącz tryb zaznaczania fragmentu. |
| `` <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 | |
| `` / `` | Filter the current view by text | |
## 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 | |
| `` <c-o> `` | Copy path to clipboard | |
| `` <space> `` | Przełącz stan poczekalni | Toggle staged for selected file. |
| `` <c-b> `` | Filter files by status | |
| `` y `` | Copy to clipboard | |
| `` c `` | Zatwierdź zmiany | Commit staged changes. |
| `` w `` | Zatwierdź zmiany bez skryptu pre-commit | |
| `` A `` | Zmień ostatni commit | |
| `` C `` | Zatwierdź zmiany używając edytora | |
| `` <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 `` | Otwórz plik | Open file in default application. |
| `` i `` | Ignore or exclude file | |
| `` 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 | Przełącz widok plików między płaskim a drzewem. Płaski układ pokazuje wszystkie ścieżki plików na jednej liście, układ drzewa grupuje pliki według katalogów. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` M `` | Otwórz zewnętrzne narzędzie scalania | Uruchom `git mergetool`. |
| `` f `` | Pobierz | Pobierz zmiany ze zdalnego serwera. |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | Wyświetl opcje schowka | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | Przełącz stan poczekalni wszystkich | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Zatwierdź pojedyncze linie | 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 `` | Pokaż opcje porzucania zmian | 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. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | Open external merge tool | Run `git mergetool`. |
| `` f `` | Pobierz | Fetch changes from remote. |
| `` / `` | Search the current view by text | |
## Pliki commita
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj ścieżkę 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 | Przełącz widok plików między płaskim a drzewem. Płaski układ pokazuje wszystkie ścieżki plików na jednej liście, układ drzewa grupuje pliki według katalogów. |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
| `` <c-o> `` | Copy path to clipboard | |
| `` c `` | Przełącz | Plik wybierania |
| `` d `` | Remove | Porzuć zmiany commita dla tego pliku |
| `` o `` | Otwórz plik | 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. |
| `` / `` | Search the current view by text | |
## Podsumowanie commita
## Poczekalnia
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij | |
| `` <left> `` | Poprzedni kawałek | |
| `` <right> `` | Następny kawałek | |
| `` v `` | Toggle range select | |
| `` a `` | Select hunk | Toggle hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` <space> `` | Przełącz stan poczekalni | 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 `` | Otwórz plik | Open file in default application. |
| `` e `` | Edytuj plik | Open file in external editor. |
| `` <esc> `` | Wróć do panelu plików | |
| `` <tab> `` | Switch view | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | Zatwierdź zmiany | Commit staged changes. |
| `` w `` | Zatwierdź zmiany bez skryptu pre-commit | |
| `` C `` | Zatwierdź zmiany używając edytora | |
| `` <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 | |
## 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 | |
| `` 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) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
| `` <c-o> `` | Copy commit SHA to clipboard | |
| `` <space> `` | Przełącz | 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 | |
| `` g `` | Wyświetl opcje resetu | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopiuj commit (przebieranie) | 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) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Remote branches
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy branch name to clipboard | |
| `` <space> `` | Przełącz | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` n `` | Nowa gałąź | |
| `` M `` | Scal do obecnej gałęzi | Merge selected branch into currently checked out branch. |
| `` r `` | Zmiana bazy gałęzi | 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 `` | Wyświetl opcje resetu | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 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 `` | Pobierz | 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 | |
## Scalanie
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Wybierz kawałek | |
| `` b `` | Wybierz oba kawałki | |
| `` <up> `` | Wybierz poprzedni kawałek | |
| `` <down> `` | Wybierz następny kawałek | |
| `` <left> `` | Poprzedni konflikt | |
| `` <right> `` | Następny konflikt | |
| `` z `` | Cofnij | Undo last merge conflict resolution. |
| `` e `` | Edytuj plik | Open file in external editor. |
| `` o `` | Otwórz plik | Open file in default application. |
| `` M `` | Open external merge tool | Run `git mergetool`. |
| `` <esc> `` | Wróć do panelu plików | |
## 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 | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
| `` <space> `` | Zastosuj | Apply the stash entry to your working directory. |
| `` g `` | Wyciągnij | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Porzuć | Remove the stash entry from the stash list. |
| `` n `` | Nowa gałąź | 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 | |
| `` <enter> `` | Przeglądaj pliki commita | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 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. |
| `` o `` | Otwórz konfigurację | Open file in default application. |
| `` e `` | Edytuj konfigurację | Open file in external editor. |
| `` u `` | Sprawdź aktualizacje | |
| `` <enter> `` | Przełącz na ostatnie repozytorium | |
| `` a `` | Pokaż wszystkie gałęzie w logach | |
| `` <enter> `` | Switch to a recent repo | |
| `` a `` | Pokaż wszystkie logi gałęzi | |
## 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 | |
| `` 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) | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
| `` <c-o> `` | Copy commit SHA to clipboard | |
| `` <space> `` | Przełącz | 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 | |
| `` g `` | Wyświetl opcje resetu | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopiuj commit (przebieranie) | 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) | |
| `` <enter> `` | Przeglądaj pliki commita | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
## 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 | |
| `` <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 | |
## Tagi
## Tags
| Key | Action | Info |
|-----|--------|-------------|
| `` <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) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
| `` <space> `` | Przełącz | Checkout the selected tag 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. |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Zdalne
## Worktrees
| 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 | |
| `` 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 | |
## Zdalne gałęzie
## Zwykłe
| 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) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
| `` mouse wheel down (fn+up) `` | Przewiń w dół | |
| `` mouse wheel up (fn+down) `` | Przewiń w górę | |
+8 -13
View File
@@ -14,8 +14,6 @@ _Связки клавиш_
| `` @ `` | Открыть меню журнала команд | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Отправить изменения | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Получить и слить изменения | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Выполнить пользовательскую команду | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
@@ -125,7 +123,7 @@ _Связки клавиш_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <c-o> `` | Скопировать SHA коммита в буфер обмена | |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
@@ -142,11 +140,11 @@ _Связки клавиш_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <c-o> `` | Скопировать SHA коммита в буфер обмена | |
| `` <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. |
| `` f `` | Объединить несколько коммитов в один отбросив сообщение коммита (Fixup) | Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded. |
| `` r `` | Перефразировать коммит | Reword the selected commit's message. |
| `` 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. |
@@ -154,7 +152,7 @@ _Связки клавиш_
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Выбрать коммит (в середине перебазирования) |
| `` F `` | Создать fixup коммит | Создать fixup коммит для этого коммита |
| `` F `` | Create fixup commit | Создать fixup коммит для этого коммита |
| `` S `` | Apply fixup commits | Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение) |
| `` <c-j> `` | Переместить коммит вниз на один | |
| `` <c-k> `` | Переместить коммит вверх на один | |
@@ -191,14 +189,13 @@ If you would instead like to start an interactive rebase from the selected commi
| `` F `` | Принудительное переключение | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | Слияние с текущей переключённой веткой | Merge selected branch into currently checked out branch. |
| `` f `` | Перемотать эту ветку вперёд из её upstream-ветки | Fast-forward selected branch from its upstream. |
| `` T `` | Создать тег | |
| `` s `` | Порядок сортировки | |
| `` g `` | Просмотреть параметры сброса | |
| `` R `` | Переименовать ветку | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -222,7 +219,7 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <c-o> `` | Скопировать SHA коммита в буфер обмена | |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
@@ -291,7 +288,6 @@ If you would instead like to start an interactive rebase from the selected commi
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Отправить тег | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -301,15 +297,14 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название ветки в буфер обмена | |
| `` <space> `` | Переключить | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` <space> `` | Переключить | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` n `` | Новая ветка | |
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | Слияние с текущей переключённой веткой | Merge selected branch into currently checked out branch. |
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Установить как upstream-ветку переключённую ветку |
| `` s `` | Порядок сортировки | |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
+8 -13
View File
@@ -14,8 +14,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` @ `` | 打开命令日志菜单 | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | 推送 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | 拉取 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | 扩大差异视图中显示的上下文范围 | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | 缩小差异视图中显示的上下文范围 | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | 执行自定义命令 | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
@@ -55,7 +53,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
| `` <c-o> `` | 将提交的 SHA 复制到剪贴板 | |
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在浏览器中打开提交 | |
@@ -93,14 +91,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` F `` | 强制检出 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | 将已检出的分支变基到该分支 | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 合并到当前检出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
| `` f `` | 从上游快进此分支 | Fast-forward selected branch from its upstream. |
| `` T `` | 创建标签 | |
| `` s `` | Sort order | |
| `` g `` | 查看重置选项 | |
| `` R `` | 重命名分支 | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 查看提交 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -109,7 +106,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
| `` <c-o> `` | 将提交的 SHA 复制到剪贴板 | |
| `` <space> `` | 检出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在浏览器中打开提交 | |
@@ -140,11 +137,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
| `` <c-o> `` | 将提交的 SHA 复制到剪贴板 | |
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
| `` b `` | 查看二分查找选项 | |
| `` s `` | 压缩 | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | 修正(fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` f `` | 修正(fixup) | Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded. |
| `` r `` | 改写提交 | Reword the selected commit's message. |
| `` 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. |
@@ -152,7 +149,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | 选择提交(变基过程中) |
| `` F `` | 为此提交创建修正 | 创建修正提交 |
| `` F `` | Create fixup commit | 创建修正提交 |
| `` S `` | Apply fixup commits | 压缩在所选提交之上的所有“fixup!”提交(自动压缩) |
| `` <c-j> `` | 下移提交 | |
| `` <c-k> `` | 上移提交 | |
@@ -251,7 +248,6 @@ If you would instead like to start an interactive rebase from the selected commi
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | 推送标签 | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 查看提交 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
@@ -344,15 +340,14 @@ If you would instead like to start an interactive rebase from the selected commi
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
| `` <space> `` | 检出 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` <space> `` | 检出 | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` n `` | 新分支 | |
| `` M `` | 合并到当前检出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
| `` r `` | 将已检出的分支变基到该分支 | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | 设置为检出分支的上游 |
| `` s `` | Sort order | |
| `` g `` | 查看重置选项 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 查看提交 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
+81 -86
View File
@@ -2,9 +2,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
# 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 |
|-----|--------|-------------|
@@ -14,8 +14,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` @ `` | 開啟命令記錄選單 | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | 推送 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | 拉取 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | 增加差異檢視中顯示變更周圍上下文的大小 | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | 減小差異檢視中顯示變更周圍上下文的大小 | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | 執行自訂命令 | Bring up a prompt where you can enter a shell command to execute. Not to be confused with pre-configured custom commands. |
@@ -31,10 +29,10 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` q `` | 結束 | |
| `` <esc> `` | 取消 | |
| `` <c-w> `` | 切換是否在差異檢視中顯示空格變更 | Toggle whether or not whitespace changes are shown in the diff view. |
| `` z `` | 復原 | 將使用 reflog 確任 git 指令以復原。這不包括工作區更改;只考慮提交。 |
| `` <c-z> `` | 取消復原 | 將使用 reflog 確任 git 指令以重作。這不包括工作區更改;只考慮提交。 |
| `` z `` | 復原 | 將使用 reflog 確定要運行哪個 git 命令以復原上一個 git 命令。這不包括工作區的更改;只考慮提交。 |
| `` <c-z> `` | 取消復原 | 將使用 reflog 確定要運行哪個 git 命令以重作上一個 git 命令。這不包括工作區的更改;只考慮提交。 |
## 移動
## 列表面板導航
| Key | Action | Info |
|-----|--------|-------------|
@@ -45,35 +43,47 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` v `` | 切換拖曳選擇 | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | 搜尋 | |
| `` / `` | 開始搜尋 | |
| `` H `` | 向左捲動 | |
| `` L `` | 向右捲動 | |
| `` ] `` | 下一個索引標籤 | |
| `` [ `` | 上一個索引標籤 | |
## 主面板 (補丁生成)
## Reflog
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 選擇上一段 | |
| `` <right> `` | 選擇下一段 | |
| `` v `` | 切換拖曳選擇 | |
| `` a `` | 切換選擇程式碼塊 | Toggle hunk selection mode. |
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
| `` o `` | 開啟檔案 | Open file in default application. |
| `` e `` | 編輯檔案 | Open file in external editor. |
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
| `` <esc> `` | 退出自訂補丁建立器 | |
| `` / `` | 搜尋 | |
| `` <c-o> `` | 複製提交 SHA 到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
| `` n `` | 從提交建立新分支 | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 主面板(一般)
## Worktrees
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
## 主視窗 (一般)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 向下捲動 | |
| `` mouse wheel up (fn+down) `` | 向上捲動 | |
## 主面板(合併)
## 主視窗 (合併中)
| Key | Action | Info |
|-----|--------|-------------|
@@ -89,7 +99,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` M `` | 開啟外部合併工具 (git mergetool) | Run `git mergetool`. |
| `` <esc> `` | 返回檔案面板 | |
## 主面板(預存)
## 主視窗 (預存中)
| Key | Action | Info |
|-----|--------|-------------|
@@ -109,7 +119,22 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` 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> |
| `` / `` | 搜尋 | |
| `` / `` | 開始搜尋 | |
## 主面板 (補丁生成)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 選擇上一段 | |
| `` <right> `` | 選擇下一段 | |
| `` v `` | 切換拖曳選擇 | |
| `` a `` | 切換選擇程式碼塊 | Toggle hunk selection mode. |
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
| `` o `` | 開啟檔案 | Open file in default application. |
| `` e `` | 編輯檔案 | Open file in external editor. |
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
| `` <esc> `` | 退出自訂補丁建立器 | |
| `` / `` | 開始搜尋 | |
## 功能表
@@ -117,13 +142,13 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|-----|--------|-------------|
| `` <enter> `` | 執行 | |
| `` <esc> `` | 關閉 | |
| `` / `` | 搜尋 | |
| `` / `` | Filter the current view by text | |
## 子提交
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <c-o> `` | 複製提交 SHA 到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
@@ -133,8 +158,8 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
| `` w `` | View worktree options | |
| `` / `` | 開始搜尋 | |
## 子模組
@@ -148,27 +173,17 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` e `` | 更新子模組 URL | |
| `` i `` | Initialize | 初始化子模組 |
| `` b `` | 查看批量子模組選項 | |
| `` / `` | 搜尋 | |
## 工作目錄
| 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 |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <c-o> `` | 複製提交 SHA 到剪貼簿 | |
| `` <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. |
| `` f `` | 修復 (Fixup) | Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded. |
| `` r `` | 改寫提交 | Reword the selected commit's message. |
| `` 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. |
@@ -176,14 +191,14 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | 挑選提交 (於變基過程中) |
| `` F `` | 建立修復提交 | 為此提交建立修復提交 |
| `` S `` | 壓縮上方所有「fixup」提交(自動壓縮) | 是否壓縮上方 {{.commit}} 所有「fixup」提交? |
| `` F `` | Create fixup commit | 為此提交建立修復提交 |
| `` S `` | Apply fixup commits | 壓縮上方所有的“fixup!”提交 (自動壓縮) |
| `` <c-j> `` | 向下移動提交 | |
| `` <c-k> `` | 向上移動提交 | |
| `` V `` | 貼上提交 (揀選) | |
| `` B `` | 為了變基已標注提交為基準提交 | 請為了下一次變基選擇一項基準提交;此將執行 `git rebase --onto`。 |
| `` 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. |
| `` 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. |
@@ -195,8 +210,8 @@ If you would instead like to start an interactive rebase from the selected commi
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
| `` w `` | View worktree options | |
| `` / `` | 開始搜尋 | |
## 提交摘要
@@ -211,15 +226,15 @@ If you would instead like to start an interactive rebase from the selected commi
|-----|--------|-------------|
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
| `` c `` | 檢出 | 檢出檔案 |
| `` d `` | Remove | Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file. |
| `` 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. |
| `` / `` | 搜尋 | |
| `` ` `` | 切換檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` / `` | 開始搜尋 | |
## 收藏 (Stash)
@@ -231,25 +246,8 @@ If you would instead like to start an interactive rebase from the selected commi
| `` 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 `` | 重新命名收藏 | |
| `` <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 `` | 從提交建立新分支 | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 本地分支
@@ -266,17 +264,16 @@ If you would instead like to start an interactive rebase from the selected commi
| `` F `` | 強制檢出 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 合併到當前檢出的分支 | Merge selected branch into currently checked out branch. |
| `` f `` | 從上游快進此分支 | Fast-forward selected branch from its upstream. |
| `` T `` | 建立標籤 | |
| `` s `` | Sort order | |
| `` g `` | 檢視重設選項 | |
| `` R `` | 重新命名分支 | |
| `` u `` | 檢視上游設定 | 檢視有關上游分支的設定(例如重設至上游) |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 標籤
@@ -287,10 +284,9 @@ If you would instead like to start an interactive rebase from the selected commi
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | 推送標籤 | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 檔案
@@ -302,7 +298,7 @@ If you would instead like to start an interactive rebase from the selected commi
| `` y `` | Copy to clipboard | |
| `` c `` | 提交變更 | Commit staged changes. |
| `` w `` | 沒有預提交 hook 就提交更改 | |
| `` A `` | 修改上次提交 | |
| `` 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. |
@@ -313,14 +309,14 @@ If you would instead like to start an interactive rebase from the selected commi
| `` S `` | 檢視收藏選項 | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | 全部預存/取消預存 | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | 選擇檔案中的單個程式碼塊/行,或展開/折疊目錄 | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Discard | View options for discarding changes to the selected file. |
| `` d `` | 檢視“捨棄更改”的選項 | View options for discarding changes to the selected file. |
| `` g `` | 檢視上游重設選項 | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` ` `` | 切換檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | 開啟外部合併工具 (git mergetool) | Run `git mergetool`. |
| `` f `` | 擷取 | Fetch changes from remote. |
| `` / `` | 搜尋 | |
| `` / `` | 開始搜尋 | |
## 狀態
@@ -348,22 +344,21 @@ If you would instead like to start an interactive rebase from the selected commi
| `` 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 | |
## 遠端分支
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` <space> `` | 檢出 | Checkout a new local branch based on the selected remote branch. The new branch will track the remote branch. |
| `` n `` | 新分支 | |
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` M `` | 合併到當前檢出的分支 | Merge selected branch into currently checked out branch. |
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | 將此分支設為當前分支之上游 |
| `` s `` | Sort order | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
+9 -10
View File
@@ -1,6 +1,6 @@
module github.com/jesseduffield/lazygit
go 1.22
go 1.21
require (
github.com/adrg/xdg v0.4.0
@@ -8,18 +8,19 @@ require (
github.com/aybabtme/humanlog v0.4.1
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.11
github.com/gdamore/tcell/v2 v2.7.4
github.com/fsmiamoto/git-todo-parser v0.0.5
github.com/gdamore/tcell/v2 v2.7.3
github.com/go-errors/errors v1.5.1
github.com/gookit/color v1.4.2
github.com/iancoleman/orderedmap v0.3.0
github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b
github.com/jesseduffield/gocui v0.3.1-0.20240303173746-f2b0f1f68dd8
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/jesseduffield/yaml v2.1.0+incompatible
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
@@ -34,11 +35,9 @@ require (
github.com/sirupsen/logrus v1.4.2
github.com/spf13/afero v1.9.5
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
github.com/stefanhaller/git-todo-parser v0.0.7-0.20240406123903-fd957137b6e2
github.com/stretchr/testify v1.8.1
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
golang.org/x/sync v0.7.0
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -49,7 +48,7 @@ require (
github.com/davecgh/go-spew v1.1.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/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
@@ -75,8 +74,8 @@ require (
github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
+12 -18
View File
@@ -84,12 +84,13 @@ github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
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/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/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.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
github.com/gdamore/tcell/v2 v2.7.3 h1:YLQlOj5F0hSlKy5TJvlych29+WTcJzbElnLYwx8gvdg=
github.com/gdamore/tcell/v2 v2.7.3/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
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=
@@ -171,8 +172,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@@ -188,14 +187,16 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b h1:oxCq0DvR2GMGf4UaoaASb0nQK/fJMQW3c3PNCLWCjS8=
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
github.com/jesseduffield/gocui v0.3.1-0.20240303173746-f2b0f1f68dd8 h1:DGyAjpaAnxDuKO4MEoFjifhkUV7sU6znMR9eRfjjvn0=
github.com/jesseduffield/gocui v0.3.1-0.20240303173746-f2b0f1f68dd8/go.mod h1:lLLfxEGyIvvkzzpHdKkfgIVFmxqEejeACxKMVxSHLeM=
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/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
github.com/jesseduffield/yaml v2.1.0+incompatible/go.mod h1:w0xGhOSIJCGYYW+hnFPTutCy5aACpkcwbmORt5axGqk=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/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=
@@ -281,8 +282,6 @@ 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/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.20240406123903-fd957137b6e2 h1:RTNWemd/9z9A5L/AggEP3OdhRz5dXETB/wdAlYF0SuM=
github.com/stefanhaller/git-todo-parser v0.0.7-0.20240406123903-fd957137b6e2/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/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -423,8 +422,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/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=
@@ -473,15 +470,13 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -491,9 +486,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
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=
+9 -15
View File
@@ -14,6 +14,7 @@ import (
"github.com/spf13/afero"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
@@ -118,14 +119,7 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
return app, err
}
// If we're not in a repo, repoPaths will be nil. 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 {
return app, err
}
showRecentRepos, err := app.setupRepo(repoPaths)
showRecentRepos, err := app.setupRepo()
if err != nil {
return app, err
}
@@ -174,16 +168,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
@@ -229,7 +221,6 @@ func (app *App) setupRepo(
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
return false, err
}
return false, nil
}
@@ -247,7 +238,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)
+26 -74
View File
@@ -8,11 +8,11 @@ import (
"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/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
// Sometimes lazygit will be invoked in daemon mode from a parent lazygit process.
@@ -33,14 +33,12 @@ const (
DaemonKindUnknown DaemonKind = iota
DaemonKindExitImmediately
DaemonKindRemoveUpdateRefsForCopiedBranch
DaemonKindCherryPick
DaemonKindMoveTodosUp
DaemonKindMoveTodosDown
DaemonKindInsertBreak
DaemonKindChangeTodoActions
DaemonKindMoveFixupCommitDown
DaemonKindWriteRebaseTodo
)
const (
@@ -54,15 +52,13 @@ func getInstruction() Instruction {
jsonData := os.Getenv(DaemonInstructionEnvKey)
mapping := map[DaemonKind]func(string) Instruction{
DaemonKindExitImmediately: deserializeInstruction[*ExitImmediatelyInstruction],
DaemonKindRemoveUpdateRefsForCopiedBranch: deserializeInstruction[*RemoveUpdateRefsForCopiedBranchInstruction],
DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction],
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
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],
DaemonKindMoveTodosUp: deserializeInstruction[*MoveTodosUpInstruction],
DaemonKindMoveTodosDown: deserializeInstruction[*MoveTodosDownInstruction],
DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction],
}
return mapping[getDaemonKind()](jsonData)
@@ -159,26 +155,6 @@ func NewExitImmediatelyInstruction() Instruction {
return &ExitImmediatelyInstruction{}
}
type RemoveUpdateRefsForCopiedBranchInstruction struct{}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) Kind() DaemonKind {
return DaemonKindRemoveUpdateRefsForCopiedBranch
}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return nil
})
}
func NewRemoveUpdateRefsForCopiedBranchInstruction() Instruction {
return &RemoveUpdateRefsForCopiedBranchInstruction{}
}
type CherryPickCommitsInstruction struct {
Todo string
}
@@ -234,7 +210,7 @@ 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,
Sha: c.Sha,
OldAction: todo.Pick,
NewAction: c.NewAction,
}
@@ -244,18 +220,18 @@ func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
})
}
// Takes the hash of some commit, and the hash of a fixup commit that was created
// 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"
type MoveFixupCommitDownInstruction struct {
OriginalHash string
FixupHash string
OriginalSha string
FixupSha string
}
func NewMoveFixupCommitDownInstruction(originalHash string, fixupHash string) Instruction {
func NewMoveFixupCommitDownInstruction(originalSha string, fixupSha string) Instruction {
return &MoveFixupCommitDownInstruction{
OriginalHash: originalHash,
FixupHash: fixupHash,
OriginalSha: originalSha,
FixupSha: fixupSha,
}
}
@@ -269,17 +245,17 @@ func (self *MoveFixupCommitDownInstruction) SerializedInstructions() string {
func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return utils.MoveFixupCommitDown(path, self.OriginalHash, self.FixupHash, getCommentChar())
return utils.MoveFixupCommitDown(path, self.OriginalSha, self.FixupSha, getCommentChar())
})
}
type MoveTodosUpInstruction struct {
Hashes []string
Shas []string
}
func NewMoveTodosUpInstruction(hashes []string) Instruction {
func NewMoveTodosUpInstruction(shas []string) Instruction {
return &MoveTodosUpInstruction{
Hashes: hashes,
Shas: shas,
}
}
@@ -292,9 +268,9 @@ func (self *MoveTodosUpInstruction) SerializedInstructions() string {
}
func (self *MoveTodosUpInstruction) run(common *common.Common) error {
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
todosToMove := lo.Map(self.Shas, func(sha string, _ int) utils.Todo {
return utils.Todo{
Hash: hash,
Sha: sha,
Action: todo.Pick,
}
})
@@ -305,12 +281,12 @@ func (self *MoveTodosUpInstruction) run(common *common.Common) error {
}
type MoveTodosDownInstruction struct {
Hashes []string
Shas []string
}
func NewMoveTodosDownInstruction(hashes []string) Instruction {
func NewMoveTodosDownInstruction(shas []string) Instruction {
return &MoveTodosDownInstruction{
Hashes: hashes,
Shas: shas,
}
}
@@ -323,9 +299,9 @@ func (self *MoveTodosDownInstruction) SerializedInstructions() string {
}
func (self *MoveTodosDownInstruction) run(common *common.Common) error {
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
todosToMove := lo.Map(self.Shas, func(sha string, _ int) utils.Todo {
return utils.Todo{
Hash: hash,
Sha: sha,
Action: todo.Pick,
}
})
@@ -354,27 +330,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)
})
}
+3 -8
View File
@@ -5,12 +5,11 @@ import (
"path/filepath"
"strings"
"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/env"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
type TodoLine struct {
@@ -22,7 +21,7 @@ func (self *TodoLine) ToString() string {
if self.Action == "break" {
return self.Action + "\n"
} else {
return self.Action + " " + self.Commit.Hash + " " + self.Commit.Name + "\n"
return self.Action + " " + self.Commit.Sha + " " + self.Commit.Name + "\n"
}
}
@@ -35,7 +34,7 @@ func TodoLinesToString(todoLines []TodoLine) string {
}
type ChangeTodoAction struct {
Hash string
Sha string
NewAction todo.TodoCommand
}
@@ -45,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
+1 -16
View File
@@ -4,8 +4,6 @@ import (
"bytes"
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"os"
"os/exec"
"path/filepath"
@@ -32,7 +30,6 @@ type cliArgs struct {
PrintVersionInfo bool
Debug bool
TailLogs bool
Profile bool
PrintDefaultConfig bool
PrintConfigDir bool
UseConfigDir string
@@ -148,14 +145,6 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
return
}
if cliArgs.Profile {
go func() {
if err := http.ListenAndServe("localhost:6060", nil); err != nil {
log.Fatal(err)
}
}()
}
parsedGitArg := parseGitArg(cliArgs.GitArg)
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest))
@@ -182,9 +171,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")
@@ -216,7 +202,6 @@ func parseCliArgsAndEnvVars() *cliArgs {
PrintVersionInfo: printVersionInfo,
Debug: debug,
TailLogs: tailLogs,
Profile: profile,
PrintDefaultConfig: printDefaultConfig,
PrintConfigDir: printConfigDir,
UseConfigDir: useConfigDir,
@@ -278,7 +263,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
+1 -4
View File
@@ -51,10 +51,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 {
+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)
})
}
+1 -1
View File
@@ -134,7 +134,7 @@ func NewGitCommandAux(
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.RebaseMode, gitCommon)
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
+12 -12
View File
@@ -76,7 +76,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,7 +86,7 @@ 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"))
@@ -94,8 +94,8 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
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
}
@@ -135,7 +135,7 @@ func (self *BisectCommands) StartWithTerms(oldTerm string, newTerm string) error
}
// 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 +143,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 +153,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 +185,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()
+10 -10
View File
@@ -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,7 +93,7 @@ func (self *BisectInfo) Bisecting() bool {
return false
}
if self.GetNewHash() == "" {
if self.GetNewSha() == "" {
return false
}
+3 -42
View File
@@ -7,12 +7,10 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/mgutz/str"
"github.com/samber/lo"
)
type BranchCommands struct {
*GitCommon
allBranchesLogCmdIndex uint8 // keeps track of current all branches log command
}
func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
@@ -30,26 +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()
}
// 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(
@@ -76,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
@@ -227,18 +205,13 @@ func (self *BranchCommands) Rename(oldName string, newName string) error {
type MergeOpts struct {
FastForwardOnly bool
Squash bool
}
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
if opts.Squash && opts.FastForwardOnly {
panic("Squash and FastForwardOnly can't both be true")
}
cmdArgs := NewGitCmd("merge").
Arg("--no-edit").
Arg(strings.Fields(self.UserConfig.Git.Merging.Args)...).
ArgIf(opts.FastForwardOnly, "--ff-only").
ArgIf(opts.Squash, "--squash", "--ff").
Arg(branchName).
ToArgv()
@@ -246,17 +219,5 @@ func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
}
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
// Only choose between non-empty, non-identical commands
candidates := lo.Uniq(lo.WithoutEmpty(append([]string{
self.UserConfig.Git.AllBranchesLogCmd,
},
self.UserConfig.Git.AllBranchesLogCmds...,
)))
n := len(candidates)
i := self.allBranchesLogCmdIndex
self.allBranchesLogCmdIndex = uint8((int(i) + 1) % n)
return self.cmd.New(str.ToArgv(candidates[i])).DontLog()
return self.cmd.New(str.ToArgv(self.UserConfig.Git.AllBranchesLogCmd)).DontLog()
}
+20 -138
View File
@@ -5,7 +5,6 @@ import (
"regexp"
"strconv"
"strings"
"time"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/go-git/v5/config"
@@ -15,7 +14,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"golang.org/x/exp/slices"
"golang.org/x/sync/errgroup"
)
// context:
@@ -42,7 +40,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 +47,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,14 +60,8 @@ 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) {
branches := self.obtainBranches(self.version.IsAtLeast(2, 22, 0))
func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch, error) {
branches := self.obtainBranches()
if self.AppState.LocalBranchSortOrder == "recency" {
reflogBranches := self.obtainReflogBranches(reflogCommits)
@@ -130,109 +119,12 @@ 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(canUsePushTrack bool) []*models.Branch {
func (self *BranchLoader) obtainBranches() []*models.Branch {
output, err := self.getRawBranches()
if err != nil {
panic(err)
@@ -255,7 +147,7 @@ func (self *BranchLoader) obtainBranches(canUsePushTrack bool) []*models.Branch
}
storeCommitDateAsRecency := self.AppState.LocalBranchSortOrder != "recency"
return obtainBranch(split, storeCommitDateAsRecency, canUsePushTrack), true
return obtainBranch(split, storeCommitDateAsRecency), true
})
}
@@ -291,31 +183,23 @@ var branchFields = []string{
"refname:short",
"upstream:short",
"upstream:track",
"push:track",
"subject",
"objectname",
"committerdate:unix",
}
// Obtain branch information from parsed line output of getRawBranches()
func obtainBranch(split []string, storeCommitDateAsRecency bool, canUsePushTrack bool) *models.Branch {
func obtainBranch(split []string, storeCommitDateAsRecency bool) *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]
commitDate := split[6]
name := strings.TrimPrefix(fullName, "heads/")
aheadForPull, behindForPull, gone := parseUpstreamInfo(upstreamName, track)
var aheadForPush, behindForPush string
if canUsePushTrack {
aheadForPush, behindForPush, _ = parseUpstreamInfo(upstreamName, pushTrack)
} else {
aheadForPush, behindForPush = aheadForPull, behindForPull
}
pushables, pullables, gone := parseUpstreamInfo(upstreamName, track)
recency := ""
if storeCommitDateAsRecency {
@@ -325,16 +209,14 @@ func obtainBranch(split []string, storeCommitDateAsRecency bool, canUsePushTrack
}
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,
Recency: recency,
Pushables: pushables,
Pullables: pullables,
UpstreamGone: gone,
Head: headMarker == "*",
Subject: subject,
CommitHash: commitHash,
}
}
@@ -350,10 +232,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 {
+45 -57
View File
@@ -25,101 +25,89 @@ func TestObtainBranch(t *testing.T) {
scenarios := []scenario{
{
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", "", "", "subject", "123", timeStamp},
input: []string{"", "heads/a_branch", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
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},
input: []string{"", "a_branch", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
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},
input: []string{"*", "a_branch", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
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},
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
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},
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
expectedBranch: &models.Branch{
Name: "a_branch",
UpstreamGone: true,
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
UpstreamGone: true,
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "WithCommitDateAsRecency",
input: []string{"", "a_branch", "", "", "", "subject", "123", timeStamp},
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",
Recency: "2h",
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, true)
branch := obtainBranch(s.input, s.storeCommitDateAsRecency)
assert.EqualValues(t, s.expectedBranch, branch)
})
}
+5
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}")
@@ -88,6 +89,7 @@ func TestBranchDeleteBranch(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
@@ -148,6 +150,7 @@ func TestBranchMerge(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs(s.expected, "", nil)
@@ -187,6 +190,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}))
@@ -275,6 +279,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())
+31 -68
View File
@@ -39,13 +39,13 @@ func (self *CommitCommands) SetAuthor(value string) error {
}
// 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)
func (self *CommitCommands) AddCoAuthor(sha string, value string) error {
message, err := self.GetCommitMessage(sha)
if err != nil {
return err
}
message = AddCoAuthorToMessage(message, author)
message = message + fmt.Sprintf("\nCo-authored-by: %s", value)
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only", "-m", message).
@@ -54,28 +54,9 @@ func (self *CommitCommands) AddCoAuthor(hash string, author string) error {
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
@@ -155,28 +136,26 @@ func (self *CommitCommands) signoffFlag() string {
}
}
func (self *CommitCommands) GetCommitMessage(commitHash string) (string, error) {
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
cmdArgs := NewGitCmd("log").
Arg("--format=%B", "--max-count=1", commitHash).
Config("log.showsignature=false").
Arg("--format=%B", "--max-count=1", commitSha).
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) {
func (self *CommitCommands) GetCommitSubject(commitSha string) (string, error) {
cmdArgs := NewGitCmd("log").
Arg("--format=%s", "--max-count=1", commitHash).
Config("log.showsignature=false").
Arg("--format=%s", "--max-count=1", commitSha).
ToArgv()
subject, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return strings.TrimSpace(subject), 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
@@ -187,9 +166,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()
@@ -206,14 +185,14 @@ 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()
@@ -224,19 +203,19 @@ func (self *CommitCommands) GetCommitMessagesFirstLine(hashes []string) (string,
// 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) {
func (self *CommitCommands) GetShasAndCommitMessagesFirstLine(shas []string) (string, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%h %s").
Arg(hashes...).
Arg(shas...).
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()
@@ -255,7 +234,7 @@ func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommands.ICmdObj {
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string) oscommands.ICmdObj {
contextSize := self.AppState.DiffContextSize
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
@@ -269,9 +248,8 @@ func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommand
Arg("--stat").
Arg("--decorate").
Arg("-p").
Arg(hash).
Arg(sha).
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
ArgIf(filterPath != "", "--", filterPath).
Dir(self.repoPaths.worktreePath).
ToArgv()
@@ -279,38 +257,23 @@ func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommand
return self.cmd.New(cmdArgs).DontLog()
}
// Revert reverts the selected commit by hash
func (self *CommitCommands) Revert(hash string) error {
cmdArgs := NewGitCmd("revert").Arg(hash).ToArgv()
// 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()
}
func (self *CommitCommands) RevertMerge(hash string, parentNumber int) error {
cmdArgs := NewGitCmd("revert").Arg(hash, "-m", fmt.Sprintf("%d", parentNumber)).
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()
}
+111 -30
View File
@@ -11,13 +11,13 @@ import (
"strings"
"sync"
"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/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:
@@ -35,6 +35,11 @@ type CommitLoader struct {
readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string
// List of main branches that exist in the repo.
// We use these to obtain the merge base of the branch.
// When nil, we're yet to obtain the list of existing main branches.
// When an empty slice, we've obtained the list and it's empty.
mainBranches []string
*GitCommon
}
@@ -51,6 +56,7 @@ func NewCommitLoader(
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
mainBranches: nil,
GitCommon: gitCommon,
}
}
@@ -66,7 +72,6 @@ type GetCommitsOptions struct {
All bool
// If non-empty, show divergence from this ref (left-right log)
RefToShowDivergenceFrom string
MainBranches *MainBranches
}
// GetCommits obtains the commits of the current branch
@@ -103,9 +108,9 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
go utils.Safe(func() {
defer wg.Done()
ancestor = opts.MainBranches.GetMergeBase(opts.RefName)
ancestor = self.getMergeBase(opts.RefName)
if opts.RefToShowDivergenceFrom != "" {
remoteAncestor = opts.MainBranches.GetMergeBase(opts.RefToShowDivergenceFrom)
remoteAncestor = self.getMergeBase(opts.RefToShowDivergenceFrom)
}
})
@@ -124,7 +129,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
}
for _, commit := range commits {
if commit.Hash == firstPushedCommit {
if commit.Sha == firstPushedCommit {
passedFirstPushedCommit = true
}
if commit.Status != models.StatusRebasing {
@@ -193,24 +198,24 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
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(line string, showDivergence bool) *models.Commit {
split := strings.SplitN(line, "\x00", 8)
hash := split[0]
sha := split[0]
unixTimestamp := split[1]
authorName := split[2]
authorEmail := split[3]
extraInfo := strings.TrimSpace(split[4])
parentHashes := split[5]
message := split[6]
divergence := models.DivergenceNone
if showDivergence {
divergence = lo.Ternary(split[6] == "<", models.DivergenceLeft, models.DivergenceRight)
divergence = lo.Ternary(split[7] == "<", models.DivergenceLeft, models.DivergenceRight)
}
message := split[7]
tags := []string{}
@@ -236,7 +241,7 @@ func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool
}
return &models.Commit{
Hash: hash,
Sha: sha,
Name: message,
Tags: tags,
ExtraInfo: extraInfo,
@@ -255,8 +260,8 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
return nil, nil
}
commitHashes := lo.FilterMap(commits, func(commit *models.Commit, _ int) (string, bool) {
return commit.Hash, commit.Hash != ""
commitShas := lo.FilterMap(commits, func(commit *models.Commit, _ int) (string, bool) {
return commit.Sha, commit.Sha != ""
})
// note that we're not filtering these as we do non-rebasing commits just because
@@ -265,14 +270,14 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
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) {
commit := self.extractCommitFromLine(line, false)
fullCommits[commit.Hash] = commit
fullCommits[commit.Sha] = commit
return false, nil
})
if err != nil {
@@ -280,23 +285,23 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
}
findFullCommit := lo.Ternary(self.version.IsOlderThan(2, 25, 2),
func(hash string) *models.Commit {
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(commits))
for _, rebasingCommit := range commits {
if rebasingCommit.Hash == "" {
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)
@@ -332,9 +337,9 @@ func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) []*mod
// See if the current commit couldn't be applied because it conflicted; if
// so, add a fake entry for it
if conflictedCommitHash := self.getConflictedCommit(todos); conflictedCommitHash != "" {
if conflictedCommitSha := self.getConflictedCommit(todos); conflictedCommitSha != "" {
commits = append(commits, &models.Commit{
Hash: conflictedCommitHash,
Sha: conflictedCommitSha,
Name: "",
Status: models.StatusRebasing,
Action: models.ActionConflict,
@@ -343,15 +348,13 @@ func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) []*mod
for _, t := range todos {
if t.Command == todo.UpdateRef {
t.Msg = t.Ref
} else if t.Command == todo.Exec {
t.Msg = t.ExecCommand
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.Commit{
Hash: t.Commit,
Sha: t.Commit,
Name: t.Msg,
Status: models.StatusRebasing,
Action: t.Command,
@@ -455,8 +458,8 @@ func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
passedAncestor := false
for i, commit := range commits {
// some commits aren't really commits and don't have hashes, such as the update-ref todo
if commit.Hash != "" && strings.HasPrefix(ancestor, commit.Hash) {
// some commits aren't really commits and don't have sha's, such as the update-ref todo
if commit.Sha != "" && strings.HasPrefix(ancestor, commit.Sha) {
passedAncestor = true
}
if commit.Status != models.StatusPushed && commit.Status != models.StatusUnpushed {
@@ -468,6 +471,84 @@ func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
}
}
func (self *CommitLoader) getMergeBase(refName string) string {
if self.mainBranches == nil {
self.mainBranches = self.getExistingMainBranches()
}
if len(self.mainBranches) == 0 {
return ""
}
// We pass all configured main branches to the merge-base call; git will
// return the base commit for the closest one.
output, err := self.cmd.New(
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
// If there's an error, it must be because one of the main branches that
// used to exist when we called getExistingMainBranches() was deleted
// meanwhile. To fix this for next time, throw away our cache.
self.mainBranches = nil
}
return ignoringWarnings(output)
}
func (self *CommitLoader) getExistingMainBranches() []string {
var existingBranches []string
var wg sync.WaitGroup
mainBranches := self.UserConfig.Git.MainBranches
existingBranches = make([]string, len(mainBranches))
for i, branchName := range mainBranches {
wg.Add(1)
i := i
branchName := branchName
go utils.Safe(func() {
defer wg.Done()
// Try to determine upstream of local main branch
if ref, err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
).DontLog().RunWithOutput(); err == nil {
existingBranches[i] = strings.TrimSpace(ref)
return
}
// If this failed, a local branch for this main branch doesn't exist or it
// has no upstream configured. Try looking for one in the "origin" remote.
ref := "refs/remotes/origin/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
return
}
// If this failed as well, try if we have the main branch as a local
// branch. This covers the case where somebody is using git locally
// for something, but never pushing anywhere.
ref = "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
}
})
}
wg.Wait()
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
return branch != ""
})
return existingBranches
}
func ignoringWarnings(commandOutput string) string {
trimmedOutput := strings.TrimSpace(commandOutput)
split := strings.Split(trimmedOutput, "\n")
@@ -478,7 +559,7 @@ func ignoringWarnings(commandOutput string) string {
return lastLine
}
// getFirstPushedCommit returns the first commit hash which has been pushed to the ref's upstream.
// getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream.
// all commits above this are deemed unpushed and marked as such.
func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
output, err := self.cmd.New(
@@ -524,4 +605,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
return self.cmd.New(cmdArgs).DontLog()
}
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s`
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m`
+55 -57
View File
@@ -5,26 +5,26 @@ import (
"strings"
"testing"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/go-errors/errors"
"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/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stefanhaller/git-todo-parser/todo"
"github.com/stretchr/testify/assert"
)
var commitsOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode
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 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.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode`, "|", "\x00", -1)
var singleCommitOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|better typing for rebase mode`, "|", "\x00", -1)
func TestGetCommits(t *testing.T) {
type scenario struct {
@@ -46,7 +46,7 @@ func TestGetCommits(t *testing.T) {
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -58,7 +58,7 @@ func TestGetCommits(t *testing.T) {
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
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%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -73,7 +73,7 @@ func TestGetCommits(t *testing.T) {
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%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%x00%m", "--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
@@ -86,7 +86,7 @@ func TestGetCommits(t *testing.T) {
expectedCommits: []*models.Commit{
{
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
@@ -100,7 +100,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164",
Sha: "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164",
Name: "fix logging",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -114,7 +114,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c",
Sha: "e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c",
Name: "refactor",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -128,7 +128,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "d8084cd558925eb7c9c38afeed5725c21653ab90",
Sha: "d8084cd558925eb7c9c38afeed5725c21653ab90",
Name: "WIP",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -142,7 +142,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "65f910ebd85283b5cce9bf67d03d3f1a9ea3813a",
Sha: "65f910ebd85283b5cce9bf67d03d3f1a9ea3813a",
Name: "WIP",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -156,7 +156,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "26c07b1ab33860a1a7591a0638f9925ccf497ffa",
Sha: "26c07b1ab33860a1a7591a0638f9925ccf497ffa",
Name: "WIP",
Status: models.StatusMerged,
Action: models.ActionNone,
@@ -170,7 +170,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "3d4470a6c072208722e5ae9a54bcb9634959a1c5",
Sha: "3d4470a6c072208722e5ae9a54bcb9634959a1c5",
Name: "WIP",
Status: models.StatusMerged,
Action: models.ActionNone,
@@ -184,7 +184,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Hash: "053a66a7be3da43aacdc7aa78e1fe757b82c4dd2",
Sha: "053a66a7be3da43aacdc7aa78e1fe757b82c4dd2",
Name: "refactoring the config struct",
Status: models.StatusMerged,
Action: models.ActionNone,
@@ -210,7 +210,7 @@ func TestGetCommits(t *testing.T) {
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%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%x00%m", "--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")).
@@ -221,7 +221,7 @@ func TestGetCommits(t *testing.T) {
expectedCommits: []*models.Commit{
{
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
@@ -247,7 +247,7 @@ func TestGetCommits(t *testing.T) {
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%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%x00%m", "--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")).
@@ -260,7 +260,7 @@ func TestGetCommits(t *testing.T) {
expectedCommits: []*models.Commit{
{
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
@@ -283,7 +283,7 @@ func TestGetCommits(t *testing.T) {
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -295,7 +295,7 @@ func TestGetCommits(t *testing.T) {
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -303,15 +303,15 @@ func TestGetCommits(t *testing.T) {
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.testName, func(t *testing.T) {
common := utils.NewDummyCommon()
common.AppState = &config.AppState{}
common.AppState.GitLogOrder = scenario.logOrder
cmd := oscommands.NewDummyCmdObjBuilder(scenario.runner)
builder := &CommitLoader{
Common: common,
cmd: cmd,
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) {
@@ -323,9 +323,7 @@ func TestGetCommits(t *testing.T) {
}
common.UserConfig.Git.MainBranches = scenario.mainBranches
opts := scenario.opts
opts.MainBranches = NewMainBranches(scenario.mainBranches, cmd)
commits, err := builder.GetCommits(opts)
commits, err := builder.GetCommits(scenario.opts)
assert.Equal(t, scenario.expectedCommits, commits)
assert.Equal(t, scenario.expectedError, err)
@@ -341,14 +339,14 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
todos []todo.Todo
doneTodos []todo.Todo
amendFileExists bool
expectedHash string
expectedSha string
}{
{
testName: "no done todos",
todos: []todo.Todo{},
doneTodos: []todo.Todo{},
amendFileExists: false,
expectedHash: "",
expectedSha: "",
},
{
testName: "common case (conflict)",
@@ -364,7 +362,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedHash: "fa1afe1",
expectedSha: "fa1afe1",
},
{
testName: "last command was 'break'",
@@ -373,7 +371,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
{Command: todo.Break},
},
amendFileExists: false,
expectedHash: "",
expectedSha: "",
},
{
testName: "last command was 'exec'",
@@ -385,7 +383,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedHash: "",
expectedSha: "",
},
{
testName: "last command was 'reword'",
@@ -394,7 +392,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
{Command: todo.Reword},
},
amendFileExists: false,
expectedHash: "",
expectedSha: "",
},
{
testName: "'pick' was rescheduled",
@@ -411,7 +409,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedHash: "",
expectedSha: "",
},
{
testName: "'pick' was rescheduled, buggy git version",
@@ -436,7 +434,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedHash: "",
expectedSha: "",
},
{
testName: "conflicting 'pick' after 'exec'",
@@ -461,7 +459,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedHash: "fa1afe1",
expectedSha: "fa1afe1",
},
{
testName: "'edit' with amend file",
@@ -473,7 +471,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: true,
expectedHash: "",
expectedSha: "",
},
{
testName: "'edit' without amend file",
@@ -485,7 +483,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedHash: "fa1afe1",
expectedSha: "fa1afe1",
},
}
for _, scenario := range scenarios {
@@ -505,8 +503,8 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
}
hash := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists)
assert.Equal(t, scenario.expectedHash, hash)
sha := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists)
assert.Equal(t, scenario.expectedSha, sha)
})
}
}
@@ -523,29 +521,29 @@ func TestCommitLoader_setCommitMergedStatuses(t *testing.T) {
{
testName: "basic",
commits: []*models.Commit{
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Hash: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusPushed},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusPushed},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
},
ancestor: "67890",
expectedCommits: []*models.Commit{
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Hash: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusMerged},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusMerged},
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusMerged},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusMerged},
},
},
{
testName: "with update-ref",
commits: []*models.Commit{
{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},
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
},
ancestor: "deadbeef",
expectedCommits: []*models.Commit{
{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},
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
},
},
}
+49 -175
View File
@@ -30,6 +30,7 @@ func TestCommitRewordCommit(t *testing.T) {
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
@@ -99,6 +100,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
@@ -134,6 +136,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
@@ -150,7 +153,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)
}
@@ -158,7 +161,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) {
@@ -168,59 +171,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()
})
}
@@ -228,80 +182,65 @@ func TestCommitCreateAmendCommit(t *testing.T) {
func TestCommitShowCmdObj(t *testing.T) {
type scenario struct {
testName string
filterPath string
contextSize int
similarityThreshold int
ignoreWhitespace bool
extDiffCmd string
expected []string
testName string
filterPath string
contextSize int
ignoreWhitespace bool
extDiffCmd string
expected []string
}
scenarios := []scenario{
{
testName: "Default case without filter path",
filterPath: "",
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
testName: "Default case without filter path",
filterPath: "",
contextSize: 3,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
},
{
testName: "Default case with filter path",
filterPath: "file.txt",
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--", "file.txt"},
testName: "Default case with filter path",
filterPath: "file.txt",
contextSize: 3,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
},
{
testName: "Show diff with custom context size",
filterPath: "",
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
testName: "Show diff with custom context size",
filterPath: "",
contextSize: 77,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
},
{
testName: "Show diff with custom similarity threshold",
filterPath: "",
contextSize: 3,
similarityThreshold: 33,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%"},
testName: "Show diff, ignoring whitespace",
filterPath: "",
contextSize: 77,
ignoreWhitespace: true,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
},
{
testName: "Show diff, ignoring whitespace",
filterPath: "",
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: true,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%"},
},
{
testName: "Show diff with external diff command",
filterPath: "",
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "difft --color=always",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
testName: "Show diff with external diff command",
filterPath: "",
contextSize: 3,
ignoreWhitespace: false,
extDiffCmd: "difft --color=always",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd
appState := &config.AppState{}
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
appState.DiffContextSize = s.contextSize
appState.RenameSimilarityThreshold = s.similarityThreshold
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
repoPaths := RepoPaths{
@@ -344,9 +283,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{"log", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
})
output, err := instance.GetCommitMessage("deadbeef")
@@ -367,14 +307,14 @@ 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{"log", "--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{"log", "--format=%B", "--max-count=1", "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)
@@ -383,6 +323,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})
@@ -392,70 +333,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)
})
}
}
+2 -12
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
@@ -17,16 +13,10 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
}
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
useExtDiff := extDiffCmd != ""
return self.cmd.New(
NewGitCmd("diff").
Config("diff.noprefix=false").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
Arg("--submodule").
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
Arg("--submodule", "--no-ext-diff", "--color").
Arg(diffArgs...).
Dir(self.repoPaths.worktreePath).
ToArgv(),
+3 -7
View File
@@ -8,7 +8,6 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
type FileCommands struct {
@@ -76,21 +75,18 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
return utils.ResolvePlaceholderString(editCmdTemplate, templateValues), nil
}
func (self *FileCommands) GetEditCmdStr(filenames []string) (string, bool) {
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 multiple files are selected, we'll simply edit just the first one.
// It's not worth fixing this for the legacy support.
if cmdStr, err := self.GetEditCmdStrLegacy(filenames[0], 1); err == nil {
if cmdStr, err := self.GetEditCmdStrLegacy(filename, 1); err == nil {
return cmdStr, true
}
}
template, suspend := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
quotedFilenames := lo.Map(filenames, func(filename string, _ int) string { return self.cmd.Quote(filename) })
templateValues := map[string]string{
"filename": strings.Join(quotedFilenames, " "),
"filename": self.cmd.Quote(filename),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
+3 -7
View File
@@ -100,19 +100,15 @@ type FileStatus struct {
PreviousName string
}
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.AppState.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
}
+10 -19
View File
@@ -5,31 +5,27 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/stretchr/testify/assert"
)
func TestFileGetStatusFiles(t *testing.T) {
type scenario struct {
testName string
similarityThreshold int
runner oscommands.ICmdObjRunner
expectedFiles []*models.File
testName string
runner oscommands.ICmdObjRunner
expectedFiles []*models.File
}
scenarios := []scenario{
{
"No files found",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "", nil),
[]*models.File{},
},
{
"Several files found",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
nil,
),
@@ -98,9 +94,8 @@ func TestFileGetStatusFiles(t *testing.T) {
},
{
"File with new line char",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "MM a\nb.txt", nil),
[]*models.File{
{
Name: "a\nb.txt",
@@ -118,9 +113,8 @@ func TestFileGetStatusFiles(t *testing.T) {
},
{
"Renamed files",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
nil,
),
@@ -155,9 +149,8 @@ func TestFileGetStatusFiles(t *testing.T) {
},
{
"File with arrow in name",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
`?? a -> b.txt`,
nil,
),
@@ -179,14 +172,12 @@ func TestFileGetStatusFiles(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)
appState := &config.AppState{}
appState.RenameSimilarityThreshold = s.similarityThreshold
loader := &FileLoader{
GitCommon: buildGitCommon(commonDeps{appState: appState}),
GitCommon: buildGitCommon(commonDeps{}),
cmd: cmd,
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
getFileType: func(string) string { return "file" },
+6 -14
View File
@@ -177,9 +177,9 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
}
}
func TestEditFilesCmd(t *testing.T) {
func TestEditFileCmd(t *testing.T) {
type scenario struct {
filenames []string
filename string
osConfig config.OSConfig
expectedCmdStr string
suspend bool
@@ -187,13 +187,13 @@ func TestEditFilesCmd(t *testing.T) {
scenarios := []scenario{
{
filenames: []string{"test"},
filename: "test",
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
suspend: true,
},
{
filenames: []string{"test"},
filename: "test",
osConfig: config.OSConfig{
Edit: "nano {{filename}}",
},
@@ -201,21 +201,13 @@ func TestEditFilesCmd(t *testing.T) {
suspend: 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,
},
}
for _, s := range scenarios {
@@ -226,7 +218,7 @@ func TestEditFilesCmd(t *testing.T) {
userConfig: userConfig,
})
cmdStr, suspend := instance.GetEditCmdStr(s.filenames)
cmdStr, suspend := instance.GetEditCmdStr(s.filename)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.suspend, suspend)
}
+2
View File
@@ -23,6 +23,7 @@ func TestStartCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildFlowCommands(commonDeps{})
@@ -68,6 +69,7 @@ func TestFinishCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildFlowCommands(commonDeps{
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
-122
View File
@@ -1,122 +0,0 @@
package git_commands
import (
"strings"
"sync"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
)
type MainBranches struct {
// List of main branches configured by the user. Just the bare names.
configuredMainBranches []string
// Which of these actually exist in the repository. Full ref names, and it
// could be either "refs/heads/..." or "refs/remotes/origin/..." depending
// on which one exists for a given bare name.
existingMainBranches []string
cmd oscommands.ICmdObjBuilder
mutex *deadlock.Mutex
}
func NewMainBranches(
configuredMainBranches []string,
cmd oscommands.ICmdObjBuilder,
) *MainBranches {
return &MainBranches{
configuredMainBranches: configuredMainBranches,
existingMainBranches: nil,
cmd: cmd,
mutex: &deadlock.Mutex{},
}
}
// Get the list of main branches that exist in the repository. This is a list of
// full ref names.
func (self *MainBranches) Get() []string {
self.mutex.Lock()
defer self.mutex.Unlock()
if self.existingMainBranches == nil {
self.existingMainBranches = self.determineMainBranches()
}
return self.existingMainBranches
}
// Return the merge base of the given refName with the closest main branch.
func (self *MainBranches) GetMergeBase(refName string) string {
mainBranches := self.Get()
if len(mainBranches) == 0 {
return ""
}
// We pass all existing main branches to the merge-base call; git will
// return the base commit for the closest one.
// We ignore errors from this call, since we can't distinguish whether the
// error is because one of the main branches has been deleted since the last
// call to determineMainBranches, or because the refName has no common
// history with any of the main branches. Since the former should happen
// very rarely, users must quit and restart lazygit to fix it; the latter is
// also not very common, but can totally happen and is not an error.
output, _ := self.cmd.New(
NewGitCmd("merge-base").Arg(refName).Arg(mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
return ignoringWarnings(output)
}
func (self *MainBranches) determineMainBranches() []string {
var existingBranches []string
var wg sync.WaitGroup
existingBranches = make([]string, len(self.configuredMainBranches))
for i, branchName := range self.configuredMainBranches {
wg.Add(1)
go utils.Safe(func() {
defer wg.Done()
// Try to determine upstream of local main branch
if ref, err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
).DontLog().RunWithOutput(); err == nil {
existingBranches[i] = strings.TrimSpace(ref)
return
}
// If this failed, a local branch for this main branch doesn't exist or it
// has no upstream configured. Try looking for one in the "origin" remote.
ref := "refs/remotes/origin/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
return
}
// If this failed as well, try if we have the main branch as a local
// branch. This covers the case where somebody is using git locally
// for something, but never pushing anywhere.
ref = "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
}
})
}
wg.Wait()
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
return branch != ""
})
return existingBranches
}
+13 -13
View File
@@ -4,12 +4,12 @@ import (
"path/filepath"
"time"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/app/daemon"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/stefanhaller/git-todo-parser/todo"
)
type PatchCommands struct {
@@ -47,8 +47,8 @@ type ApplyPatchOpts struct {
Reverse bool
}
func (self *PatchCommands) ApplyCustomPatch(reverse bool, turnAddedFilesIntoDiffAgainstEmptyFile bool) error {
patch := self.PatchBuilder.PatchToApply(reverse, turnAddedFilesIntoDiffAgainstEmptyFile)
func (self *PatchCommands) ApplyCustomPatch(reverse bool) error {
patch := self.PatchBuilder.PatchToApply(reverse)
return self.ApplyPatch(patch, ApplyPatchOpts{
Index: true,
@@ -94,7 +94,7 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com
}
// apply each patch in reverse
if err := self.ApplyCustomPatch(true, true); err != nil {
if err := self.ApplyCustomPatch(true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -123,7 +123,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
}
// apply each patch forward
if err := self.ApplyCustomPatch(false, false); err != nil {
if err := self.ApplyCustomPatch(false); err != nil {
// Don't abort the rebase here; this might cause conflicts, so give
// the user a chance to resolve them
return err
@@ -157,13 +157,13 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
baseIndex := sourceCommitIdx + 1
changes := []daemon.ChangeTodoAction{
{Hash: commits[sourceCommitIdx].Hash, NewAction: todo.Edit},
{Hash: commits[destinationCommitIdx].Hash, NewAction: todo.Edit},
{Sha: commits[sourceCommitIdx].Sha, NewAction: todo.Edit},
{Sha: commits[destinationCommitIdx].Sha, NewAction: todo.Edit},
}
self.os.LogCommand(logTodoChanges(changes), false)
err := self.rebase.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: commits[baseIndex].Hash,
baseShaOrRoot: commits[baseIndex].Sha,
overrideEditor: true,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
}).Run()
@@ -172,7 +172,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
}
// apply each patch in reverse
if err := self.ApplyCustomPatch(true, true); err != nil {
if err := self.ApplyCustomPatch(true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -219,7 +219,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitIdx int, stash bool) error {
if stash {
if err := self.stash.Push(self.Tr.StashPrefix + commits[commitIdx].Hash); err != nil {
if err := self.stash.Push(self.Tr.StashPrefix + commits[commitIdx].Sha); err != nil {
return err
}
}
@@ -228,7 +228,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
return err
}
if err := self.ApplyCustomPatch(true, true); err != nil {
if err := self.ApplyCustomPatch(true); err != nil {
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
_ = self.rebase.AbortRebase()
}
@@ -282,7 +282,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(
return err
}
if err := self.ApplyCustomPatch(true, true); err != nil {
if err := self.ApplyCustomPatch(true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -324,7 +324,7 @@ func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string,
cmdArgs := NewGitCmd("diff").
Config("diff.noprefix=false").
Arg("--no-ext-diff").
Arg("HEAD.." + commit.Hash).
Arg("HEAD.." + commit.Sha).
ToArgv()
return self.cmd.New(cmdArgs).RunWithOutput()
+75 -138
View File
@@ -5,13 +5,13 @@ import (
"path/filepath"
"strings"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/app/daemon"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
type RebaseCommands struct {
@@ -56,84 +56,79 @@ func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, su
func (self *RebaseCommands) RewordCommitInEditor(commits []*models.Commit, index int) (oscommands.ICmdObj, error) {
changes := []daemon.ChangeTodoAction{{
Hash: commits[index].Hash,
Sha: commits[index].Sha,
NewAction: todo.Reword,
}}
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: getBaseHashOrRoot(commits, index+1),
instruction: daemon.NewChangeTodoActionsInstruction(changes),
baseShaOrRoot: getBaseShaOrRoot(commits, index+1),
instruction: daemon.NewChangeTodoActionsInstruction(changes),
}), nil
}
func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, start, end int) error {
return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, index int) error {
return self.GenericAmend(commits, index, func() error {
return self.commit.ResetAuthor()
})
}
func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, start, end int, value string) error {
return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, index int, value string) error {
return self.GenericAmend(commits, index, func() error {
return self.commit.SetAuthor(value)
})
}
func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, start, end int, value string) error {
return self.GenericAmend(commits, start, end, func(commit *models.Commit) error {
return self.commit.AddCoAuthor(commit.Hash, value)
func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, index int, value string) error {
return self.GenericAmend(commits, index, func() error {
return self.commit.AddCoAuthor(commits[index].Sha, value)
})
}
func (self *RebaseCommands) GenericAmend(commits []*models.Commit, start, end int, f func(commit *models.Commit) error) error {
if start == end && models.IsHeadCommit(commits, start) {
func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f func() error) error {
if models.IsHeadCommit(commits, index) {
// we've selected the top commit so no rebase is required
return f(commits[start])
return f()
}
err := self.BeginInteractiveRebaseForCommitRange(commits, start, end, false)
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
if err != nil {
return err
}
for commitIndex := end; commitIndex >= start; commitIndex-- {
err = f(commits[commitIndex])
if err != nil {
return err
}
if err := self.ContinueRebase(); err != nil {
return err
}
// now the selected commit should be our head so we'll amend it
err = f()
if err != nil {
return err
}
return nil
return self.ContinueRebase()
}
func (self *RebaseCommands) MoveCommitsDown(commits []*models.Commit, startIdx int, endIdx int) error {
baseHashOrRoot := getBaseHashOrRoot(commits, endIdx+2)
baseShaOrRoot := getBaseShaOrRoot(commits, endIdx+2)
hashes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
return commit.Hash
shas := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
return commit.Sha
})
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: baseHashOrRoot,
instruction: daemon.NewMoveTodosDownInstruction(hashes),
baseShaOrRoot: baseShaOrRoot,
instruction: daemon.NewMoveTodosDownInstruction(shas),
overrideEditor: true,
}).Run()
}
func (self *RebaseCommands) MoveCommitsUp(commits []*models.Commit, startIdx int, endIdx int) error {
baseHashOrRoot := getBaseHashOrRoot(commits, endIdx+1)
baseShaOrRoot := getBaseShaOrRoot(commits, endIdx+1)
hashes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
return commit.Hash
shas := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
return commit.Sha
})
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: baseHashOrRoot,
instruction: daemon.NewMoveTodosUpInstruction(hashes),
baseShaOrRoot: baseShaOrRoot,
instruction: daemon.NewMoveTodosUpInstruction(shas),
overrideEditor: true,
}).Run()
}
@@ -144,11 +139,11 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, startIdx
baseIndex++
}
baseHashOrRoot := getBaseHashOrRoot(commits, baseIndex)
baseShaOrRoot := getBaseShaOrRoot(commits, baseIndex)
changes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) daemon.ChangeTodoAction {
return daemon.ChangeTodoAction{
Hash: commit.Hash,
Sha: commit.Sha,
NewAction: action,
}
})
@@ -156,7 +151,7 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, startIdx
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: baseHashOrRoot,
baseShaOrRoot: baseShaOrRoot,
overrideEditor: true,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
}).Run()
@@ -171,8 +166,8 @@ func (self *RebaseCommands) EditRebase(branchRef string) error {
)
self.os.LogCommand(msg, false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: branchRef,
instruction: daemon.NewInsertBreakInstruction(),
baseShaOrRoot: branchRef,
instruction: daemon.NewInsertBreakInstruction(),
}).Run()
}
@@ -186,21 +181,21 @@ func (self *RebaseCommands) EditRebaseFromBaseCommit(targetBranchName string, ba
)
self.os.LogCommand(msg, false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: baseCommit,
onto: targetBranchName,
instruction: daemon.NewInsertBreakInstruction(),
baseShaOrRoot: baseCommit,
onto: targetBranchName,
instruction: daemon.NewInsertBreakInstruction(),
}).Run()
}
func logTodoChanges(changes []daemon.ChangeTodoAction) string {
changeTodoStr := strings.Join(lo.Map(changes, func(c daemon.ChangeTodoAction, _ int) string {
return fmt.Sprintf("%s:%s", c.Hash, c.NewAction)
return fmt.Sprintf("%s:%s", c.Sha, c.NewAction)
}), "\n")
return fmt.Sprintf("Changing TODO actions:\n%s", changeTodoStr)
}
type PrepareInteractiveRebaseCommandOpts struct {
baseHashOrRoot string
baseShaOrRoot string
onto string
instruction daemon.Instruction
overrideEditor bool
@@ -209,7 +204,7 @@ type PrepareInteractiveRebaseCommandOpts struct {
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
// we tell git to run lazygit to edit the todo list, and we pass the client
// lazygit instructions what to do with the todo file
// lazygit a todo string to write to the todo file
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
ex := oscommands.GetLazygitPath()
@@ -221,7 +216,7 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
Arg("--no-autosquash").
ArgIf(self.version.IsAtLeast(2, 22, 0), "--rebase-merges").
ArgIf(opts.onto != "", "--onto", opts.onto).
Arg(opts.baseHashOrRoot).
Arg(opts.baseShaOrRoot).
ToArgv()
debug := "FALSE"
@@ -238,7 +233,7 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
if opts.instruction != nil {
cmdObj.AddEnvVars(daemon.ToEnvVars(opts.instruction)...)
} else {
cmdObj.AddEnvVars(daemon.ToEnvVars(daemon.NewRemoveUpdateRefsForCopiedBranchInstruction())...)
gitSequenceEditor = "true"
}
cmdObj.AddEnvVars(
@@ -255,71 +250,33 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
return cmdObj
}
// GitRebaseEditTodo runs "git rebase --edit-todo", saving the given todosFileContent to the file
func (self *RebaseCommands) GitRebaseEditTodo(todosFileContent []byte) error {
ex := oscommands.GetLazygitPath()
cmdArgs := NewGitCmd("rebase").
Arg("--edit-todo").
ToArgv()
debug := "FALSE"
if self.Debug {
debug = "TRUE"
}
self.Log.WithField("command", cmdArgs).Debug("RunCommand")
cmdObj := self.cmd.New(cmdArgs)
cmdObj.AddEnvVars(daemon.ToEnvVars(daemon.NewWriteRebaseTodoInstruction(todosFileContent))...)
cmdObj.AddEnvVars(
"DEBUG="+debug,
"LANG=en_US.UTF-8", // Force using EN as language
"LC_ALL=en_US.UTF-8", // Force using EN as language
"GIT_EDITOR="+ex,
"GIT_SEQUENCE_EDITOR="+ex,
)
return cmdObj.Run()
}
// AmendTo amends the given commit with whatever files are staged
func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) error {
commit := commits[commitIndex]
if err := self.commit.CreateFixupCommit(commit.Hash); err != nil {
if err := self.commit.CreateFixupCommit(commit.Sha); err != nil {
return err
}
// Get the hash of the commit we just created
// Get the sha of the commit we just created
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
fixupHash, err := self.cmd.New(cmdArgs).RunWithOutput()
fixupSha, err := self.cmd.New(cmdArgs).RunWithOutput()
if err != nil {
return err
}
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
overrideEditor: true,
instruction: daemon.NewMoveFixupCommitDownInstruction(commit.Hash, fixupHash),
instruction: daemon.NewMoveFixupCommitDownInstruction(commit.Sha, fixupSha),
}).Run()
}
func todoFromCommit(commit *models.Commit) utils.Todo {
if commit.Action == todo.UpdateRef {
return utils.Todo{Ref: commit.Name, Action: commit.Action}
} else {
return utils.Todo{Hash: commit.Hash, Action: commit.Action}
}
}
// Sets the action for the given commits in the git-rebase-todo file
func (self *RebaseCommands) EditRebaseTodo(commits []*models.Commit, action todo.TodoCommand) error {
commitsWithAction := lo.Map(commits, func(commit *models.Commit, _ int) utils.TodoChange {
return utils.TodoChange{
Hash: commit.Hash,
Sha: commit.Sha,
OldAction: commit.Action,
NewAction: action,
}
@@ -332,27 +289,13 @@ func (self *RebaseCommands) EditRebaseTodo(commits []*models.Commit, action todo
)
}
func (self *RebaseCommands) DeleteUpdateRefTodos(commits []*models.Commit) error {
todosToDelete := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
return todoFromCommit(commit)
})
todosFileContent, err := utils.DeleteTodos(
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
todosToDelete,
self.config.GetCoreCommentChar(),
)
if err != nil {
return err
}
return self.GitRebaseEditTodo(todosFileContent)
}
func (self *RebaseCommands) MoveTodosDown(commits []*models.Commit) error {
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
return todoFromCommit(commit)
return utils.Todo{
Sha: commit.Sha,
Action: commit.Action,
}
})
return utils.MoveTodosDown(fileName, todosToMove, self.config.GetCoreCommentChar())
@@ -361,7 +304,10 @@ func (self *RebaseCommands) MoveTodosDown(commits []*models.Commit) error {
func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
return todoFromCommit(commit)
return utils.Todo{
Sha: commit.Sha,
Action: commit.Action,
}
})
return utils.MoveTodosUp(fileName, todosToMove, self.config.GetCoreCommentChar())
@@ -369,13 +315,13 @@ func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) error {
hashOrRoot := commit.Hash + "^"
shaOrRoot := commit.Sha + "^"
if commit.IsFirstCommit() {
hashOrRoot = "--root"
shaOrRoot = "--root"
}
cmdArgs := NewGitCmd("rebase").
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", hashOrRoot).
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", shaOrRoot).
ToArgv()
return self.runSkipEditorCommand(self.cmd.New(cmdArgs))
@@ -386,13 +332,7 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
commits []*models.Commit, commitIndex int, keepCommitsThatBecomeEmpty bool,
) error {
return self.BeginInteractiveRebaseForCommitRange(commits, commitIndex, commitIndex, keepCommitsThatBecomeEmpty)
}
func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange(
commits []*models.Commit, start, end int, keepCommitsThatBecomeEmpty bool,
) error {
if len(commits)-1 < end {
if len(commits)-1 < commitIndex {
return errors.New("index outside of range of commits")
}
@@ -403,17 +343,14 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange(
return errors.New(self.Tr.DisabledForGPG)
}
changes := make([]daemon.ChangeTodoAction, 0, end-start)
for commitIndex := end; commitIndex >= start; commitIndex-- {
changes = append(changes, daemon.ChangeTodoAction{
Hash: commits[commitIndex].Hash,
NewAction: todo.Edit,
})
}
changes := []daemon.ChangeTodoAction{{
Sha: commits[commitIndex].Sha,
NewAction: todo.Edit,
}}
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: getBaseHashOrRoot(commits, end+1),
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
overrideEditor: true,
keepCommitsThatBecomeEmpty: keepCommitsThatBecomeEmpty,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
@@ -422,13 +359,13 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange(
// RebaseBranch interactive rebases onto a branch
func (self *RebaseCommands) RebaseBranch(branchName string) error {
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseHashOrRoot: branchName}).Run()
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseShaOrRoot: branchName}).Run()
}
func (self *RebaseCommands) RebaseBranchFromBaseCommit(targetBranchName string, baseCommit string) error {
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: baseCommit,
onto: targetBranchName,
baseShaOrRoot: baseCommit,
onto: targetBranchName,
}).Run()
}
@@ -517,10 +454,10 @@ func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, comm
return self.ContinueRebase()
}
// CherryPickCommits begins an interactive rebase with the given hashes being cherry picked onto HEAD
// CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD
func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
commitLines := lo.Map(commits, func(commit *models.Commit, _ int) string {
return fmt.Sprintf("%s %s", utils.ShortHash(commit.Hash), commit.Name)
return fmt.Sprintf("%s %s", utils.ShortSha(commit.Sha), commit.Name)
})
msg := utils.ResolvePlaceholderString(
self.Tr.Log.CherryPickCommits,
@@ -531,8 +468,8 @@ func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
self.os.LogCommand(msg, false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: "HEAD",
instruction: daemon.NewCherryPickCommitsInstruction(commits),
baseShaOrRoot: "HEAD",
instruction: daemon.NewCherryPickCommitsInstruction(commits),
}).Run()
}
@@ -552,13 +489,13 @@ func (self *RebaseCommands) CherryPickCommitsDuringRebase(commits []*models.Comm
// we can't start an interactive rebase from the first commit without passing the
// '--root' arg
func getBaseHashOrRoot(commits []*models.Commit, index int) string {
func getBaseShaOrRoot(commits []*models.Commit, index int) string {
// We assume that the commits slice contains the initial commit of the repo.
// Technically this assumption could prove false, but it's unlikely you'll
// be starting a rebase from 300 commits ago (which is the original commit limit
// at time of writing)
if index < len(commits) {
return commits[index].Hash
return commits[index].Sha
} else {
return "--root"
}
+6 -3
View File
@@ -67,6 +67,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildRebaseCommands(commonDeps{runner: s.runner, gitVersion: s.gitVersion})
s.test(instance.RebaseBranch(s.arg))
@@ -88,6 +89,7 @@ func TestRebaseSkipEditorCommand(t *testing.T) {
`^GIT_SEQUENCE_EDITOR=.*$`,
"^" + daemon.DaemonKindEnvKey + "=" + strconv.Itoa(int(daemon.DaemonKindExitImmediately)) + "$",
} {
regexStr := regexStr
foundMatch := lo.ContainsBy(envVars, func(envVar string) bool {
return regexp.MustCompile(regexStr).MatchString(envVar)
})
@@ -129,7 +131,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
{
testName: "returns error when using gpg",
gitConfigMockResponses: map[string]string{"commit.gpgsign": "true"},
commits: []*models.Commit{{Name: "commit", Hash: "123456"}},
commits: []*models.Commit{{Name: "commit", Sha: "123456"}},
commitIndex: 0,
fileName: []string{"test999.txt"},
runner: oscommands.NewFakeRunner(t),
@@ -141,8 +143,8 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
testName: "checks out file if it already existed",
gitConfigMockResponses: nil,
commits: []*models.Commit{
{Name: "commit", Hash: "123456"},
{Name: "commit2", Hash: "abcdef"},
{Name: "commit", Sha: "123456"},
{Name: "commit2", Sha: "abcdef"},
},
commitIndex: 0,
fileName: []string{"test999.txt"},
@@ -161,6 +163,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildRebaseCommands(commonDeps{
runner: s.runner,
@@ -45,7 +45,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
}
// note that the unix timestamp here is the timestamp of the COMMIT, not the reflog entry itself,
// so two consecutive reflog entries may have both the same hash and therefore same timestamp.
// so two consecutive reflog entries may have both the same SHA and therefore same timestamp.
// We use the reflog message to disambiguate, and fingers crossed that we never see the same of those
// twice in a row. Reason being that it would mean we'd be erroneously exiting early.
if lastReflogCommit != nil && self.sameReflogCommit(commit, lastReflogCommit) {
@@ -65,7 +65,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
}
func (self *ReflogCommitLoader) sameReflogCommit(a *models.Commit, b *models.Commit) bool {
return a.Hash == b.Hash && a.UnixTimestamp == b.UnixTimestamp && a.Name == b.Name
return a.Sha == b.Sha && a.UnixTimestamp == b.UnixTimestamp && a.Name == b.Name
}
func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
@@ -83,7 +83,7 @@ func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
}
return &models.Commit{
Hash: fields[0],
Sha: fields[0],
Name: fields[2],
UnixTimestamp: int64(unixTimestamp),
Status: models.StatusReflog,
@@ -50,35 +50,35 @@ func TestGetReflogCommits(t *testing.T) {
lastReflogCommit: nil,
expectedCommits: []*models.Commit{
{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from master to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Hash: "f4ddf2f0d4be4ccc7efa",
Sha: "f4ddf2f0d4be4ccc7efa",
Name: "checkout: moving from A to master",
Status: models.StatusReflog,
UnixTimestamp: 1643149435,
@@ -94,7 +94,7 @@ func TestGetReflogCommits(t *testing.T) {
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -102,7 +102,7 @@ func TestGetReflogCommits(t *testing.T) {
},
expectedCommits: []*models.Commit{
{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -118,7 +118,7 @@ func TestGetReflogCommits(t *testing.T) {
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--follow", "--", "path"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -127,7 +127,7 @@ func TestGetReflogCommits(t *testing.T) {
filterPath: "path",
expectedCommits: []*models.Commit{
{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -143,7 +143,7 @@ func TestGetReflogCommits(t *testing.T) {
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--author=John Doe <john@doe.com>"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -152,7 +152,7 @@ func TestGetReflogCommits(t *testing.T) {
filterAuthor: "John Doe <john@doe.com>",
expectedCommits: []*models.Commit{
{
Hash: "c3c4b66b64c97ffeecde",
Sha: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -176,6 +176,7 @@ func TestGetReflogCommits(t *testing.T) {
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.testName, func(t *testing.T) {
builder := &ReflogCommitLoader{
Common: utils.NewDummyCommon(),
+11 -24
View File
@@ -2,7 +2,6 @@ package git_commands
import (
ioFs "io/fs"
"os"
"path"
"path/filepath"
"strings"
@@ -19,7 +18,6 @@ type RepoPaths struct {
repoPath string
repoGitDirPath string
repoName string
isBareRepo bool
}
var gitPathFormatVersion GitVersion = GitVersion{2, 31, 0, ""}
@@ -56,10 +54,6 @@ func (self *RepoPaths) RepoName() string {
return self.repoName
}
func (self *RepoPaths) IsBareRepo() bool {
return self.isBareRepo
}
// Returns the repo paths for a typical repo
func MockRepoPaths(currentPath string) *RepoPaths {
return &RepoPaths{
@@ -68,7 +62,6 @@ func MockRepoPaths(currentPath string) *RepoPaths {
repoPath: currentPath,
repoGitDirPath: path.Join(currentPath, ".git"),
repoName: "lazygit",
isBareRepo: false,
}
}
@@ -76,19 +69,7 @@ func GetRepoPaths(
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
) (*RepoPaths, error) {
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
return GetRepoPathsForDir(cwd, cmd, version)
}
func GetRepoPathsForDir(
dir string,
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
) (*RepoPaths, error) {
gitDirOutput, err := callGitRevParseWithDir(cmd, version, dir, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree")
gitDirOutput, err := callGitRevParse(cmd, version, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree")
if err != nil {
return nil, err
}
@@ -103,14 +84,13 @@ func GetRepoPathsForDir(
return nil, err
}
}
isBareRepo := gitDirResults[3] == "true"
// If we're in a submodule, --show-superproject-working-tree will return
// a value, meaning gitDirResults will be length 5. In that case
// a value, meaning gitDirResults will be length 4. In that case
// return the worktree path as the repoPath. Otherwise we're in a
// normal repo or a worktree so return the parent of the git common
// dir (repoGitDirPath)
isSubmodule := len(gitDirResults) == 5
isSubmodule := len(gitDirResults) == 4
var repoPath string
if isSubmodule {
@@ -126,10 +106,17 @@ func GetRepoPathsForDir(
repoPath: repoPath,
repoGitDirPath: repoGitDirPath,
repoName: repoName,
isBareRepo: isBareRepo,
}, nil
}
func callGitRevParse(
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
gitRevArgs ...string,
) (string, error) {
return callGitRevParseWithDir(cmd, version, "", gitRevArgs...)
}
func callGitRevParseWithDir(
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
+6 -42
View File
@@ -36,12 +36,10 @@ func TestGetRepoPaths(t *testing.T) {
"/path/to/repo/.git",
// --git-common-dir
"/path/to/repo/.git",
// --is-bare-repository
"false",
// --show-superproject-working-tree
}
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
strings.Join(expectedOutput, "\n"),
nil)
},
@@ -52,38 +50,6 @@ func TestGetRepoPaths(t *testing.T) {
repoPath: "/path/to/repo",
repoGitDirPath: "/path/to/repo/.git",
repoName: "repo",
isBareRepo: false,
},
Err: nil,
},
{
Name: "bare repo",
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
// setup for main worktree
expectedOutput := []string{
// --show-toplevel
"/path/to/repo",
// --git-dir
"/path/to/bare_repo/bare.git",
// --git-common-dir
"/path/to/bare_repo/bare.git",
// --is-bare-repository
"true",
// --show-superproject-working-tree
}
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
strings.Join(expectedOutput, "\n"),
nil)
},
Path: "/path/to/repo",
Expected: &RepoPaths{
worktreePath: "/path/to/repo",
worktreeGitDirPath: "/path/to/bare_repo/bare.git",
repoPath: "/path/to/bare_repo",
repoGitDirPath: "/path/to/bare_repo/bare.git",
repoName: "bare_repo",
isBareRepo: true,
},
Err: nil,
},
@@ -97,13 +63,11 @@ func TestGetRepoPaths(t *testing.T) {
"/path/to/repo/.git/modules/submodule1",
// --git-common-dir
"/path/to/repo/.git/modules/submodule1",
// --is-bare-repository
"false",
// --show-superproject-working-tree
"/path/to/repo",
}
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
strings.Join(expectedOutput, "\n"),
nil)
},
@@ -114,7 +78,6 @@ func TestGetRepoPaths(t *testing.T) {
repoPath: "/path/to/repo/submodule1",
repoGitDirPath: "/path/to/repo/.git/modules/submodule1",
repoName: "submodule1",
isBareRepo: false,
},
Err: nil,
},
@@ -122,7 +85,7 @@ func TestGetRepoPaths(t *testing.T) {
Name: "git rev-parse returns an error",
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
"",
errors.New("fatal: invalid gitfile format: /path/to/repo/worktree2/.git"))
},
@@ -131,13 +94,14 @@ func TestGetRepoPaths(t *testing.T) {
Err: func(getRevParseArgs argFn) error {
args := strings.Join(getRevParseArgs(), " ")
return errors.New(
fmt.Sprintf("'git %v --show-toplevel --absolute-git-dir --git-common-dir --is-bare-repository --show-superproject-working-tree' failed: fatal: invalid gitfile format: /path/to/repo/worktree2/.git", args),
fmt.Sprintf("'git %v --show-toplevel --absolute-git-dir --git-common-dir --show-superproject-working-tree' failed: fatal: invalid gitfile format: /path/to/repo/worktree2/.git", args),
)
},
},
}
for _, s := range scenarios {
s := s
t.Run(s.Name, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t)
cmd := oscommands.NewDummyCmdObjBuilder(runner)
@@ -157,7 +121,7 @@ func TestGetRepoPaths(t *testing.T) {
// prepare the filesystem for the scenario
s.BeforeFunc(runner, getRevParseArgs)
repoPaths, err := GetRepoPathsForDir("", cmd, version)
repoPaths, err := GetRepoPaths(cmd, version)
// check the error and the paths
if s.Err != nil {
+9 -23
View File
@@ -60,24 +60,24 @@ func (self *StashCommands) Push(message string) error {
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Store(hash string, message string) error {
func (self *StashCommands) Store(sha string, message string) error {
trimmedMessage := strings.Trim(message, " \t")
cmdArgs := NewGitCmd("stash").Arg("store").
ArgIf(trimmedMessage != "", "-m", trimmedMessage).
Arg(hash).
Arg(sha).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Hash(index int) (string, error) {
func (self *StashCommands) Sha(index int) (string, error) {
cmdArgs := NewGitCmd("rev-parse").
Arg(fmt.Sprintf("refs/stash@{%d}", index)).
ToArgv()
hash, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
return strings.Trim(hash, "\r\n"), err
sha, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
return strings.Trim(sha, "\r\n"), err
}
func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
@@ -87,7 +87,6 @@ func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
Arg(fmt.Sprintf("--unified=%d", self.AppState.DiffContextSize)).
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
Arg(fmt.Sprintf("stash@{%d}", index)).
Dir(self.repoPaths.worktreePath).
ToArgv()
@@ -122,22 +121,9 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
return nil
}
// SaveStagedChanges stashes only the currently staged changes.
// SaveStagedChanges stashes only the currently staged changes. This takes a few steps
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
func (self *StashCommands) SaveStagedChanges(message string) error {
if self.version.IsAtLeast(2, 35, 0) {
return self.cmd.New(NewGitCmd("stash").Arg("push").Arg("--staged").Arg("-m", message).ToArgv()).Run()
}
// Git versions older than 2.35.0 don't support the --staged flag, so we
// need to fall back to a more complex solution.
// Shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
//
// Note that this method has a few bugs:
// - it fails when there are *only* staged changes
// - it fails when staged and unstaged changes within a single file are too close together
// We don't bother fixing these, because users can simply update git when
// they are affected by these issues.
// wrap in 'writing', which uses a mutex
if err := self.cmd.New(
NewGitCmd("stash").Arg("--keep-index").ToArgv(),
@@ -193,7 +179,7 @@ func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
}
func (self *StashCommands) Rename(index int, message string) error {
hash, err := self.Hash(index)
sha, err := self.Sha(index)
if err != nil {
return err
}
@@ -202,7 +188,7 @@ func (self *StashCommands) Rename(index int, message string) error {
return err
}
err = self.Store(hash, message)
err = self.Store(sha, message)
if err != nil {
return err
}
@@ -47,6 +47,7 @@ func TestGetStashEntries(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)
+38 -48
View File
@@ -47,7 +47,7 @@ func TestStashSave(t *testing.T) {
func TestStashStore(t *testing.T) {
type scenario struct {
testName string
hash string
sha string
message string
expected []string
}
@@ -55,99 +55,88 @@ func TestStashStore(t *testing.T) {
scenarios := []scenario{
{
testName: "Non-empty message",
hash: "0123456789abcdef",
sha: "0123456789abcdef",
message: "New stash name",
expected: []string{"stash", "store", "-m", "New stash name", "0123456789abcdef"},
},
{
testName: "Empty message",
hash: "0123456789abcdef",
sha: "0123456789abcdef",
message: "",
expected: []string{"stash", "store", "0123456789abcdef"},
},
{
testName: "Space message",
hash: "0123456789abcdef",
sha: "0123456789abcdef",
message: " ",
expected: []string{"stash", "store", "0123456789abcdef"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs(s.expected, "", nil)
instance := buildStashCommands(commonDeps{runner: runner})
assert.NoError(t, instance.Store(s.hash, s.message))
assert.NoError(t, instance.Store(s.sha, s.message))
runner.CheckForMissingCalls()
})
}
}
func TestStashHash(t *testing.T) {
func TestStashSha(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"rev-parse", "refs/stash@{5}"}, "14d94495194651adfd5f070590df566c11d28243\n", nil)
instance := buildStashCommands(commonDeps{runner: runner})
hash, err := instance.Hash(5)
sha, err := instance.Sha(5)
assert.NoError(t, err)
assert.Equal(t, "14d94495194651adfd5f070590df566c11d28243", hash)
assert.Equal(t, "14d94495194651adfd5f070590df566c11d28243", sha)
runner.CheckForMissingCalls()
}
func TestStashStashEntryCmdObj(t *testing.T) {
type scenario struct {
testName string
index int
contextSize int
similarityThreshold int
ignoreWhitespace bool
expected []string
testName string
index int
contextSize int
ignoreWhitespace bool
expected []string
}
scenarios := []scenario{
{
testName: "Default case",
index: 5,
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--find-renames=50%", "stash@{5}"},
testName: "Default case",
index: 5,
contextSize: 3,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "stash@{5}"},
},
{
testName: "Show diff with custom context size",
index: 5,
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "--find-renames=50%", "stash@{5}"},
testName: "Show diff with custom context size",
index: 5,
contextSize: 77,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "stash@{5}"},
},
{
testName: "Show diff with custom similarity threshold",
index: 5,
contextSize: 3,
similarityThreshold: 33,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--find-renames=33%", "stash@{5}"},
},
{
testName: "Default case",
index: 5,
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: true,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "--find-renames=50%", "stash@{5}"},
testName: "Default case",
index: 5,
contextSize: 3,
ignoreWhitespace: true,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "stash@{5}"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
appState := &config.AppState{}
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
appState.DiffContextSize = s.contextSize
appState.RenameSimilarityThreshold = s.similarityThreshold
repoPaths := RepoPaths{
worktreePath: "/path/to/worktree",
}
@@ -164,8 +153,8 @@ func TestStashRename(t *testing.T) {
testName string
index int
message string
expectedHashCmd []string
hashResult string
expectedShaCmd []string
shaResult string
expectedDropCmd []string
expectedStoreCmd []string
}
@@ -175,8 +164,8 @@ func TestStashRename(t *testing.T) {
testName: "Default case",
index: 3,
message: "New message",
expectedHashCmd: []string{"rev-parse", "refs/stash@{3}"},
hashResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedShaCmd: []string{"rev-parse", "refs/stash@{3}"},
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedDropCmd: []string{"stash", "drop", "stash@{3}"},
expectedStoreCmd: []string{"stash", "store", "-m", "New message", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd"},
},
@@ -184,17 +173,18 @@ func TestStashRename(t *testing.T) {
testName: "Empty message",
index: 4,
message: "",
expectedHashCmd: []string{"rev-parse", "refs/stash@{4}"},
hashResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedShaCmd: []string{"rev-parse", "refs/stash@{4}"},
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedDropCmd: []string{"stash", "drop", "stash@{4}"},
expectedStoreCmd: []string{"stash", "store", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs(s.expectedHashCmd, s.hashResult, nil).
ExpectGitArgs(s.expectedShaCmd, s.shaResult, nil).
ExpectGitArgs(s.expectedDropCmd, "", nil).
ExpectGitArgs(s.expectedStoreCmd, "", nil)
instance := buildStashCommands(commonDeps{runner: runner})
+16 -2
View File
@@ -3,8 +3,10 @@ package git_commands
import (
"os"
"path/filepath"
"strconv"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
)
@@ -47,8 +49,20 @@ func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
return enums.REBASE_MODE_NONE
}
func (self *StatusCommands) IsBareRepo() bool {
return self.repoPaths.isBareRepo
func (self *StatusCommands) IsBareRepo() (bool, error) {
return IsBareRepo(self.os)
}
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
res, err := osCommand.Cmd.New(
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return false, err
}
// The command returns output with a newline, so we need to strip
return strconv.ParseBool(strings.TrimSpace(res))
}
func (self *StatusCommands) IsInNormalRebase() (bool, error) {
+2 -4
View File
@@ -19,7 +19,6 @@ func NewSyncCommands(gitCommon *GitCommon) *SyncCommands {
// Push pushes to a branch
type PushOpts struct {
Force bool
ForceWithLease bool
UpstreamRemote string
UpstreamBranch string
SetUpstream bool
@@ -31,11 +30,10 @@ func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (oscommands
}
cmdArgs := NewGitCmd("push").
ArgIf(opts.Force, "--force").
ArgIf(opts.ForceWithLease, "--force-with-lease").
ArgIf(opts.Force, "--force-with-lease").
ArgIf(opts.SetUpstream, "--set-upstream").
ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote).
ArgIf(opts.UpstreamBranch != "", "HEAD:"+opts.UpstreamBranch).
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
ToArgv()
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task)
+13 -18
View File
@@ -18,70 +18,62 @@ func TestSyncPush(t *testing.T) {
scenarios := []scenario{
{
testName: "Push with force disabled",
opts: PushOpts{ForceWithLease: false},
opts: PushOpts{Force: false},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push"})
assert.NoError(t, err)
},
},
{
testName: "Push with force-with-lease enabled",
opts: PushOpts{ForceWithLease: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.NoError(t, err)
},
},
{
testName: "Push with force enabled",
opts: PushOpts{Force: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.NoError(t, err)
},
},
{
testName: "Push with force disabled, upstream supplied",
opts: PushOpts{
ForceWithLease: false,
Force: false,
UpstreamRemote: "origin",
UpstreamBranch: "master",
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "HEAD:master"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "master"})
assert.NoError(t, err)
},
},
{
testName: "Push with force disabled, setting upstream",
opts: PushOpts{
ForceWithLease: false,
Force: false,
UpstreamRemote: "origin",
UpstreamBranch: "master",
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "HEAD:master"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "master"})
assert.NoError(t, err)
},
},
{
testName: "Push with force-with-lease enabled, setting upstream",
testName: "Push with force enabled, setting upstream",
opts: PushOpts{
ForceWithLease: true,
Force: true,
UpstreamRemote: "origin",
UpstreamBranch: "master",
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "HEAD:master"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"})
assert.NoError(t, err)
},
},
{
testName: "Push with remote branch but no origin",
opts: PushOpts{
ForceWithLease: true,
Force: true,
UpstreamRemote: "",
UpstreamBranch: "master",
SetUpstream: true,
@@ -94,6 +86,7 @@ func TestSyncPush(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildSyncCommands(commonDeps{})
task := gocui.NewFakeTask()
@@ -131,6 +124,7 @@ func TestSyncFetch(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildSyncCommands(commonDeps{})
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
@@ -169,6 +163,7 @@ func TestSyncFetchBackground(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildSyncCommands(commonDeps{})
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
@@ -44,6 +44,7 @@ func TestGetTags(t *testing.T) {
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.testName, func(t *testing.T) {
loader := &TagLoader{
Common: utils.NewDummyCommon(),
+4 -7
View File
@@ -3,7 +3,6 @@ package git_commands
import (
"fmt"
"os"
"path"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@@ -233,8 +232,7 @@ func (self *WorkingTreeCommands) Ignore(filename string) error {
// Exclude adds a file to the .git/info/exclude for the repo
func (self *WorkingTreeCommands) Exclude(filename string) error {
excludeFile := path.Join(self.repoPaths.repoGitDirPath, "info", "exclude")
return self.os.AppendLineToFile(excludeFile, filename)
return self.os.AppendLineToFile(".git/info/exclude", filename)
}
// WorktreeFileDiff returns the diff of a file
@@ -263,7 +261,6 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
Arg(fmt.Sprintf("--unified=%d", contextSize)).
Arg(fmt.Sprintf("--color=%s", colorArg)).
ArgIf(!plain && self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
ArgIf(cached, "--cached").
ArgIf(noIndex, "--no-index").
Arg("--").
@@ -314,8 +311,8 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
}
// CheckoutFile checks out the file for the given commit
func (self *WorkingTreeCommands) CheckoutFile(commitHash, fileName string) error {
cmdArgs := NewGitCmd("checkout").Arg(commitHash, "--", fileName).
func (self *WorkingTreeCommands) CheckoutFile(commitSha, fileName string) error {
cmdArgs := NewGitCmd("checkout").Arg(commitSha, "--", fileName).
ToArgv()
return self.cmd.New(cmdArgs).Run()
@@ -364,7 +361,7 @@ func (self *WorkingTreeCommands) ResetAndClean() error {
return self.RemoveUntrackedFiles()
}
// ResetHard runs `git reset --hard`
// ResetHardHead runs `git reset --hard`
func (self *WorkingTreeCommands) ResetHard(ref string) error {
cmdArgs := NewGitCmd("reset").Arg("--hard", ref).
ToArgv()
+58 -72
View File
@@ -61,6 +61,7 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.UnStageFile([]string{"test.txt"}, s.reset))
@@ -189,6 +190,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, removeFile: s.removeFile})
err := instance.DiscardAllFileChanges(s.file)
@@ -205,14 +207,13 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
func TestWorkingTreeDiff(t *testing.T) {
type scenario struct {
testName string
file *models.File
plain bool
cached bool
ignoreWhitespace bool
contextSize int
similarityThreshold int
runner *oscommands.FakeCmdObjRunner
testName string
file *models.File
plain bool
cached bool
ignoreWhitespace bool
contextSize int
runner *oscommands.FakeCmdObjRunner
}
const expectedResult = "pretend this is an actual git diff"
@@ -225,13 +226,12 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "cached",
@@ -240,13 +240,12 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: true,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
plain: false,
cached: true,
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--cached", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "plain",
@@ -255,13 +254,12 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: true,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
plain: true,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "File not tracked and file has no staged changes",
@@ -270,13 +268,12 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: false,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
},
{
testName: "Default case (ignore whitespace)",
@@ -285,13 +282,12 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: true,
contextSize: 3,
similarityThreshold: 50,
plain: false,
cached: false,
ignoreWhitespace: true,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom context size",
@@ -300,38 +296,22 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 17,
similarityThreshold: 50,
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 17,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom similarity threshold",
file: &models.File{
Name: "test.txt",
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 33,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=33%", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
appState := &config.AppState{}
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
appState.DiffContextSize = s.contextSize
appState.RenameSimilarityThreshold = s.similarityThreshold
repoPaths := RepoPaths{
worktreePath: "/path/to/worktree",
}
@@ -395,6 +375,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
appState := &config.AppState{}
@@ -416,18 +397,18 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
func TestWorkingTreeCheckoutFile(t *testing.T) {
type scenario struct {
testName string
commitHash string
fileName string
runner *oscommands.FakeCmdObjRunner
test func(error)
testName string
commitSha string
fileName string
runner *oscommands.FakeCmdObjRunner
test func(error)
}
scenarios := []scenario{
{
testName: "typical case",
commitHash: "11af912",
fileName: "test999.txt",
testName: "typical case",
commitSha: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", nil),
test: func(err error) {
@@ -435,9 +416,9 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
},
},
{
testName: "returns error if there is one",
commitHash: "11af912",
fileName: "test999.txt",
testName: "returns error if there is one",
commitSha: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
test: func(err error) {
@@ -447,10 +428,11 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.CheckoutFile(s.commitHash, s.fileName))
s.test(instance.CheckoutFile(s.commitSha, s.fileName))
s.runner.CheckForMissingCalls()
})
}
@@ -477,6 +459,7 @@ func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.DiscardUnstagedFileChanges(s.file))
@@ -504,6 +487,7 @@ func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.DiscardAnyUnstagedFileChanges())
@@ -531,6 +515,7 @@ func TestWorkingTreeRemoveUntrackedFiles(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.RemoveUntrackedFiles())
@@ -560,6 +545,7 @@ func TestWorkingTreeResetHard(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.ResetHard(s.ref))
@@ -76,6 +76,8 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
wg := sync.WaitGroup{}
wg.Add(len(worktrees))
for _, worktree := range worktrees {
worktree := worktree
go utils.Safe(func() {
defer wg.Done()
@@ -181,6 +181,7 @@ branch refs/heads/mybranch-worktree
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t)
fs := afero.NewMemMapFs()
@@ -50,6 +50,7 @@ func TestGetBool(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
fake := NewFakeGitConfig(s.mockResponses)
real := NewCachedGitConfig(
@@ -86,6 +87,7 @@ func TestGet(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
fake := NewFakeGitConfig(s.mockResponses)
real := NewCachedGitConfig(
+6 -6
View File
@@ -14,7 +14,7 @@ var githubServiceDef = ServiceDefinition{
provider: "github",
pullRequestURLIntoDefaultBranch: "/compare/{{.From}}?expand=1",
pullRequestURLIntoTargetBranch: "/compare/{{.To}}...{{.From}}?expand=1",
commitURL: "/commit/{{.CommitHash}}",
commitURL: "/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
}
@@ -23,7 +23,7 @@ var bitbucketServiceDef = ServiceDefinition{
provider: "bitbucket",
pullRequestURLIntoDefaultBranch: "/pull-requests/new?source={{.From}}&t=1",
pullRequestURLIntoTargetBranch: "/pull-requests/new?source={{.From}}&dest={{.To}}&t=1",
commitURL: "/commits/{{.CommitHash}}",
commitURL: "/commits/{{.CommitSha}}",
regexStrings: []string{
`^(?:https?|ssh)://.*/(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
`^.*@.*:(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
@@ -35,7 +35,7 @@ var gitLabServiceDef = ServiceDefinition{
provider: "gitlab",
pullRequestURLIntoDefaultBranch: "/-/merge_requests/new?merge_request[source_branch]={{.From}}",
pullRequestURLIntoTargetBranch: "/-/merge_requests/new?merge_request[source_branch]={{.From}}&merge_request[target_branch]={{.To}}",
commitURL: "/-/commit/{{.CommitHash}}",
commitURL: "/-/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
}
@@ -44,7 +44,7 @@ var azdoServiceDef = ServiceDefinition{
provider: "azuredevops",
pullRequestURLIntoDefaultBranch: "/pullrequestcreate?sourceRef={{.From}}",
pullRequestURLIntoTargetBranch: "/pullrequestcreate?sourceRef={{.From}}&targetRef={{.To}}",
commitURL: "/commit/{{.CommitHash}}",
commitURL: "/commit/{{.CommitSha}}",
regexStrings: []string{
`^git@ssh.dev.azure.com.*/(?P<org>.*)/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
`^https://.*@dev.azure.com/(?P<org>.*?)/(?P<project>.*?)/_git/(?P<repo>.*?)(?:\.git)?$`,
@@ -56,7 +56,7 @@ var bitbucketServerServiceDef = ServiceDefinition{
provider: "bitbucketServer",
pullRequestURLIntoDefaultBranch: "/pull-requests?create&sourceBranch={{.From}}",
pullRequestURLIntoTargetBranch: "/pull-requests?create&targetBranch={{.To}}&sourceBranch={{.From}}",
commitURL: "/commits/{{.CommitHash}}",
commitURL: "/commits/{{.CommitSha}}",
regexStrings: []string{
`^ssh://git@.*/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
`^https://.*/scm/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
@@ -68,7 +68,7 @@ var giteaServiceDef = ServiceDefinition{
provider: "gitea",
pullRequestURLIntoDefaultBranch: "/compare/{{.From}}",
pullRequestURLIntoTargetBranch: "/compare/{{.To}}...{{.From}}",
commitURL: "/commit/{{.CommitHash}}",
commitURL: "/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
}
@@ -51,13 +51,13 @@ func (self *HostingServiceMgr) GetPullRequestURL(from string, to string) (string
}
}
func (self *HostingServiceMgr) GetCommitURL(commitHash string) (string, error) {
func (self *HostingServiceMgr) GetCommitURL(commitSha string) (string, error) {
gitService, err := self.getService()
if err != nil {
return "", err
}
pullRequestURL := gitService.getCommitURL(commitHash)
pullRequestURL := gitService.getCommitURL(commitSha)
return pullRequestURL, nil
}
@@ -174,8 +174,8 @@ func (self *Service) getPullRequestURLIntoTargetBranch(from string, to string) s
return self.resolveUrl(self.pullRequestURLIntoTargetBranch, map[string]string{"From": from, "To": to})
}
func (self *Service) getCommitURL(commitHash string) string {
return self.resolveUrl(self.commitURL, map[string]string{"CommitHash": commitHash})
func (self *Service) getCommitURL(commitSha string) string {
return self.resolveUrl(self.commitURL, map[string]string{"CommitSha": commitSha})
}
func (self *Service) resolveUrl(templateString string, args map[string]string) string {
@@ -413,10 +413,11 @@ func TestGetPullRequestURL(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
tr := i18n.EnglishTranslationSet()
log := &fakes.FakeFieldLogger{}
hostingServiceMgr := NewHostingServiceMgr(log, tr, s.remoteUrl, s.configServiceDomains)
hostingServiceMgr := NewHostingServiceMgr(log, &tr, s.remoteUrl, s.configServiceDomains)
s.test(hostingServiceMgr.GetPullRequestURL(s.from, s.to))
log.AssertErrors(t, s.expectedLoggedErrors)
})
+12 -28
View File
@@ -1,9 +1,6 @@
package models
import (
"fmt"
"sync/atomic"
)
import "fmt"
// Branch : A git branch
// duplicating this for now
@@ -13,14 +10,10 @@ type Branch struct {
DisplayName string
// indicator of when the branch was last checked out e.g. '2d', '3m'
Recency string
// how many commits ahead we are from the remote branch (how many commits we can push, assuming we push to our tracked remote branch)
AheadForPull string
// how many commits ahead we are from the remote branch (how many commits we can push)
Pushables string
// how many commits behind we are from the remote branch (how many commits we can pull)
BehindForPull string
// how many commits ahead we are from the branch we're pushing to (which might not be the same as our upstream branch in a triangular workflow)
AheadForPush string
// how many commits behind we are from the branch we're pushing to (which might not be the same as our upstream branch in a triangular workflow)
BehindForPush string
Pullables string
// whether the remote branch is 'gone' i.e. we're tracking a remote branch that has been deleted
UpstreamGone bool
// whether this is the current branch. Exactly one branch should have this be true
@@ -35,11 +28,6 @@ type Branch struct {
Subject string
// commit hash
CommitHash string
// How far we have fallen behind our base branch. 0 means either not
// determined yet, or up to date with base branch. (We don't need to
// distinguish the two, as we don't draw anything in both cases.)
BehindBaseBranch atomic.Int32
}
func (b *Branch) FullRefName() string {
@@ -92,30 +80,26 @@ func (b *Branch) IsTrackingRemote() bool {
// we know that the remote branch is not stored locally based on our pushable/pullable
// count being question marks.
func (b *Branch) RemoteBranchStoredLocally() bool {
return b.IsTrackingRemote() && b.AheadForPull != "?" && b.BehindForPull != "?"
return b.IsTrackingRemote() && b.Pushables != "?" && b.Pullables != "?"
}
func (b *Branch) RemoteBranchNotStoredLocally() bool {
return b.IsTrackingRemote() && b.AheadForPull == "?" && b.BehindForPull == "?"
return b.IsTrackingRemote() && b.Pushables == "?" && b.Pullables == "?"
}
func (b *Branch) MatchesUpstream() bool {
return b.RemoteBranchStoredLocally() && b.AheadForPull == "0" && b.BehindForPull == "0"
return b.RemoteBranchStoredLocally() && b.Pushables == "0" && b.Pullables == "0"
}
func (b *Branch) IsAheadForPull() bool {
return b.RemoteBranchStoredLocally() && b.AheadForPull != "0"
func (b *Branch) HasCommitsToPush() bool {
return b.RemoteBranchStoredLocally() && b.Pushables != "0"
}
func (b *Branch) IsBehindForPull() bool {
return b.RemoteBranchStoredLocally() && b.BehindForPull != "0"
}
func (b *Branch) IsBehindForPush() bool {
return b.RemoteBranchStoredLocally() && b.BehindForPush != "0"
func (b *Branch) HasCommitsToPull() bool {
return b.RemoteBranchStoredLocally() && b.Pullables != "0"
}
// for when we're in a detached head state
func (b *Branch) IsRealBranch() bool {
return b.AheadForPull != "" && b.BehindForPull != ""
return b.Pushables != "" && b.Pullables != ""
}
+8 -8
View File
@@ -3,8 +3,8 @@ package models
import (
"fmt"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stefanhaller/git-todo-parser/todo"
)
// Special commit hash for empty tree object
@@ -43,7 +43,7 @@ const (
// Commit : A git commit
type Commit struct {
Hash string
Sha string
Name string
Status CommitStatus
Action todo.TodoCommand
@@ -54,20 +54,20 @@ type Commit struct {
UnixTimestamp int64
Divergence Divergence // set to DivergenceNone unless we are showing the divergence view
// Hashes of parent commits (will be multiple if it's a merge commit)
// SHAs of parent commits (will be multiple if it's a merge commit)
Parents []string
}
func (c *Commit) ShortHash() string {
return utils.ShortHash(c.Hash)
func (c *Commit) ShortSha() string {
return utils.ShortSha(c.Sha)
}
func (c *Commit) FullRefName() string {
return c.Hash
return c.Sha
}
func (c *Commit) RefName() string {
return c.Hash
return c.Sha
}
func (c *Commit) ParentRefName() string {
@@ -86,7 +86,7 @@ func (c *Commit) ID() string {
}
func (c *Commit) Description() string {
return fmt.Sprintf("%s %s", c.Hash[:7], c.Name)
return fmt.Sprintf("%s %s", c.Sha[:7], c.Name)
}
func (c *Commit) IsMerge() bool {
+4 -8
View File
@@ -161,7 +161,7 @@ func (self *cmdObjRunner) RunAndProcessLines(cmdObj ICmdObj, onLine func(line st
}
scanner := bufio.NewScanner(stdoutPipe)
scanner.Split(utils.ScanLinesAndTruncateWhenLongerThanBuffer(bufio.MaxScanTokenSize))
scanner.Split(bufio.ScanLines)
if err := cmd.Start(); err != nil {
return err
}
@@ -178,11 +178,6 @@ func (self *cmdObjRunner) RunAndProcessLines(cmdObj ICmdObj, onLine func(line st
}
}
if scanner.Err() != nil {
_ = Kill(cmd)
return scanner.Err()
}
_ = cmd.Wait()
self.log.Infof("%s (%s)", cmdObj.ToString(), time.Since(t))
@@ -225,6 +220,9 @@ func (self *cmdObjRunner) runAndStreamAux(
cmdObj ICmdObj,
onRun func(*cmdHandler, io.Writer),
) error {
// if we're streaming this we don't want any fancy terminal stuff
cmdObj.AddEnvVars("TERM=dumb")
cmdWriter := self.guiIO.newCmdWriterFn()
if cmdObj.ShouldLog() {
@@ -284,7 +282,6 @@ const (
Username
Passphrase
PIN
Token
)
// Whenever we're asked for a password we just enter a newline, which will
@@ -377,7 +374,6 @@ func (self *cmdObjRunner) getCheckForCredentialRequestFunc() func([]byte) (Crede
`Username\s*for\s*'.+':`: Username,
`Enter\s*passphrase\s*for\s*key\s*'.+':`: Passphrase,
`Enter\s*PIN\s*for\s*.+\s*key\s*.+:`: PIN,
`.*2FA Token.*`: Token,
}
compiledPrompts := map[*regexp.Regexp]CredentialType{}
@@ -39,8 +39,6 @@ func TestProcessOutput(t *testing.T) {
return "passphrase"
case PIN:
return "pin"
case Token:
return "token"
default:
panic("unexpected credential type")
}
@@ -94,12 +92,6 @@ func TestProcessOutput(t *testing.T) {
output: "Enter PIN for key '123':",
expectedToWrite: "pin",
},
{
name: "2FA token prompt",
promptUserForCredential: defaultPromptUserForCredential,
output: "testuser 2FA Token (citadel)",
expectedToWrite: "token",
},
{
name: "username and password prompt",
promptUserForCredential: defaultPromptUserForCredential,
+2 -2
View File
@@ -1,7 +1,7 @@
package oscommands
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
@@ -86,7 +86,7 @@ func CopyDir(src string, dst string) (err error) {
return err
}
if !si.IsDir() {
return errors.New("source is not a directory")
return fmt.Errorf("source is not a directory")
}
_, err = os.Stat(dst)
+4 -20
View File
@@ -239,13 +239,14 @@ func (c *OSCommand) PipeCommands(cmdObjs ...ICmdObj) error {
wg.Add(len(cmds))
for _, cmd := range cmds {
currentCmd := cmd
go utils.Safe(func() {
stderr, err := cmd.StderrPipe()
stderr, err := currentCmd.StderrPipe()
if err != nil {
c.Log.Error(err)
}
if err := cmd.Start(); err != nil {
if err := currentCmd.Start(); err != nil {
c.Log.Error(err)
}
@@ -255,7 +256,7 @@ func (c *OSCommand) PipeCommands(cmdObjs ...ICmdObj) error {
}
}
if err := cmd.Wait(); err != nil {
if err := currentCmd.Wait(); err != nil {
c.Log.Error(err)
}
@@ -302,23 +303,6 @@ func (c *OSCommand) CopyToClipboard(str string) error {
return clipboard.WriteAll(str)
}
func (c *OSCommand) PasteFromClipboard() (string, error) {
var s string
var err error
if c.UserConfig.OS.CopyToClipboardCmd != "" {
cmdStr := c.UserConfig.OS.ReadFromClipboardCmd
s, err = c.Cmd.NewShell(cmdStr).RunWithOutput()
} else {
s, err = clipboard.ReadAll()
}
if err != nil {
return "", err
}
return strings.ReplaceAll(s, "\r\n", "\n"), nil
}
func (c *OSCommand) RemoveFile(path string) error {
msg := utils.ResolvePlaceholderString(
c.Tr.Log.RemoveFile,
+11 -29
View File
@@ -33,7 +33,7 @@ type (
// PatchBuilder manages the building of a patch for a commit to be applied to another commit (or the working tree, or removed from the current commit). We also support building patches from things like stashes, for which there is less flexibility
type PatchBuilder struct {
// To is the commit hash if we're dealing with files of a commit, or a stash ref for a stash
// To is the commit sha if we're dealing with files of a commit, or a stash ref for a stash
To string
From string
reverse bool
@@ -46,7 +46,7 @@ type PatchBuilder struct {
fileInfoMap map[string]*fileInfo
Log *logrus.Entry
// loadFileDiff loads the diff of a file, for a given to (typically a commit hash)
// loadFileDiff loads the diff of a file, for a given to (typically a commit SHA)
loadFileDiff loadFileDiffFunc
}
@@ -65,7 +65,7 @@ func (p *PatchBuilder) Start(from, to string, reverse bool, canRebase bool) {
p.fileInfoMap = map[string]*fileInfo{}
}
func (p *PatchBuilder) PatchToApply(reverse bool, turnAddedFilesIntoDiffAgainstEmptyFile bool) string {
func (p *PatchBuilder) PatchToApply(reverse bool) string {
patch := ""
for filename, info := range p.fileInfoMap {
@@ -73,12 +73,7 @@ func (p *PatchBuilder) PatchToApply(reverse bool, turnAddedFilesIntoDiffAgainstE
continue
}
patch += p.RenderPatchForFile(RenderPatchForFileOpts{
Filename: filename,
Plain: true,
Reverse: reverse,
TurnAddedFilesIntoDiffAgainstEmptyFile: turnAddedFilesIntoDiffAgainstEmptyFile,
})
patch += p.RenderPatchForFile(filename, true, reverse)
}
return patch
@@ -177,15 +172,8 @@ func (p *PatchBuilder) RemoveFileLineRange(filename string, firstLineIdx, lastLi
return nil
}
type RenderPatchForFileOpts struct {
Filename string
Plain bool
Reverse bool
TurnAddedFilesIntoDiffAgainstEmptyFile bool
}
func (p *PatchBuilder) RenderPatchForFile(opts RenderPatchForFileOpts) string {
info, err := p.getFileInfo(opts.Filename)
func (p *PatchBuilder) RenderPatchForFile(filename string, plain bool, reverse bool) string {
info, err := p.getFileInfo(filename)
if err != nil {
p.Log.Error(err)
return ""
@@ -195,7 +183,7 @@ func (p *PatchBuilder) RenderPatchForFile(opts RenderPatchForFileOpts) string {
return ""
}
if info.mode == WHOLE && opts.Plain {
if info.mode == WHOLE && plain {
// Use the whole diff (spares us parsing it and then formatting it).
// TODO: see if this is actually noticeably faster.
// The reverse flag is only for part patches so we're ignoring it here.
@@ -204,12 +192,11 @@ func (p *PatchBuilder) RenderPatchForFile(opts RenderPatchForFileOpts) string {
patch := Parse(info.diff).
Transform(TransformOpts{
Reverse: opts.Reverse,
TurnAddedFilesIntoDiffAgainstEmptyFile: opts.TurnAddedFilesIntoDiffAgainstEmptyFile,
IncludedLineIndices: info.includedLineIndices,
Reverse: reverse,
IncludedLineIndices: info.includedLineIndices,
})
if opts.Plain {
if plain {
return patch.FormatPlain()
} else {
return patch.FormatView(FormatViewOpts{})
@@ -222,12 +209,7 @@ func (p *PatchBuilder) renderEachFilePatch(plain bool) []string {
sort.Strings(filenames)
patches := lo.Map(filenames, func(filename string, _ int) string {
return p.RenderPatchForFile(RenderPatchForFileOpts{
Filename: filename,
Plain: plain,
Reverse: false,
TurnAddedFilesIntoDiffAgainstEmptyFile: true,
})
return p.RenderPatchForFile(filename, plain, false)
})
output := lo.Filter(patches, func(patch string, _ int) bool {
return patch != ""
+4
View File
@@ -509,6 +509,7 @@ func TestTransform(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
lineIndices := ExpandRange(s.firstLineIndex, s.lastLineIndex)
@@ -565,6 +566,7 @@ func TestParseAndFormatPlain(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
// here we parse the patch, then format it, and ensure the result
// matches the original patch. Note that unified diffs allow omitting
@@ -602,6 +604,7 @@ func TestLineNumberOfLine(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
for i, idx := range s.indexes {
patch := Parse(s.patchStr)
@@ -630,6 +633,7 @@ func TestGetNextStageableLineIndex(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
for i, idx := range s.indexes {
patch := Parse(s.patchStr)
+1 -24
View File
@@ -1,10 +1,6 @@
package patch
import (
"strings"
"github.com/samber/lo"
)
import "github.com/samber/lo"
type patchTransformer struct {
patch *Patch
@@ -26,13 +22,6 @@ type TransformOpts struct {
// information it needs to cleanly apply patches
FileNameOverride string
// Custom patches tend to work better when treating new files as diffs
// against an empty file. The only case where we need this to be false is
// when moving a custom patch to an earlier commit; in that case the patch
// command would fail with the error "file does not exist in index" if we
// treat it as a diff against an empty file.
TurnAddedFilesIntoDiffAgainstEmptyFile bool
// The indices of lines that should be included in the patch.
IncludedLineIndices []int
}
@@ -72,18 +61,6 @@ func (self *patchTransformer) transformHeader() []string {
"--- a/" + self.opts.FileNameOverride,
"+++ b/" + self.opts.FileNameOverride,
}
} else if self.opts.TurnAddedFilesIntoDiffAgainstEmptyFile {
result := make([]string, 0, len(self.patch.header))
for idx, line := range self.patch.header {
if strings.HasPrefix(line, "new file mode") {
continue
}
if line == "--- /dev/null" && strings.HasPrefix(self.patch.header[idx+1], "+++ b/") {
line = "--- a/" + self.patch.header[idx+1][6:]
}
result = append(result, line)
}
return result
} else {
return self.patch.header
}
+9 -33
View File
@@ -8,7 +8,7 @@ import (
"github.com/adrg/xdg"
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
"gopkg.in/yaml.v3"
yaml "github.com/jesseduffield/yaml"
)
// AppConfig contains the base configuration fields required for lazygit.
@@ -164,10 +164,6 @@ func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error)
if err := yaml.Unmarshal(content, base); err != nil {
return nil, fmt.Errorf("The config at `%s` couldn't be parsed, please inspect it before opening up an issue.\n%w", path, err)
}
if err := base.Validate(); err != nil {
return nil, fmt.Errorf("The config at `%s` has a validation error.\n%w", path, err)
}
}
return base, nil
@@ -184,11 +180,6 @@ func migrateUserConfig(path string, content []byte) ([]byte, error) {
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
}
changedContent, err = changeNullKeybindingsToDisabled(changedContent)
if err != nil {
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
}
// Add more migrations here...
// Write config back if changed
@@ -202,17 +193,6 @@ func migrateUserConfig(path string, content []byte) ([]byte, error) {
return content, nil
}
func changeNullKeybindingsToDisabled(changedContent []byte) ([]byte, error) {
return yaml_utils.Walk(changedContent, func(node *yaml.Node, path string) bool {
if strings.HasPrefix(path, "keybinding.") && node.Kind == yaml.ScalarNode && node.Tag == "!!null" {
node.Value = "<disabled>"
node.Tag = "!!str"
return true
}
return false
})
}
func (c *AppConfig) GetDebug() bool {
return c.Debug
}
@@ -363,14 +343,12 @@ type AppState struct {
LastUpdateCheck int64
RecentRepos []string
StartupPopupVersion int
LastVersion string // this is the last version the user was using, for the purpose of showing release notes
// these are for custom commands typed in directly, not for custom commands in the lazygit config
CustomCommandsHistory []string
HideCommandLog bool
IgnoreWhitespaceInDiffView bool
DiffContextSize int
RenameSimilarityThreshold int
LocalBranchSortOrder string
RemoteBranchSortOrder string
@@ -386,16 +364,14 @@ type AppState struct {
func getDefaultAppState() *AppState {
return &AppState{
LastUpdateCheck: 0,
RecentRepos: []string{},
StartupPopupVersion: 0,
LastVersion: "",
DiffContextSize: 3,
RenameSimilarityThreshold: 50,
LocalBranchSortOrder: "recency",
RemoteBranchSortOrder: "alphabetical",
GitLogOrder: "", // should be "topo-order" eventually
GitLogShowGraph: "", // should be "always" eventually
LastUpdateCheck: 0,
RecentRepos: []string{},
StartupPopupVersion: 0,
DiffContextSize: 3,
LocalBranchSortOrder: "recency",
RemoteBranchSortOrder: "alphabetical",
GitLogOrder: "", // should be "topo-order" eventually
GitLogShowGraph: "", // should be "always" eventually
}
}
+9 -2
View File
@@ -12,9 +12,16 @@ func isWSL() bool {
func isContainer() bool {
data, err := os.ReadFile("/proc/1/cgroup")
return err == nil && (strings.Contains(string(data), "docker") ||
if strings.Contains(string(data), "docker") ||
strings.Contains(string(data), "/lxc/") ||
os.Getenv("CONTAINER") != "")
[]string{string(data)}[0] != "systemd" &&
[]string{string(data)}[0] != "init" ||
os.Getenv("container") != "" {
return err == nil && true
}
return err == nil && false
}
// GetPlatformDefaultConfig gets the defaults for the platform
+1 -1
View File
@@ -1,7 +1,7 @@
package config
import (
"gopkg.in/yaml.v3"
yaml "github.com/jesseduffield/yaml"
)
// NewDummyAppConfig creates a new dummy AppConfig for testing
+13 -20
View File
@@ -1,7 +1,5 @@
package config
import "os"
func GetEditTemplate(osConfig *OSConfig, guessDefaultEditor func() string) (string, bool) {
preset := getPreset(osConfig, guessDefaultEditor)
template := osConfig.Edit
@@ -44,11 +42,9 @@ type editPreset struct {
editAtLineTemplate string
editAtLineAndWaitTemplate string
openDirInEditorTemplate string
suspend func() bool
suspend bool
}
func returnBool(a bool) func() bool { return (func() bool { return a }) }
// IF YOU ADD A PRESET TO THIS FUNCTION YOU MUST UPDATE THE `Supported presets` SECTION OF docs/Config.md
func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset {
presets := map[string]*editPreset{
@@ -56,15 +52,12 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
"vim": standardTerminalEditorPreset("vim"),
"nvim": standardTerminalEditorPreset("nvim"),
"nvim-remote": {
editTemplate: `[ -z "$NVIM" ] && (nvim -- {{filename}}) || (nvim --server "$NVIM" --remote-send "q" && nvim --server "$NVIM" --remote-tab {{filename}})`,
editAtLineTemplate: `[ -z "$NVIM" ] && (nvim +{{line}} -- {{filename}}) || (nvim --server "$NVIM" --remote-send "q" && nvim --server "$NVIM" --remote-tab {{filename}} && nvim --server "$NVIM" --remote-send ":{{line}}<CR>")`,
editTemplate: `nvim --server "$NVIM" --remote-tab {{filename}}`,
editAtLineTemplate: `nvim --server "$NVIM" --remote-tab {{filename}}; [ -z "$NVIM" ] || nvim --server "$NVIM" --remote-send ":{{line}}<CR>"`,
// No remote-wait support yet. See https://github.com/neovim/neovim/pull/17856
editAtLineAndWaitTemplate: `nvim +{{line}} {{filename}}`,
openDirInEditorTemplate: `[ -z "$NVIM" ] && (nvim -- {{dir}}) || (nvim --server "$NVIM" --remote-send "q" && nvim --server "$NVIM" --remote-tab {{dir}})`,
suspend: func() bool {
_, ok := os.LookupEnv("NVIM")
return !ok
},
openDirInEditorTemplate: `nvim --server "$NVIM" --remote-tab {{dir}}`,
suspend: false,
},
"lvim": standardTerminalEditorPreset("lvim"),
"emacs": standardTerminalEditorPreset("emacs"),
@@ -76,42 +69,42 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
editAtLineTemplate: "helix -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "helix -- {{filename}}:{{line}}",
openDirInEditorTemplate: "helix -- {{dir}}",
suspend: returnBool(true),
suspend: true,
},
"helix (hx)": {
editTemplate: "hx -- {{filename}}",
editAtLineTemplate: "hx -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "hx -- {{filename}}:{{line}}",
openDirInEditorTemplate: "hx -- {{dir}}",
suspend: returnBool(true),
suspend: true,
},
"vscode": {
editTemplate: "code --reuse-window -- {{filename}}",
editAtLineTemplate: "code --reuse-window --goto -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "code --reuse-window --goto --wait -- {{filename}}:{{line}}",
openDirInEditorTemplate: "code -- {{dir}}",
suspend: returnBool(false),
suspend: false,
},
"sublime": {
editTemplate: "subl -- {{filename}}",
editAtLineTemplate: "subl -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "subl --wait -- {{filename}}:{{line}}",
openDirInEditorTemplate: "subl -- {{dir}}",
suspend: returnBool(false),
suspend: false,
},
"bbedit": {
editTemplate: "bbedit -- {{filename}}",
editAtLineTemplate: "bbedit +{{line}} -- {{filename}}",
editAtLineAndWaitTemplate: "bbedit +{{line}} --wait -- {{filename}}",
openDirInEditorTemplate: "bbedit -- {{dir}}",
suspend: returnBool(false),
suspend: false,
},
"xcode": {
editTemplate: "xed -- {{filename}}",
editAtLineTemplate: "xed --line {{line}} -- {{filename}}",
editAtLineAndWaitTemplate: "xed --line {{line}} --wait -- {{filename}}",
openDirInEditorTemplate: "xed -- {{dir}}",
suspend: returnBool(false),
suspend: false,
},
}
@@ -148,7 +141,7 @@ func standardTerminalEditorPreset(editor string) *editPreset {
editAtLineTemplate: editor + " +{{line}} -- {{filename}}",
editAtLineAndWaitTemplate: editor + " +{{line}} -- {{filename}}",
openDirInEditorTemplate: editor + " -- {{dir}}",
suspend: returnBool(true),
suspend: true,
}
}
@@ -156,5 +149,5 @@ func getEditInTerminal(osConfig *OSConfig, preset *editPreset) bool {
if osConfig.SuspendOnEdit != nil {
return *osConfig.SuspendOnEdit
}
return preset.suspend()
return preset.suspend
}
+205 -293
View File
@@ -19,9 +19,12 @@ type UserConfig struct {
ConfirmOnQuit bool `yaml:"confirmOnQuit"`
// If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close
QuitOnTopLevelReturn bool `yaml:"quitOnTopLevelReturn"`
// Keybindings
Keybinding KeybindingConfig `yaml:"keybinding"`
// Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc
OS OSConfig `yaml:"os,omitempty"`
// If true, don't display introductory popups upon opening Lazygit.
// Lazygit sets this to true upon first runninng the program so that you don't see introductory popups every time you open the program.
DisableStartupPopups bool `yaml:"disableStartupPopups"`
// User-configured commands that can be invoked from within Lazygit
CustomCommands []CustomCommand `yaml:"customCommands" jsonschema:"uniqueItems=true"`
@@ -35,8 +38,6 @@ type UserConfig struct {
NotARepository string `yaml:"notARepository" jsonschema:"enum=prompt,enum=create,enum=skip,enum=quit"`
// If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.
PromptToReturnFromSubprocess bool `yaml:"promptToReturnFromSubprocess"`
// Keybindings
Keybinding KeybindingConfig `yaml:"keybinding"`
}
type RefresherConfig struct {
@@ -77,9 +78,6 @@ type GuiConfig struct {
SidePanelWidth float64 `yaml:"sidePanelWidth" jsonschema:"maximum=1,minimum=0"`
// If true, increase the height of the focused side window; creating an accordion effect.
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
// The weight of the expanded side panel, relative to the other panels. 2 means
// twice as tall as the other panels. Only relevant if `expandFocusedSidePanel` is true.
ExpandedSidePanelWeight int `yaml:"expandedSidePanelWeight"`
// Sometimes the main window is split in two (e.g. when the selected file has both staged and unstaged changes). This setting controls how the two sections are split.
// Options are:
// - 'horizontal': split the window horizontally
@@ -125,17 +123,8 @@ type GuiConfig struct {
NerdFontsVersion string `yaml:"nerdFontsVersion" jsonschema:"enum=2,enum=3,enum="`
// If true (default), file icons are shown in the file views. Only relevant if NerdFontsVersion is not empty.
ShowFileIcons bool `yaml:"showFileIcons"`
// Length of author name in (non-expanded) commits view. 2 means show initials only.
CommitAuthorShortLength int `yaml:"commitAuthorShortLength"`
// Length of author name in expanded commits view. 2 means show initials only.
CommitAuthorLongLength int `yaml:"commitAuthorLongLength"`
// Length of commit hash in commits view. 0 shows '*' if NF icons aren't on.
CommitHashLength int `yaml:"commitHashLength" jsonschema:"minimum=0"`
// If true, show commit hashes alongside branch names in the branches view.
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
// Whether to show the divergence from the base branch in the branches view.
// One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
ShowDivergenceFromBaseBranch string `yaml:"showDivergenceFromBaseBranch" jsonschema:"enum=none,enum=onlyArrow,enum=arrowAndNumber"`
// Height of the command log view
CommandLogSize int `yaml:"commandLogSize" jsonschema:"minimum=0"`
// Whether to split the main window when viewing file changes.
@@ -153,18 +142,6 @@ type GuiConfig struct {
// Whether to stack UI components on top of each other.
// One of 'auto' (default) | 'always' | 'never'
PortraitMode string `yaml:"portraitMode"`
// How things are filtered when typing '/'.
// One of 'substring' (default) | 'fuzzy'
FilterMode string `yaml:"filterMode" jsonschema:"enum=substring,enum=fuzzy"`
// Config relating to the spinner.
Spinner SpinnerConfig `yaml:"spinner"`
// Status panel view.
// One of 'dashboard' (default) | 'allBranchesLog'
StatusPanelView string `yaml:"statusPanelView" jsonschema:"enum=dashboard,enum=allBranchesLog"`
}
func (c *GuiConfig) UseFuzzySearch() bool {
return c.FilterMode == "fuzzy"
}
type ThemeConfig struct {
@@ -179,8 +156,6 @@ type ThemeConfig struct {
// Background color of selected line.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
SelectedLineBgColor []string `yaml:"selectedLineBgColor" jsonschema:"minItems=1,uniqueItems=true"`
// Background color of selected line when view doesn't have focus.
InactiveViewSelectedLineBgColor []string `yaml:"inactiveViewSelectedLineBgColor" jsonschema:"minItems=1,uniqueItems=true"`
// Foreground color of copied commit
CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor" jsonschema:"minItems=1,uniqueItems=true"`
// Background color of copied commit
@@ -200,13 +175,6 @@ type CommitLengthConfig struct {
Show bool `yaml:"show"`
}
type SpinnerConfig struct {
// The frames of the spinner animation.
Frames []string `yaml:"frames"`
// The "speed" of the spinner in milliseconds.
Rate int `yaml:"rate" jsonschema:"minimum=1"`
}
type GitConfig struct {
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
Paging PagingConfig `yaml:"paging"`
@@ -226,29 +194,19 @@ type GitConfig struct {
FetchAll bool `yaml:"fetchAll"`
// Command used when displaying the current branch git log in the main window
BranchLogCmd string `yaml:"branchLogCmd"`
// Command used to display git log of all branches in the main window.
// Deprecated: User `allBranchesLogCmds` instead.
// Command used to display git log of all branches in the main window
AllBranchesLogCmd string `yaml:"allBranchesLogCmd"`
// Commands used to display git log of all branches in the main window, they will be cycled in order of appearance
AllBranchesLogCmds []string `yaml:"allBranchesLogCmds"`
// If true, do not spawn a separate process when using GPG
OverrideGpg bool `yaml:"overrideGpg"`
// If true, do not allow force pushes
DisableForcePushing bool `yaml:"disableForcePushing"`
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix
CommitPrefix *CommitPrefixConfig `yaml:"commitPrefix"`
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix
CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"`
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-branch-name-prefix
BranchPrefix string `yaml:"branchPrefix"`
// If true, parse emoji strings in commit messages e.g. render :rocket: as 🚀
// (This should really be under 'gui', not 'git')
ParseEmoji bool `yaml:"parseEmoji"`
// Config for showing the log in the commits view
Log LogConfig `yaml:"log"`
// When copying commit hashes to the clipboard, truncate them to this
// length. Set to 40 to disable truncation.
TruncateCopiedCommitHashesTo int `yaml:"truncateCopiedCommitHashesTo"`
}
type PagerType string
@@ -268,7 +226,7 @@ type PagingConfig struct {
// diff-so-fancy
// delta --dark --paging=never
// ydiff -p cat -s --wrap --width={{columnWidth}}
Pager PagerType `yaml:"pager"`
Pager PagerType `yaml:"pager" jsonschema:"minLength=1"`
// If true, Lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
UseConfig bool `yaml:"useConfig"`
// e.g. 'difft --color=always'
@@ -278,10 +236,6 @@ type PagingConfig struct {
type CommitConfig struct {
// If true, pass '--signoff' flag when committing
SignOff bool `yaml:"signOff"`
// Automatic WYSIWYG wrapping of the commit message as you type
AutoWrapCommitMessage bool `yaml:"autoWrapCommitMessage"`
// If autoWrapCommitMessage is true, the width to wrap to
AutoWrapWidth int `yaml:"autoWrapWidth"`
}
type MergingConfig struct {
@@ -290,8 +244,6 @@ type MergingConfig struct {
ManualCommit bool `yaml:"manualCommit"`
// Extra args passed to `git merge`, e.g. --no-ff
Args string `yaml:"args" jsonschema:"example=--no-ff"`
// The commit message to use for a squash merge commit. Can contain "{{selectedRef}}" and "{{currentBranch}}" placeholders.
SquashMergeMessage string `yaml:"squashMergeMessage"`
}
type LogConfig struct {
@@ -312,9 +264,9 @@ type LogConfig struct {
type CommitPrefixConfig struct {
// pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*"
Pattern string `yaml:"pattern" jsonschema:"example=^\\w+\\/(\\w+-\\w+).*"`
Pattern string `yaml:"pattern" jsonschema:"example=^\\w+\\/(\\w+-\\w+).*,minLength=1"`
// Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] "
Replace string `yaml:"replace" jsonschema:"example=[$1]"`
Replace string `yaml:"replace" jsonschema:"example=[$1] ,minLength=1"`
}
type UpdateConfig struct {
@@ -325,91 +277,88 @@ type UpdateConfig struct {
}
type KeybindingConfig struct {
Universal KeybindingUniversalConfig `yaml:"universal"`
Status KeybindingStatusConfig `yaml:"status"`
Files KeybindingFilesConfig `yaml:"files"`
Branches KeybindingBranchesConfig `yaml:"branches"`
Worktrees KeybindingWorktreesConfig `yaml:"worktrees"`
Commits KeybindingCommitsConfig `yaml:"commits"`
AmendAttribute KeybindingAmendAttributeConfig `yaml:"amendAttribute"`
Stash KeybindingStashConfig `yaml:"stash"`
CommitFiles KeybindingCommitFilesConfig `yaml:"commitFiles"`
Main KeybindingMainConfig `yaml:"main"`
Submodules KeybindingSubmodulesConfig `yaml:"submodules"`
CommitMessage KeybindingCommitMessageConfig `yaml:"commitMessage"`
Universal KeybindingUniversalConfig `yaml:"universal"`
Status KeybindingStatusConfig `yaml:"status"`
Files KeybindingFilesConfig `yaml:"files"`
Branches KeybindingBranchesConfig `yaml:"branches"`
Worktrees KeybindingWorktreesConfig `yaml:"worktrees"`
Commits KeybindingCommitsConfig `yaml:"commits"`
Stash KeybindingStashConfig `yaml:"stash"`
CommitFiles KeybindingCommitFilesConfig `yaml:"commitFiles"`
Main KeybindingMainConfig `yaml:"main"`
Submodules KeybindingSubmodulesConfig `yaml:"submodules"`
CommitMessage KeybindingCommitMessageConfig `yaml:"commitMessage"`
}
// damn looks like we have some inconsistencies here with -alt and -alt1
type KeybindingUniversalConfig struct {
Quit string `yaml:"quit"`
QuitAlt1 string `yaml:"quit-alt1"`
Return string `yaml:"return"`
QuitWithoutChangingDirectory string `yaml:"quitWithoutChangingDirectory"`
TogglePanel string `yaml:"togglePanel"`
PrevItem string `yaml:"prevItem"`
NextItem string `yaml:"nextItem"`
PrevItemAlt string `yaml:"prevItem-alt"`
NextItemAlt string `yaml:"nextItem-alt"`
PrevPage string `yaml:"prevPage"`
NextPage string `yaml:"nextPage"`
ScrollLeft string `yaml:"scrollLeft"`
ScrollRight string `yaml:"scrollRight"`
GotoTop string `yaml:"gotoTop"`
GotoBottom string `yaml:"gotoBottom"`
ToggleRangeSelect string `yaml:"toggleRangeSelect"`
RangeSelectDown string `yaml:"rangeSelectDown"`
RangeSelectUp string `yaml:"rangeSelectUp"`
PrevBlock string `yaml:"prevBlock"`
NextBlock string `yaml:"nextBlock"`
PrevBlockAlt string `yaml:"prevBlock-alt"`
NextBlockAlt string `yaml:"nextBlock-alt"`
NextBlockAlt2 string `yaml:"nextBlock-alt2"`
PrevBlockAlt2 string `yaml:"prevBlock-alt2"`
JumpToBlock []string `yaml:"jumpToBlock"`
NextMatch string `yaml:"nextMatch"`
PrevMatch string `yaml:"prevMatch"`
StartSearch string `yaml:"startSearch"`
OptionMenu string `yaml:"optionMenu"`
OptionMenuAlt1 string `yaml:"optionMenu-alt1"`
Select string `yaml:"select"`
GoInto string `yaml:"goInto"`
Confirm string `yaml:"confirm"`
ConfirmInEditor string `yaml:"confirmInEditor"`
Remove string `yaml:"remove"`
New string `yaml:"new"`
Edit string `yaml:"edit"`
OpenFile string `yaml:"openFile"`
ScrollUpMain string `yaml:"scrollUpMain"`
ScrollDownMain string `yaml:"scrollDownMain"`
ScrollUpMainAlt1 string `yaml:"scrollUpMain-alt1"`
ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"`
ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"`
ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"`
ExecuteCustomCommand string `yaml:"executeCustomCommand"`
CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"`
Push string `yaml:"pushFiles"` // 'Files' appended for legacy reasons
Pull string `yaml:"pullFiles"` // 'Files' appended for legacy reasons
Refresh string `yaml:"refresh"`
CreatePatchOptionsMenu string `yaml:"createPatchOptionsMenu"`
NextTab string `yaml:"nextTab"`
PrevTab string `yaml:"prevTab"`
NextScreenMode string `yaml:"nextScreenMode"`
PrevScreenMode string `yaml:"prevScreenMode"`
Undo string `yaml:"undo"`
Redo string `yaml:"redo"`
FilteringMenu string `yaml:"filteringMenu"`
DiffingMenu string `yaml:"diffingMenu"`
DiffingMenuAlt string `yaml:"diffingMenu-alt"`
CopyToClipboard string `yaml:"copyToClipboard"`
OpenRecentRepos string `yaml:"openRecentRepos"`
SubmitEditorText string `yaml:"submitEditorText"`
ExtrasMenu string `yaml:"extrasMenu"`
ToggleWhitespaceInDiffView string `yaml:"toggleWhitespaceInDiffView"`
IncreaseContextInDiffView string `yaml:"increaseContextInDiffView"`
DecreaseContextInDiffView string `yaml:"decreaseContextInDiffView"`
IncreaseRenameSimilarityThreshold string `yaml:"increaseRenameSimilarityThreshold"`
DecreaseRenameSimilarityThreshold string `yaml:"decreaseRenameSimilarityThreshold"`
OpenDiffTool string `yaml:"openDiffTool"`
Quit string `yaml:"quit"`
QuitAlt1 string `yaml:"quit-alt1"`
Return string `yaml:"return"`
QuitWithoutChangingDirectory string `yaml:"quitWithoutChangingDirectory"`
TogglePanel string `yaml:"togglePanel"`
PrevItem string `yaml:"prevItem"`
NextItem string `yaml:"nextItem"`
PrevItemAlt string `yaml:"prevItem-alt"`
NextItemAlt string `yaml:"nextItem-alt"`
PrevPage string `yaml:"prevPage"`
NextPage string `yaml:"nextPage"`
ScrollLeft string `yaml:"scrollLeft"`
ScrollRight string `yaml:"scrollRight"`
GotoTop string `yaml:"gotoTop"`
GotoBottom string `yaml:"gotoBottom"`
ToggleRangeSelect string `yaml:"toggleRangeSelect"`
RangeSelectDown string `yaml:"rangeSelectDown"`
RangeSelectUp string `yaml:"rangeSelectUp"`
PrevBlock string `yaml:"prevBlock"`
NextBlock string `yaml:"nextBlock"`
PrevBlockAlt string `yaml:"prevBlock-alt"`
NextBlockAlt string `yaml:"nextBlock-alt"`
NextBlockAlt2 string `yaml:"nextBlock-alt2"`
PrevBlockAlt2 string `yaml:"prevBlock-alt2"`
JumpToBlock []string `yaml:"jumpToBlock"`
NextMatch string `yaml:"nextMatch"`
PrevMatch string `yaml:"prevMatch"`
StartSearch string `yaml:"startSearch"`
OptionMenu string `yaml:"optionMenu"`
OptionMenuAlt1 string `yaml:"optionMenu-alt1"`
Select string `yaml:"select"`
GoInto string `yaml:"goInto"`
Confirm string `yaml:"confirm"`
ConfirmInEditor string `yaml:"confirmInEditor"`
Remove string `yaml:"remove"`
New string `yaml:"new"`
Edit string `yaml:"edit"`
OpenFile string `yaml:"openFile"`
ScrollUpMain string `yaml:"scrollUpMain"`
ScrollDownMain string `yaml:"scrollDownMain"`
ScrollUpMainAlt1 string `yaml:"scrollUpMain-alt1"`
ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"`
ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"`
ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"`
ExecuteCustomCommand string `yaml:"executeCustomCommand"`
CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"`
Push string `yaml:"pushFiles"` // 'Files' appended for legacy reasons
Pull string `yaml:"pullFiles"` // 'Files' appended for legacy reasons
Refresh string `yaml:"refresh"`
CreatePatchOptionsMenu string `yaml:"createPatchOptionsMenu"`
NextTab string `yaml:"nextTab"`
PrevTab string `yaml:"prevTab"`
NextScreenMode string `yaml:"nextScreenMode"`
PrevScreenMode string `yaml:"prevScreenMode"`
Undo string `yaml:"undo"`
Redo string `yaml:"redo"`
FilteringMenu string `yaml:"filteringMenu"`
DiffingMenu string `yaml:"diffingMenu"`
DiffingMenuAlt string `yaml:"diffingMenu-alt"`
CopyToClipboard string `yaml:"copyToClipboard"`
OpenRecentRepos string `yaml:"openRecentRepos"`
SubmitEditorText string `yaml:"submitEditorText"`
ExtrasMenu string `yaml:"extrasMenu"`
ToggleWhitespaceInDiffView string `yaml:"toggleWhitespaceInDiffView"`
IncreaseContextInDiffView string `yaml:"increaseContextInDiffView"`
DecreaseContextInDiffView string `yaml:"decreaseContextInDiffView"`
OpenDiffTool string `yaml:"openDiffTool"`
}
type KeybindingStatusConfig struct {
@@ -487,12 +436,6 @@ type KeybindingCommitsConfig struct {
StartInteractiveRebase string `yaml:"startInteractiveRebase"`
}
type KeybindingAmendAttributeConfig struct {
ResetAuthor string `yaml:"resetAuthor"`
SetAuthor string `yaml:"setAuthor"`
AddCoAuthor string `yaml:"addCoAuthor"`
}
type KeybindingStashConfig struct {
PopStash string `yaml:"popStash"`
RenameStash string `yaml:"renameStash"`
@@ -515,7 +458,7 @@ type KeybindingSubmodulesConfig struct {
}
type KeybindingCommitMessageConfig struct {
CommitMenu string `yaml:"commitMenu"`
SwitchToEditor string `yaml:"switchToEditor"`
}
// OSConfig contains config on the level of the os
@@ -574,12 +517,8 @@ type OSConfig struct {
OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
// CopyToClipboardCmd is the command for copying to clipboard.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard
CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"`
// ReadFromClipboardCmd is the command for reading the clipboard.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
ReadFromClipboardCmd string `yaml:"readFromClipboardCmd,omitempty"`
}
type CustomCommandAfterHook struct {
@@ -605,8 +544,6 @@ type CustomCommand struct {
Stream bool `yaml:"stream"`
// If true, show the command's output in a popup within Lazygit
ShowOutput bool `yaml:"showOutput"`
// The title to display in the popup panel if showOutput is true. If left unset, the command will be used as the title.
OutputTitle string `yaml:"outputTitle"`
// Actions to take after the command has completed
After CustomCommandAfterHook `yaml:"after"`
}
@@ -676,55 +613,42 @@ func GetDefaultConfig() *UserConfig {
SkipStashWarning: false,
SidePanelWidth: 0.3333,
ExpandFocusedSidePanel: false,
ExpandedSidePanelWeight: 2,
MainPanelSplitMode: "flexible",
EnlargedSideViewLocation: "left",
Language: "auto",
TimeFormat: "02 Jan 06",
ShortTimeFormat: time.Kitchen,
Theme: ThemeConfig{
ActiveBorderColor: []string{"green", "bold"},
SearchingActiveBorderColor: []string{"cyan", "bold"},
InactiveBorderColor: []string{"default"},
OptionsTextColor: []string{"blue"},
SelectedLineBgColor: []string{"blue"},
InactiveViewSelectedLineBgColor: []string{"bold"},
CherryPickedCommitBgColor: []string{"cyan"},
CherryPickedCommitFgColor: []string{"blue"},
MarkedBaseCommitBgColor: []string{"yellow"},
MarkedBaseCommitFgColor: []string{"blue"},
UnstagedChangesColor: []string{"red"},
DefaultFgColor: []string{"default"},
ActiveBorderColor: []string{"green", "bold"},
SearchingActiveBorderColor: []string{"cyan", "bold"},
InactiveBorderColor: []string{"default"},
OptionsTextColor: []string{"blue"},
SelectedLineBgColor: []string{"blue"},
CherryPickedCommitBgColor: []string{"cyan"},
CherryPickedCommitFgColor: []string{"blue"},
MarkedBaseCommitBgColor: []string{"yellow"},
MarkedBaseCommitFgColor: []string{"blue"},
UnstagedChangesColor: []string{"red"},
DefaultFgColor: []string{"default"},
},
CommitLength: CommitLengthConfig{Show: true},
SkipNoStagedFilesWarning: false,
ShowListFooter: true,
ShowCommandLog: true,
ShowBottomLine: true,
ShowPanelJumps: true,
ShowFileTree: true,
ShowRandomTip: true,
ShowIcons: false,
NerdFontsVersion: "",
ShowFileIcons: true,
CommitAuthorShortLength: 2,
CommitAuthorLongLength: 17,
CommitHashLength: 8,
ShowBranchCommitHash: false,
ShowDivergenceFromBaseBranch: "none",
CommandLogSize: 8,
SplitDiff: "auto",
SkipRewordInEditorWarning: false,
WindowSize: "normal",
Border: "rounded",
AnimateExplosion: true,
PortraitMode: "auto",
FilterMode: "substring",
Spinner: SpinnerConfig{
Frames: []string{"|", "/", "-", "\\"},
Rate: 50,
},
StatusPanelView: "dashboard",
CommitLength: CommitLengthConfig{Show: true},
SkipNoStagedFilesWarning: false,
ShowListFooter: true,
ShowCommandLog: true,
ShowBottomLine: true,
ShowPanelJumps: true,
ShowFileTree: true,
ShowRandomTip: true,
ShowIcons: false,
NerdFontsVersion: "",
ShowFileIcons: true,
ShowBranchCommitHash: false,
CommandLogSize: 8,
SplitDiff: "auto",
SkipRewordInEditorWarning: false,
Border: "rounded",
AnimateExplosion: true,
PortraitMode: "auto",
},
Git: GitConfig{
Paging: PagingConfig{
@@ -734,32 +658,27 @@ func GetDefaultConfig() *UserConfig {
ExternalDiffCommand: "",
},
Commit: CommitConfig{
SignOff: false,
AutoWrapCommitMessage: true,
AutoWrapWidth: 72,
SignOff: false,
},
Merging: MergingConfig{
ManualCommit: false,
Args: "",
SquashMergeMessage: "Squash merge {{selectedRef}} into {{currentBranch}}",
ManualCommit: false,
Args: "",
},
Log: LogConfig{
Order: "topo-order",
ShowGraph: "always",
ShowWholeGraph: false,
},
SkipHookPrefix: "WIP",
MainBranches: []string{"master", "main"},
AutoFetch: true,
AutoRefresh: true,
FetchAll: true,
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium",
DisableForcePushing: false,
CommitPrefixes: map[string]CommitPrefixConfig(nil),
BranchPrefix: "",
ParseEmoji: false,
TruncateCopiedCommitHashesTo: 12,
SkipHookPrefix: "WIP",
MainBranches: []string{"master", "main"},
AutoFetch: true,
AutoRefresh: true,
FetchAll: true,
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium",
DisableForcePushing: false,
CommitPrefixes: map[string]CommitPrefixConfig(nil),
ParseEmoji: false,
},
Refresher: RefresherConfig{
RefreshInterval: 10,
@@ -769,85 +688,77 @@ func GetDefaultConfig() *UserConfig {
Method: "prompt",
Days: 14,
},
ConfirmOnQuit: false,
QuitOnTopLevelReturn: false,
OS: OSConfig{},
DisableStartupPopups: false,
CustomCommands: []CustomCommand(nil),
Services: map[string]string(nil),
NotARepository: "prompt",
PromptToReturnFromSubprocess: true,
ConfirmOnQuit: false,
QuitOnTopLevelReturn: false,
Keybinding: KeybindingConfig{
Universal: KeybindingUniversalConfig{
Quit: "q",
QuitAlt1: "<c-c>",
Return: "<esc>",
QuitWithoutChangingDirectory: "Q",
TogglePanel: "<tab>",
PrevItem: "<up>",
NextItem: "<down>",
PrevItemAlt: "k",
NextItemAlt: "j",
PrevPage: ",",
NextPage: ".",
ScrollLeft: "H",
ScrollRight: "L",
GotoTop: "<",
GotoBottom: ">",
ToggleRangeSelect: "v",
RangeSelectDown: "<s-down>",
RangeSelectUp: "<s-up>",
PrevBlock: "<left>",
NextBlock: "<right>",
PrevBlockAlt: "h",
NextBlockAlt: "l",
PrevBlockAlt2: "<backtab>",
NextBlockAlt2: "<tab>",
JumpToBlock: []string{"1", "2", "3", "4", "5"},
NextMatch: "n",
PrevMatch: "N",
StartSearch: "/",
OptionMenu: "<disabled>",
OptionMenuAlt1: "?",
Select: "<space>",
GoInto: "<enter>",
Confirm: "<enter>",
ConfirmInEditor: "<a-enter>",
Remove: "d",
New: "n",
Edit: "e",
OpenFile: "o",
OpenRecentRepos: "<c-r>",
ScrollUpMain: "<pgup>",
ScrollDownMain: "<pgdown>",
ScrollUpMainAlt1: "K",
ScrollDownMainAlt1: "J",
ScrollUpMainAlt2: "<c-u>",
ScrollDownMainAlt2: "<c-d>",
ExecuteCustomCommand: ":",
CreateRebaseOptionsMenu: "m",
Push: "P",
Pull: "p",
Refresh: "R",
CreatePatchOptionsMenu: "<c-p>",
NextTab: "]",
PrevTab: "[",
NextScreenMode: "+",
PrevScreenMode: "_",
Undo: "z",
Redo: "<c-z>",
FilteringMenu: "<c-s>",
DiffingMenu: "W",
DiffingMenuAlt: "<c-e>",
CopyToClipboard: "<c-o>",
SubmitEditorText: "<enter>",
ExtrasMenu: "@",
ToggleWhitespaceInDiffView: "<c-w>",
IncreaseContextInDiffView: "}",
DecreaseContextInDiffView: "{",
IncreaseRenameSimilarityThreshold: ")",
DecreaseRenameSimilarityThreshold: "(",
OpenDiffTool: "<c-t>",
Quit: "q",
QuitAlt1: "<c-c>",
Return: "<esc>",
QuitWithoutChangingDirectory: "Q",
TogglePanel: "<tab>",
PrevItem: "<up>",
NextItem: "<down>",
PrevItemAlt: "k",
NextItemAlt: "j",
PrevPage: ",",
NextPage: ".",
ScrollLeft: "H",
ScrollRight: "L",
GotoTop: "<",
GotoBottom: ">",
ToggleRangeSelect: "v",
RangeSelectDown: "<s-down>",
RangeSelectUp: "<s-up>",
PrevBlock: "<left>",
NextBlock: "<right>",
PrevBlockAlt: "h",
NextBlockAlt: "l",
PrevBlockAlt2: "<backtab>",
NextBlockAlt2: "<tab>",
JumpToBlock: []string{"1", "2", "3", "4", "5"},
NextMatch: "n",
PrevMatch: "N",
StartSearch: "/",
OptionMenu: "<disabled>",
OptionMenuAlt1: "?",
Select: "<space>",
GoInto: "<enter>",
Confirm: "<enter>",
ConfirmInEditor: "<a-enter>",
Remove: "d",
New: "n",
Edit: "e",
OpenFile: "o",
OpenRecentRepos: "<c-r>",
ScrollUpMain: "<pgup>",
ScrollDownMain: "<pgdown>",
ScrollUpMainAlt1: "K",
ScrollDownMainAlt1: "J",
ScrollUpMainAlt2: "<c-u>",
ScrollDownMainAlt2: "<c-d>",
ExecuteCustomCommand: ":",
CreateRebaseOptionsMenu: "m",
Push: "P",
Pull: "p",
Refresh: "R",
CreatePatchOptionsMenu: "<c-p>",
NextTab: "]",
PrevTab: "[",
NextScreenMode: "+",
PrevScreenMode: "_",
Undo: "z",
Redo: "<c-z>",
FilteringMenu: "<c-s>",
DiffingMenu: "W",
DiffingMenuAlt: "<c-e>",
CopyToClipboard: "<c-o>",
SubmitEditorText: "<enter>",
ExtrasMenu: "@",
ToggleWhitespaceInDiffView: "<c-w>",
IncreaseContextInDiffView: "}",
DecreaseContextInDiffView: "{",
OpenDiffTool: "<c-t>",
},
Status: KeybindingStatusConfig{
CheckForUpdate: "u",
@@ -919,11 +830,6 @@ func GetDefaultConfig() *UserConfig {
ViewBisectOptions: "b",
StartInteractiveRebase: "i",
},
AmendAttribute: KeybindingAmendAttributeConfig{
ResetAuthor: "a",
SetAuthor: "A",
AddCoAuthor: "c",
},
Stash: KeybindingStashConfig{
PopStash: "g",
RenameStash: "r",
@@ -942,8 +848,14 @@ func GetDefaultConfig() *UserConfig {
BulkMenu: "b",
},
CommitMessage: KeybindingCommitMessageConfig{
CommitMenu: "<c-o>",
SwitchToEditor: "<c-o>",
},
},
OS: OSConfig{},
DisableStartupPopups: false,
CustomCommands: []CustomCommand(nil),
Services: map[string]string(nil),
NotARepository: "prompt",
PromptToReturnFromSubprocess: true,
}
}
-27
View File
@@ -1,27 +0,0 @@
package config
import (
"fmt"
"slices"
"strings"
)
func (config *UserConfig) Validate() error {
if err := validateEnum("gui.statusPanelView", config.Gui.StatusPanelView,
[]string{"dashboard", "allBranchesLog"}); err != nil {
return err
}
if err := validateEnum("gui.showDivergenceFromBaseBranch", config.Gui.ShowDivergenceFromBaseBranch,
[]string{"none", "onlyArrow", "arrowAndNumber"}); err != nil {
return err
}
return nil
}
func validateEnum(name string, value string, allowedValues []string) error {
if slices.Contains(allowedValues, value) {
return nil
}
allowedValuesStr := strings.Join(allowedValues, ", ")
return fmt.Errorf("Unexpected value '%s' for '%s'. Allowed values: %s", value, name, allowedValuesStr)
}
-49
View File
@@ -1,49 +0,0 @@
package config
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUserConfigValidate_enums(t *testing.T) {
type testCase struct {
value string
valid bool
}
scenarios := []struct {
name string
setup func(config *UserConfig, value string)
testCases []testCase
}{
{
name: "Gui.StatusPanelView",
setup: func(config *UserConfig, value string) {
config.Gui.StatusPanelView = value
},
testCases: []testCase{
{value: "dashboard", valid: true},
{value: "allBranchesLog", valid: true},
{value: "", valid: false},
{value: "invalid_value", valid: false},
},
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
for _, testCase := range s.testCases {
config := GetDefaultConfig()
s.setup(config, testCase.value)
err := config.Validate()
if testCase.valid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
}
})
}
}
+2 -2
View File
@@ -28,9 +28,9 @@ var Links = struct {
CustomPagers: "https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md",
CustomKeybindings: "https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md",
CustomCommands: "https://github.com/jesseduffield/lazygit/wiki/Custom-Commands-Compendium",
Keybindings: "https://github.com/jesseduffield/lazygit/blob/%s/docs/keybindings",
Keybindings: "https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings",
Undoing: "https://github.com/jesseduffield/lazygit/blob/master/docs/Undoing.md",
Config: "https://github.com/jesseduffield/lazygit/blob/%s/docs/Config.md",
Config: "https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
Tutorial: "https://youtu.be/VDXvbHZYeKY",
CustomPatchDemo: "https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches",
},
+1 -27
View File
@@ -1,8 +1,6 @@
package gui
import (
"fmt"
"runtime"
"strings"
"time"
@@ -48,29 +46,6 @@ func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
refreshInterval)
}
}
if self.gui.Config.GetDebug() {
self.goEvery(time.Second*time.Duration(10), self.gui.stopChan, func() error {
formatBytes := func(b uint64) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := uint64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB",
float64(b)/float64(div), "kMGTPE"[exp])
}
m := runtime.MemStats{}
runtime.ReadMemStats(&m)
self.gui.c.Log.Infof("Heap memory in use: %s", formatBytes(m.HeapAlloc))
return nil
})
}
}
func (self *BackgroundRoutineMgr) startBackgroundFetch() {
@@ -112,10 +87,9 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
if self.pauseBackgroundRefreshes {
continue
}
self.gui.c.OnWorker(func(gocui.Task) error {
self.gui.c.OnWorker(func(gocui.Task) {
_ = function()
done <- struct{}{}
return nil
})
// waiting so that we don't bunch up refreshes if the refresh takes longer than the interval
<-done
-13
View File
@@ -230,10 +230,6 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts)
self.gui.helpers.Window.SetWindowContext(c)
self.gui.helpers.Window.MoveToTopOfWindow(c)
oldView := self.gui.c.GocuiGui().CurrentView()
if oldView != nil && oldView.Name() != viewName {
oldView.HighlightInactive = true
}
if _, err := self.gui.c.GocuiGui().SetCurrentView(viewName); err != nil {
return err
}
@@ -391,12 +387,3 @@ func (self *ContextMgr) ContextForKey(key types.ContextKey) types.Context {
return nil
}
func (self *ContextMgr) PopupContexts() []types.Context {
self.RLock()
defer self.RUnlock()
return lo.Filter(self.ContextStack, func(context types.Context, _ int) bool {
return context.GetKind() == types.TEMPORARY_POPUP || context.GetKind() == types.PERSISTENT_POPUP
})
}
+27 -39
View File
@@ -20,12 +20,11 @@ type BaseContext struct {
onFocusFn onFocusFn
onFocusLostFn onFocusLostFn
focusable bool
transient bool
hasControlledBounds bool
needsRerenderOnWidthChange types.NeedsRerenderOnWidthChangeLevel
needsRerenderOnHeightChange bool
highlightOnFocus bool
focusable bool
transient bool
hasControlledBounds bool
needsRerenderOnWidthChange bool
highlightOnFocus bool
*ParentContextMgr
}
@@ -38,16 +37,15 @@ type (
var _ types.IBaseContext = &BaseContext{}
type NewBaseContextOpts struct {
Kind types.ContextKind
Key types.ContextKey
View *gocui.View
WindowName string
Focusable bool
Transient bool
HasUncontrolledBounds bool // negating for the sake of making false the default
HighlightOnFocus bool
NeedsRerenderOnWidthChange types.NeedsRerenderOnWidthChangeLevel
NeedsRerenderOnHeightChange bool
Kind types.ContextKind
Key types.ContextKey
View *gocui.View
WindowName string
Focusable bool
Transient bool
HasUncontrolledBounds bool // negating for the sake of making false the default
HighlightOnFocus bool
NeedsRerenderOnWidthChange bool
OnGetOptionsMap func() map[string]string
}
@@ -58,19 +56,18 @@ func NewBaseContext(opts NewBaseContextOpts) *BaseContext {
hasControlledBounds := !opts.HasUncontrolledBounds
return &BaseContext{
kind: opts.Kind,
key: opts.Key,
view: opts.View,
windowName: opts.WindowName,
onGetOptionsMap: opts.OnGetOptionsMap,
focusable: opts.Focusable,
transient: opts.Transient,
hasControlledBounds: hasControlledBounds,
highlightOnFocus: opts.HighlightOnFocus,
needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange,
needsRerenderOnHeightChange: opts.NeedsRerenderOnHeightChange,
ParentContextMgr: &ParentContextMgr{},
viewTrait: viewTrait,
kind: opts.Kind,
key: opts.Key,
view: opts.View,
windowName: opts.WindowName,
onGetOptionsMap: opts.OnGetOptionsMap,
focusable: opts.Focusable,
transient: opts.Transient,
hasControlledBounds: hasControlledBounds,
highlightOnFocus: opts.HighlightOnFocus,
needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange,
ParentContextMgr: &ParentContextMgr{},
viewTrait: viewTrait,
}
}
@@ -133,11 +130,6 @@ func (self *BaseContext) AddMouseKeybindingsFn(fn types.MouseKeybindingsFn) {
self.mouseKeybindingsFns = append(self.mouseKeybindingsFns, fn)
}
func (self *BaseContext) ClearAllBindingsFn() {
self.keybindingsFns = []types.KeybindingsFn{}
self.mouseKeybindingsFns = []types.MouseKeybindingsFn{}
}
func (self *BaseContext) AddOnClickFn(fn func() error) {
if fn != nil {
self.onClickFn = fn
@@ -201,14 +193,10 @@ func (self *BaseContext) HasControlledBounds() bool {
return self.hasControlledBounds
}
func (self *BaseContext) NeedsRerenderOnWidthChange() types.NeedsRerenderOnWidthChangeLevel {
func (self *BaseContext) NeedsRerenderOnWidthChange() bool {
return self.needsRerenderOnWidthChange
}
func (self *BaseContext) NeedsRerenderOnHeightChange() bool {
return self.needsRerenderOnHeightChange
}
func (self *BaseContext) Title() string {
return ""
}
+2 -1
View File
@@ -22,6 +22,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
func(branch *models.Branch) []string {
return []string{branch.Name}
},
func() bool { return c.AppState.LocalBranchSortOrder != "alphabetical" },
)
getDisplayStrings := func(_ int, _ int) [][]string {
@@ -46,7 +47,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
Key: LOCAL_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_WIDTH_CHANGES,
NeedsRerenderOnWidthChange: true,
})),
ListRenderer: ListRenderer{
list: viewModel,
+6 -9
View File
@@ -1,7 +1,6 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
@@ -19,9 +18,8 @@ type CommitFilesContext struct {
}
var (
_ types.IListContext = (*CommitFilesContext)(nil)
_ types.DiffableContext = (*CommitFilesContext)(nil)
_ types.ISearchableContext = (*CommitFilesContext)(nil)
_ types.IListContext = (*CommitFilesContext)(nil)
_ types.DiffableContext = (*CommitFilesContext)(nil)
)
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
@@ -66,7 +64,10 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
},
}
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(ctx.OnSearchSelect))
ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
ctx.GetList().SetSelection(selectedLineIdx)
return ctx.HandleFocus(types.OnFocusOpts{})
}))
return ctx
}
@@ -74,7 +75,3 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
func (self *CommitFilesContext) GetDiffTerminals() []string {
return []string{self.GetRef().RefName()}
}
func (self *CommitFilesContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
return nil
}
+5 -5
View File
@@ -8,6 +8,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
type CommitMessageContext struct {
@@ -111,13 +112,12 @@ func (self *CommitMessageContext) SetPanelState(
self.GetView().Title = summaryTitle
self.c.Views().CommitDescription.Title = descriptionTitle
self.c.Views().CommitDescription.Subtitle = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionSubTitle,
subtitleTemplate := lo.Ternary(onSwitchToEditor != nil, self.c.Tr.CommitDescriptionSubTitle, self.c.Tr.CommitDescriptionSubTitleNoSwitch)
self.c.Views().CommitDescription.Subtitle = utils.ResolvePlaceholderString(subtitleTemplate,
map[string]string{
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.Universal.TogglePanel),
"commitMenuKeybinding": keybindings.Label(self.c.UserConfig.Keybinding.CommitMessage.CommitMenu),
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.Universal.TogglePanel),
"switchToEditorKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.CommitMessage.SwitchToEditor),
})
self.c.Views().CommitDescription.Visible = true
}
func (self *CommitMessageContext) RenderCommitLength() {

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