mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-30 19:16:38 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80223c7fbb | ||
|
|
ff0a16f809 | ||
|
|
48c1db6fe4 | ||
|
|
4c4929acad | ||
|
|
f4920e8efa | ||
|
|
263ad11b3b | ||
|
|
6f0f1e7995 | ||
|
|
3967edd113 | ||
|
|
32a701cb9c | ||
|
|
64bcc72e45 | ||
|
|
beb05d4a61 | ||
|
|
65c27dd8ce | ||
|
|
76452a0bcc | ||
|
|
6782f04e8c | ||
|
|
62854026a3 | ||
|
|
c88381b9ce | ||
|
|
afbef40258 | ||
|
|
4e0194d8f7 | ||
|
|
d334bae0a0 | ||
|
|
76948f82c1 | ||
|
|
a22aced1bb | ||
|
|
40cc008a5a | ||
|
|
7282159f78 | ||
|
|
e02f073cd0 | ||
|
|
853b54b069 | ||
|
|
80f860beb7 | ||
|
|
d3fb22a673 | ||
|
|
3ac9003298 | ||
|
|
e858bad3f8 | ||
|
|
9adcc8ec79 | ||
|
|
8eeb16c8da | ||
|
|
442ede95c2 | ||
|
|
3415ed975b | ||
|
|
7568f5bf05 | ||
|
|
5812466145 | ||
|
|
32bf6d685c | ||
|
|
823b2e9aab | ||
|
|
e44d6ec330 | ||
|
|
765c9eb85c | ||
|
|
ed05470732 | ||
|
|
1c87776470 | ||
|
|
fbaea583a5 | ||
|
|
6b5b493237 | ||
|
|
e6f3313d9a | ||
|
|
1fd771f818 | ||
|
|
f7a4092a29 | ||
|
|
db9fb216fd | ||
|
|
c9eef8079e | ||
|
|
396f87b46b | ||
|
|
30169bd258 | ||
|
|
c1e52fc807 | ||
|
|
0904bf9969 | ||
|
|
21483b259c | ||
|
|
8e00bbd939 | ||
|
|
5d02cba721 | ||
|
|
bf56a61b71 | ||
|
|
220cde1376 | ||
|
|
19f88290a2 | ||
|
|
47ca08240b | ||
|
|
99a83efc34 | ||
|
|
b755f52754 | ||
|
|
c31b604c9d | ||
|
|
2abd76eaa3 | ||
|
|
43811cdad8 | ||
|
|
4b59f9855e | ||
|
|
7fe73c1ee2 | ||
|
|
5811f2945c | ||
|
|
fb00584f4d | ||
|
|
5fad3ab047 | ||
|
|
3f30008224 | ||
|
|
703f053a7e | ||
|
|
1f002af06b | ||
|
|
610ac68635 | ||
|
|
60a7d6ac57 | ||
|
|
302b621b68 | ||
|
|
d0c6e27fee | ||
|
|
6478bf456b | ||
|
|
6b8853c027 | ||
|
|
97762f484f | ||
|
|
b32b55201e | ||
|
|
f45e7ca705 | ||
|
|
93b8d70209 | ||
|
|
47b1ededf3 | ||
|
|
7544de03c0 | ||
|
|
e585accd37 | ||
|
|
ba0e2d12da | ||
|
|
385f796c90 | ||
|
|
05b6588e39 | ||
|
|
ef92e30315 | ||
|
|
cb2b5c3738 | ||
|
|
851afe94f5 | ||
|
|
6941b81992 | ||
|
|
2b152dcf5e | ||
|
|
bbef570225 | ||
|
|
f689b32c33 | ||
|
|
d56a9cf73e | ||
|
|
308a4d5c93 | ||
|
|
3de12b7fde | ||
|
|
c35e3776dd | ||
|
|
b4a38a368a | ||
|
|
26096d9dd9 |
@@ -2,7 +2,7 @@ name: Check Required Labels
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, labeled, unlabeled]
|
||||
types: [opened, labeled, unlabeled, synchronize]
|
||||
|
||||
jobs:
|
||||
check-required-label:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Continuous Integration
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
- name: Test code
|
||||
# we're passing -short so that we skip the integration tests, which will be run in parallel below
|
||||
run: |
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
- name: Print git version
|
||||
run: git --version
|
||||
- name: Test code
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
- name: Build linux binary
|
||||
run: |
|
||||
GOOS=linux go build
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
- name: Check Vendor Directory
|
||||
# ensure our vendor directory matches up with our go modules
|
||||
run: |
|
||||
@@ -166,12 +166,12 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
# If you change this, make sure to also update scripts/golangci-lint-shim.sh
|
||||
version: v2.2.1
|
||||
version: v2.4.0
|
||||
- name: errors
|
||||
run: golangci-lint run
|
||||
if: ${{ failure() }}
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
|
||||
- name: Run goreleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
|
||||
@@ -28,3 +28,9 @@ __debug_bin*
|
||||
demo/output/*
|
||||
|
||||
coverage.out
|
||||
|
||||
# Nix
|
||||
result
|
||||
result-*
|
||||
.direnv
|
||||
.envrc
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
version: "2"
|
||||
run:
|
||||
go: "1.24"
|
||||
go: "1.25"
|
||||
linters:
|
||||
enable:
|
||||
- copyloopvar
|
||||
|
||||
@@ -60,6 +60,31 @@ To run lazygit from within the integrated terminal just go `go run main.go`
|
||||
|
||||
This allows you to contribute to Lazygit without needing to install anything on your local machine. The Codespace has all the necessary tools and extensions pre-installed.
|
||||
|
||||
## Using Nix for development
|
||||
|
||||
If you use Nix, you can leverage the included flake to set up a complete development environment with all necessary dependencies:
|
||||
|
||||
```sh
|
||||
nix develop
|
||||
```
|
||||
|
||||
This will drop you into a development shell that includes:
|
||||
* Latest Go toolchain
|
||||
* golangci-lint for code linting
|
||||
* git and make
|
||||
|
||||
You can also build and run lazygit using nix:
|
||||
|
||||
```sh
|
||||
# Build lazygit
|
||||
nix build
|
||||
|
||||
# Run lazygit directly
|
||||
nix run
|
||||
```
|
||||
|
||||
The nix flake supports multiple architectures (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin) and provides a consistent development environment across different systems.
|
||||
|
||||
## Code of conduct
|
||||
|
||||
Please note by participating in this project, you agree to abide by the [code of conduct].
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
# docker build -t lazygit .
|
||||
# docker run -it lazygit:latest /bin/sh
|
||||
|
||||
FROM golang:1.24 as build
|
||||
FROM golang:1.25 as build
|
||||
WORKDIR /go/src/github.com/jesseduffield/lazygit/
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
@@ -34,11 +34,9 @@ test: unit-test integration-test-all
|
||||
generate:
|
||||
go generate ./...
|
||||
|
||||
# If you execute `gofumpt -l -w .`, it will format all Go files in the current directory, including `test/_results/*` files.
|
||||
# We pass only Git-tracked Go files to gofumpt because we don't want to format the test results or get errors from it.
|
||||
.PHONY: format
|
||||
format:
|
||||
git ls-files '*.go' ':!vendor' | xargs gofumpt -l -w
|
||||
gofumpt -l -w .
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
(import (
|
||||
let
|
||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||
nodeName = lock.nodes.root.inputs.flake-compat;
|
||||
in
|
||||
fetchTarball {
|
||||
url =
|
||||
lock.nodes.${nodeName}.locked.url
|
||||
or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
|
||||
sha256 = lock.nodes.${nodeName}.locked.narHash;
|
||||
}
|
||||
) { src = ./.; }).defaultNix
|
||||
+134
-72
@@ -64,12 +64,15 @@ gui:
|
||||
# One of: 'margin' (default) | 'jump'
|
||||
scrollOffBehavior: margin
|
||||
|
||||
# The number of spaces per tab; used for everything that's shown in the main view, but probably mostly relevant for diffs.
|
||||
# Note that when using a pager, the pager has its own tab width setting, so you need to pass it separately in the pager command.
|
||||
# The number of spaces per tab; used for everything that's shown in the main
|
||||
# view, but probably mostly relevant for diffs.
|
||||
# Note that when using a pager, the pager has its own tab width setting, so you
|
||||
# need to pass it separately in the pager command.
|
||||
tabWidth: 4
|
||||
|
||||
# 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.
|
||||
# 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 amending a commit.
|
||||
@@ -81,45 +84,61 @@ gui:
|
||||
# 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.
|
||||
# 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.
|
||||
# If true, switch to a different worktree without confirmation when checking out
|
||||
# a branch that is checked out in that worktree
|
||||
skipSwitchWorktreeOnCheckoutWarning: 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.
|
||||
# If true, increase the height of the focused side window; creating an accordion
|
||||
# effect.
|
||||
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.
|
||||
# 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.
|
||||
# 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
|
||||
# - '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).
|
||||
# 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)
|
||||
# - '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
|
||||
|
||||
# If true, wrap lines in the staging view to the width of the view. This
|
||||
# makes it much easier to work with diffs that have long lines, e.g.
|
||||
# paragraphs of markdown text.
|
||||
# If true, wrap lines in the staging view to the width of the view. This makes
|
||||
# it much easier to work with diffs that have long lines, e.g. paragraphs of
|
||||
# markdown text.
|
||||
wrapLinesInStagingView: true
|
||||
|
||||
# If true, hunk selection mode will be enabled by default when entering the staging view.
|
||||
# If true, hunk selection mode will be enabled by default when entering the
|
||||
# staging view.
|
||||
useHunkModeInStagingView: true
|
||||
|
||||
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko'
|
||||
# | 'ru' | 'pt'
|
||||
language: auto
|
||||
|
||||
# Format used when displaying time e.g. commit time.
|
||||
@@ -192,11 +211,15 @@ gui:
|
||||
# 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.
|
||||
# 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, add a "/" root item in the file tree representing the root of the repository. It is only added when necessary, i.e. when there is more than one item at top level.
|
||||
# If true, add a "/" root item in the file tree representing the root of the
|
||||
# repository. It is only added when necessary, i.e. when there is more than one
|
||||
# item at top level.
|
||||
showRootItemInFileTree: true
|
||||
|
||||
# If true, show the number of lines changed per file in the Files view
|
||||
@@ -208,7 +231,9 @@ gui:
|
||||
# If true, show the command log
|
||||
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.
|
||||
# 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.
|
||||
@@ -219,10 +244,12 @@ gui:
|
||||
# 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.
|
||||
# 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.
|
||||
# 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.
|
||||
@@ -243,10 +270,12 @@ gui:
|
||||
|
||||
# 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
|
||||
# If 'auto', only split the main window when a file has both staged and unstaged
|
||||
# changes
|
||||
splitDiff: auto
|
||||
|
||||
# Default size for focused window. Can be changed from within Lazygit with '+' and '_' (but this won't change the default).
|
||||
# Default size for focused window. Can be changed from within Lazygit with '+'
|
||||
# and '_' (but this won't change the default).
|
||||
# One of: 'normal' (default) | 'half' | 'full'
|
||||
screenMode: normal
|
||||
|
||||
@@ -254,7 +283,8 @@ gui:
|
||||
# One of 'rounded' (default) | 'single' | 'double' | 'hidden' | 'bold'
|
||||
border: rounded
|
||||
|
||||
# If true, show a seriously epic explosion animation when nuking the working tree.
|
||||
# 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.
|
||||
@@ -287,27 +317,36 @@ gui:
|
||||
# If true, jump to the Files panel after applying a stash
|
||||
switchToFilesAfterStashApply: true
|
||||
|
||||
# If true, when using the panel jump keys (default 1 through 5) and target panel is already active, go to next tab instead
|
||||
# If true, when using the panel jump keys (default 1 through 5) and target panel
|
||||
# is already active, go to next tab instead
|
||||
switchTabsWithPanelJumpKeys: false
|
||||
|
||||
# Config relating to git
|
||||
git:
|
||||
# Array of pagers. Each entry has the following format:
|
||||
#
|
||||
# # 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: ""
|
||||
#
|
||||
# # e.g. 'difft --color=always'
|
||||
# externalDiffCommand: ""
|
||||
#
|
||||
# # If true, Lazygit will use git's `diff.external` config for paging.
|
||||
# # The advantage over `externalDiffCommand` is that this can be
|
||||
# # configured per file type in .gitattributes; see
|
||||
# # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
|
||||
# useExternalDiffGitConfig: false
|
||||
#
|
||||
# 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: ""
|
||||
|
||||
# e.g. 'difft --color=always'
|
||||
externalDiffCommand: ""
|
||||
|
||||
# If true, Lazygit will use git's `diff.external` config for paging. The advantage over `externalDiffCommand` is that this can be configured per file type in .gitattributes; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
|
||||
useExternalDiffGitConfig: false
|
||||
# for more information.
|
||||
pagers: []
|
||||
|
||||
# Config relating to committing
|
||||
commit:
|
||||
@@ -322,22 +361,26 @@ git:
|
||||
|
||||
# Config relating to merging
|
||||
merging:
|
||||
# If true, run merges in a subprocess so that if a commit message is required, Lazygit will not hang
|
||||
# If true, run merges in a subprocess so that if a commit message is required,
|
||||
# Lazygit will not hang
|
||||
# Only applicable to unix users.
|
||||
manualCommit: false
|
||||
|
||||
# Extra args passed to `git merge`, e.g. --no-ff
|
||||
args: ""
|
||||
|
||||
# The commit message to use for a squash merge commit. Can contain "{{selectedRef}}" and "{{currentBranch}}" placeholders.
|
||||
# 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
|
||||
# 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'
|
||||
# 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
|
||||
@@ -346,7 +389,10 @@ git:
|
||||
# If true, periodically refresh files and submodules
|
||||
autoRefresh: true
|
||||
|
||||
# If not "none", lazygit will automatically fast-forward local branches to match their upstream after fetching. Applies to branches that are not the currently checked out branch, and only to those that are strictly behind their upstream (as opposed to diverged).
|
||||
# If not "none", lazygit will automatically fast-forward local branches to match
|
||||
# their upstream after fetching. Applies to branches that are not the currently
|
||||
# checked out branch, and only to those that are strictly behind their upstream
|
||||
# (as opposed to diverged).
|
||||
# Possible values: 'none' | 'onlyMainBranches' | 'allBranches'
|
||||
autoForwardBranches: onlyMainBranches
|
||||
|
||||
@@ -354,25 +400,29 @@ git:
|
||||
fetchAll: true
|
||||
|
||||
# If true, lazygit will automatically stage files that used to have merge
|
||||
# conflicts but no longer do; and it will also ask you if you want to
|
||||
# continue a merge or rebase if you've resolved all conflicts. If false, it
|
||||
# won't do either of these things.
|
||||
# conflicts but no longer do; and it will also ask you if you want to continue a
|
||||
# merge or rebase if you've resolved all conflicts. If false, it won't do either
|
||||
# of these things.
|
||||
autoStageResolvedConflicts: true
|
||||
|
||||
# Command used when displaying the current branch git log in the main window
|
||||
branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --
|
||||
|
||||
# Commands used to display git log of all branches in the main window, they will be cycled in order of appearance (array of strings)
|
||||
# Commands used to display git log of all branches in the main window, they will
|
||||
# be cycled in order of appearance (array of strings)
|
||||
allBranchesLogCmds:
|
||||
- git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium
|
||||
|
||||
# If true, git diffs are rendered with the `--ignore-all-space` flag, which ignores whitespace changes. Can be toggled from within Lazygit with `<c-w>`.
|
||||
# If true, git diffs are rendered with the `--ignore-all-space` flag, which
|
||||
# ignores whitespace changes. Can be toggled from within Lazygit with `<c-w>`.
|
||||
ignoreWhitespaceInDiffView: false
|
||||
|
||||
# The number of lines of context to show around each diff hunk. Can be changed from within Lazygit with the `{` and `}` keys.
|
||||
# The number of lines of context to show around each diff hunk. Can be changed
|
||||
# from within Lazygit with the `{` and `}` keys.
|
||||
diffContextSize: 3
|
||||
|
||||
# The threshold for considering a file to be renamed, in percent. Can be changed from within Lazygit with the `(` and `)` keys.
|
||||
# The threshold for considering a file to be renamed, in percent. Can be changed
|
||||
# from within Lazygit with the `(` and `)` keys.
|
||||
renameSimilarityThreshold: 50
|
||||
|
||||
# If true, do not spawn a separate process when using GPG
|
||||
@@ -400,30 +450,35 @@ git:
|
||||
# 'topo-order' makes it easier to read the git log graph, but commits may not
|
||||
# appear chronologically. See https://git-scm.com/docs/
|
||||
#
|
||||
# Can be changed from within Lazygit with `Log menu -> Commit sort order` (`<c-l>` in the commits window by default).
|
||||
# Can be changed from within Lazygit 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'
|
||||
#
|
||||
# Can be toggled from within lazygit with `Log menu -> Show git graph` (`<c-l>` in the commits window by default).
|
||||
# Can be toggled from within lazygit 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`)
|
||||
# displays the whole git graph by default in the commits view (equivalent to
|
||||
# passing the `--all` argument to `git log`)
|
||||
showWholeGraph: false
|
||||
|
||||
# How branches are sorted in the local branches view.
|
||||
# One of: 'date' (default) | 'recency' | 'alphabetical'
|
||||
# Can be changed from within Lazygit with the Sort Order menu (`s`) in the branches panel.
|
||||
# Can be changed from within Lazygit with the Sort Order menu (`s`) in the
|
||||
# branches panel.
|
||||
localBranchSortOrder: date
|
||||
|
||||
# How branches are sorted in the remote branches view.
|
||||
# One of: 'date' (default) | 'alphabetical'
|
||||
# Can be changed from within Lazygit with the Sort Order menu (`s`) in the remote branches panel.
|
||||
# Can be changed from within Lazygit with the Sort Order menu (`s`) in the
|
||||
# remote branches panel.
|
||||
remoteBranchSortOrder: date
|
||||
|
||||
# When copying commit hashes to the clipboard, truncate them to this
|
||||
# length. Set to 40 to disable truncation.
|
||||
# When copying commit hashes to the clipboard, truncate them to this length. Set
|
||||
# to 40 to disable truncation.
|
||||
truncateCopiedCommitHashesTo: 12
|
||||
|
||||
# Periodic update checks
|
||||
@@ -447,10 +502,12 @@ refresher:
|
||||
# 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
|
||||
# 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
|
||||
# Config relating to things outside of Lazygit like how files are opened,
|
||||
# copying to clipboard, etc
|
||||
os:
|
||||
# Command for editing a file. Should contain "{{filename}}".
|
||||
edit: ""
|
||||
@@ -459,8 +516,8 @@ os:
|
||||
# "{{filename}}", and may optionally contain "{{line}}".
|
||||
editAtLine: ""
|
||||
|
||||
# Same as EditAtLine, except that the command needs to wait until the
|
||||
# window is closed.
|
||||
# Same as EditAtLine, except that the command needs to wait until the window is
|
||||
# closed.
|
||||
editAtLineAndWait: ""
|
||||
|
||||
# Whether lazygit suspends until an edit process returns
|
||||
@@ -469,12 +526,12 @@ os:
|
||||
# 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.
|
||||
# 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}}".
|
||||
# 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}}".
|
||||
@@ -488,7 +545,9 @@ os:
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
|
||||
readFromClipboardCmd: ""
|
||||
|
||||
# A shell startup file containing shell aliases or shell functions. This will be sourced before running any shell commands, so that shell functions are available in the `:` command prompt or even in custom commands.
|
||||
# A shell startup file containing shell aliases or shell functions. This will be
|
||||
# sourced before running any shell commands, so that shell functions are
|
||||
# available in the `:` command prompt or even in custom commands.
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands
|
||||
shellFunctionsFile: ""
|
||||
|
||||
@@ -503,13 +562,15 @@ customCommands: []
|
||||
services: {}
|
||||
|
||||
# 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
|
||||
# - '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.
|
||||
# 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
|
||||
@@ -585,6 +646,7 @@ keybinding:
|
||||
prevTab: '['
|
||||
nextScreenMode: +
|
||||
prevScreenMode: _
|
||||
cyclePagers: '|'
|
||||
undo: z
|
||||
redo: Z
|
||||
filteringMenu: <c-s>
|
||||
@@ -619,7 +681,7 @@ keybinding:
|
||||
viewResetOptions: D
|
||||
fetch: f
|
||||
toggleTreeView: '`'
|
||||
openMergeTool: M
|
||||
openMergeOptions: M
|
||||
openStatusFilter: <c-b>
|
||||
copyFileInfoToClipboard: "y"
|
||||
collapseAll: '-'
|
||||
|
||||
+50
-20
@@ -2,25 +2,29 @@
|
||||
|
||||
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `e` in the Status panel).
|
||||
|
||||
Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support.
|
||||
Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support. However, see [below](#emulating-custom-pagers-on-windows) for a workaround.
|
||||
|
||||
## Default:
|
||||
Multiple pagers are supported; you can cycle through them with the `|` key. This can be useful if you usually prefer a particular pager, but want to use a different one for certain kinds of diffs.
|
||||
|
||||
Pagers are configured with the `pagers` array in the git section; here's an example for a multi-pager setup:
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
colorArg: always
|
||||
pagers:
|
||||
- pager: delta --dark --paging=never
|
||||
- pager: ydiff -p cat -s --wrap --width={{columnWidth}}
|
||||
colorArg: never
|
||||
- externalDiffCommand: difft --color=always
|
||||
```
|
||||
|
||||
the `colorArg` key is for whether you want the `--color=always` arg in your `git diff` command. Some pagers want it set to `always`, others want it set to `never`.
|
||||
The `colorArg` key is for whether you want the `--color=always` arg in your `git diff` command. Some pagers want it set to `always`, others want it set to `never`. The default is `always`, since that's what most pagers need.
|
||||
|
||||
## Delta:
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
colorArg: always
|
||||
pager: delta --dark --paging=never
|
||||
pagers:
|
||||
- pager: delta --dark --paging=never
|
||||
```
|
||||
|
||||

|
||||
@@ -31,9 +35,8 @@ A cool feature of delta is --hyperlinks, which renders clickable links for the l
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
colorArg: always
|
||||
pager: diff-so-fancy
|
||||
pagers:
|
||||
- pager: diff-so-fancy
|
||||
```
|
||||
|
||||

|
||||
@@ -44,9 +47,9 @@ git:
|
||||
gui:
|
||||
sidePanelWidth: 0.2 # gives you more space to show things side-by-side
|
||||
git:
|
||||
paging:
|
||||
colorArg: never
|
||||
pager: ydiff -p cat -s --wrap --width={{columnWidth}}
|
||||
pagers:
|
||||
- colorArg: never
|
||||
pager: ydiff -p cat -s --wrap --width={{columnWidth}}
|
||||
```
|
||||
|
||||

|
||||
@@ -61,8 +64,8 @@ These can be used in lazygit by using the `externalDiffCommand` config; in the c
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
externalDiffCommand: difft --color=always
|
||||
pagers:
|
||||
- externalDiffCommand: difft --color=always
|
||||
```
|
||||
|
||||
The `colorArg` and `pager` options are not used in this case.
|
||||
@@ -71,16 +74,43 @@ You can add whatever extra arguments you prefer for your difftool; for instance
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off
|
||||
pagers:
|
||||
- externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off
|
||||
```
|
||||
|
||||
Instead of setting this command in lazygit's `externalDiffCommand` config, you can also tell lazygit to use the external diff command that is configured in git itself (`diff.external`), by using
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
useExternalDiffGitConfig: true
|
||||
pagers:
|
||||
- useExternalDiffGitConfig: true
|
||||
```
|
||||
|
||||
This can be useful if you also want to use it for diffs on the command line, and it also has the advantage that you can configure it per file type in `.gitattributes`; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
|
||||
|
||||
## Emulating custom pagers on Windows
|
||||
|
||||
There is a trick to emulate custom pagers on Windows using a Powershell script configured as an external diff command. It's not perfect, but certainly better than nothing. To do this, save the following script as `lazygit-pager.ps1` at a convenient place on your disk:
|
||||
|
||||
```pwsh
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
$old = $args[1].Replace('\', '/')
|
||||
$new = $args[4].Replace('\', '/')
|
||||
$path = $args[0]
|
||||
git diff --no-index --no-ext-diff $old $new
|
||||
| %{ $_.Replace($old, $path).Replace($new, $path) }
|
||||
| delta --width=$env:LAZYGIT_COLUMNS
|
||||
```
|
||||
|
||||
Use the pager of your choice with the arguments you like in the last line of the script. Personally I wouldn't want to use lazygit anymore without delta's `--hyperlinks --hyperlinks-file-link-format="lazygit-edit://{path}:{line}"` args, see [above](#delta).
|
||||
|
||||
In your lazygit config, use
|
||||
|
||||
```yml
|
||||
git:
|
||||
pagers:
|
||||
- externalDiffCommand: "C:/wherever/lazygit-pager.ps1"
|
||||
```
|
||||
|
||||
The main limitation of this approach compared to a "real" pager is that renames are not displayed correctly; they are shown as if they were modifications of the old file. (This affects only the hunk headers; the diff itself is always correct.)
|
||||
|
||||
@@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` R `` | Refresh | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
| `` + `` | Next screen mode (normal/half/fullscreen) | |
|
||||
| `` _ `` | Prev screen mode | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | Cancel | |
|
||||
| `` ? `` | Open keybindings menu | |
|
||||
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
@@ -153,7 +154,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` ` `` | Toggle file tree view | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` M `` | Open external merge tool | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | Fetch | Fetch changes from remote. |
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
@@ -210,7 +211,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` z `` | Undo | Undo last merge conflict resolution. |
|
||||
| `` e `` | Edit file | Open file in external editor. |
|
||||
| `` o `` | Open file | Open file in default application. |
|
||||
| `` M `` | Open external merge tool | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | Return to files panel | |
|
||||
|
||||
## Main panel (normal)
|
||||
|
||||
@@ -24,6 +24,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` R `` | 更新 | Gitの状態を更新します(`git status`、`git branch`などをバックグラウンドで実行してパネルの内容を更新します)。これは`git fetch`を実行しません。 |
|
||||
| `` + `` | 次の画面モード(通常/半分/全画面) | |
|
||||
| `` _ `` | 前の画面モード | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | キャンセル | |
|
||||
| `` ? `` | キーバインディングメニューを開く | |
|
||||
| `` <c-s> `` | フィルターオプションを表示 | コミットログのフィルタリングオプションを表示し、フィルタに一致するコミットのみを表示します。 |
|
||||
@@ -235,7 +236,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` D `` | リセット | 作業ツリーのリセットオプション(例:作業ツリーの完全破棄)を表示します。 |
|
||||
| `` ` `` | ファイルツリービューを切り替え | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
|
||||
| `` M `` | 外部マージツールを開く | `git mergetool`を実行します。 |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | フェッチ | リモートから変更をフェッチします。 |
|
||||
| `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます |
|
||||
| `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します |
|
||||
@@ -292,7 +293,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` z `` | 元に戻す | 最後のマージコンフリクト解決を元に戻します。 |
|
||||
| `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 |
|
||||
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
|
||||
| `` M `` | 外部マージツールを開く | `git mergetool`を実行します。 |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | ファイルパネルに戻る | |
|
||||
|
||||
## メインパネル(通常)
|
||||
|
||||
@@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` R `` | 새로고침 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
| `` + `` | 다음 스크린 모드 (normal/half/fullscreen) | |
|
||||
| `` _ `` | 이전 스크린 모드 | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | 취소 | |
|
||||
| `` ? `` | 매뉴 열기 | |
|
||||
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
@@ -152,7 +153,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` z `` | 되돌리기 | Undo last merge conflict resolution. |
|
||||
| `` e `` | 파일 편집 | Open file in external editor. |
|
||||
| `` o `` | 파일 닫기 | Open file in default application. |
|
||||
| `` M `` | Git mergetool를 열기 | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | 파일 목록으로 돌아가기 | |
|
||||
|
||||
## 메인 패널 (Normal)
|
||||
@@ -196,7 +197,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <esc> `` | 파일 목록으로 돌아가기 | |
|
||||
| `` <tab> `` | 패널 전환 | Switch to other view (staged/unstaged changes). |
|
||||
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
|
||||
| `` c `` | 커밋 변경내용 | Commit staged changes. |
|
||||
| `` c `` | 커밋 변경내용 | 스테이징된 변경 사항 커밋. |
|
||||
| `` w `` | Commit changes without pre-commit hook | |
|
||||
| `` C `` | Git 편집기를 사용하여 변경 내용을 커밋합니다. | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
@@ -378,7 +379,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <space> `` | Staged 전환 | Toggle staged for selected file. |
|
||||
| `` <c-b> `` | 파일을 필터하기 (Staged/unstaged) | |
|
||||
| `` y `` | 클립보드에 복사 | |
|
||||
| `` c `` | 커밋 변경내용 | Commit staged changes. |
|
||||
| `` c `` | 커밋 변경내용 | 스테이징된 변경 사항 커밋. |
|
||||
| `` w `` | Commit changes without pre-commit hook | |
|
||||
| `` A `` | 마지맛 커밋 수정 | |
|
||||
| `` C `` | Git 편집기를 사용하여 변경 내용을 커밋합니다. | |
|
||||
@@ -396,7 +397,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` D `` | 초기화 | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` ` `` | 파일 트리뷰로 전환 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` M `` | Git mergetool를 열기 | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | Fetch | Fetch changes from remote. |
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
|
||||
@@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` R `` | Verversen | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
| `` + `` | Volgende scherm modus (normaal/half/groot) | |
|
||||
| `` _ `` | Vorige scherm modus | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | Annuleren | |
|
||||
| `` ? `` | Open menu | |
|
||||
| `` <c-s> `` | Bekijk scoping opties | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
@@ -78,7 +79,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` ` `` | Toggle bestandsboom weergave | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` M `` | Open external merge tool | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | Fetch | Fetch changes from remote. |
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
@@ -218,7 +219,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` z `` | Ongedaan maken | Undo last merge conflict resolution. |
|
||||
| `` e `` | Verander bestand | Open file in external editor. |
|
||||
| `` o `` | Open bestand | Open file in default application. |
|
||||
| `` M `` | Open external merge tool | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | Ga terug naar het bestanden paneel | |
|
||||
|
||||
## Normaal
|
||||
|
||||
@@ -24,6 +24,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` R `` | Odśwież | Odśwież stan git (tj. uruchom `git status`, `git branch`, itp. w tle, aby zaktualizować zawartość paneli). To nie uruchamia `git fetch`. |
|
||||
| `` + `` | Następny tryb ekranu (normalny/półpełny/pełnoekranowy) | |
|
||||
| `` _ `` | Poprzedni tryb ekranu | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | Anuluj | |
|
||||
| `` ? `` | Otwórz menu przypisań klawiszy | |
|
||||
| `` <c-s> `` | Pokaż opcje filtrowania | Pokaż opcje filtrowania dziennika commitów, tak aby pokazywane były tylko commity pasujące do filtra. |
|
||||
@@ -193,7 +194,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` 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`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | Wróć do panelu plików | |
|
||||
|
||||
## Panel główny (zatwierdzanie)
|
||||
@@ -252,7 +253,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` D `` | Reset | Wyświetl opcje resetu dla drzewa roboczego (np. zniszczenie drzewa roboczego). |
|
||||
| `` ` `` | Przełącz widok drzewa plików | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
|
||||
| `` M `` | Otwórz zewnętrzne narzędzie scalania | Uruchom `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | Pobierz | Pobierz zmiany ze zdalnego serwera. |
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
|
||||
@@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` R `` | Atualizar | Atualize o estado do git (ou seja, execute `git status`, `git branch`, etc em segundo plano para atualizar o conteúdo de painéis). Isso não executa `git fetch`. |
|
||||
| `` + `` | Next screen mode (normal/half/fullscreen) | |
|
||||
| `` _ `` | Prev screen mode | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | Cancelar | |
|
||||
| `` ? `` | Open keybindings menu | |
|
||||
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
@@ -78,7 +79,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` D `` | Restaurar | Opções de redefinição de exibição para árvore de trabalho (por exemplo, nukando a árvore de trabalho). |
|
||||
| `` ` `` | Alternar exibição de árvore de arquivo | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
|
||||
| `` M `` | Abrir ferramenta de merge externa | Execute `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | Buscar | Buscar alterações do controle remoto. |
|
||||
| `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
|
||||
| `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
|
||||
@@ -278,7 +279,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` z `` | Desfazer | Desfazer resolução de conflitos de última mesclagem. |
|
||||
| `` e `` | Editar arquivo | Abrir arquivo no editor externo. |
|
||||
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
|
||||
| `` M `` | Abrir ferramenta de merge externa | Execute `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | Retornar ao painel de arquivos | |
|
||||
|
||||
## Painel principal (patch build)
|
||||
|
||||
@@ -24,6 +24,7 @@ _Связки клавиш_
|
||||
| `` R `` | Обновить | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
| `` + `` | Следующий режим экрана (нормальный/полуэкранный/полноэкранный) | |
|
||||
| `` _ `` | Предыдущий режим экрана | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | Отменить | |
|
||||
| `` ? `` | Открыть меню | |
|
||||
| `` <c-s> `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
@@ -122,7 +123,7 @@ _Связки клавиш_
|
||||
| `` z `` | Отменить | Undo last merge conflict resolution. |
|
||||
| `` e `` | Редактировать файл | Open file in external editor. |
|
||||
| `` o `` | Открыть файл | Open file in default application. |
|
||||
| `` M `` | Открыть внешний инструмент слияния (git mergetool) | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | Вернуться к панели файлов | |
|
||||
|
||||
## Главная панель (сборка патчей)
|
||||
@@ -152,7 +153,7 @@ _Связки клавиш_
|
||||
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
|
||||
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов | |
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную \| cherry-picked) выборку коммитов | |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` * `` | Select commits of current branch | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
@@ -165,7 +166,7 @@ _Связки клавиш_
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов | |
|
||||
| `` <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. |
|
||||
@@ -258,7 +259,7 @@ _Связки клавиш_
|
||||
| `` N `` | Move commits to new branch | Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.<br><br>Note that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which). |
|
||||
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
|
||||
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов | |
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную \| cherry-picked) выборку коммитов | |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` * `` | Select commits of current branch | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
@@ -390,7 +391,7 @@ _Связки клавиш_
|
||||
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` ` `` | Переключить вид дерева файлов | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
| `` M `` | Открыть внешний инструмент слияния (git mergetool) | Run `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | Получить изменения | Fetch changes from remote. |
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
|
||||
@@ -24,6 +24,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` R `` | 刷新 | 刷新git状态(即在后台上运行`git status`,`git branch`等命令以更新面板内容) 不会运行`git fetch` |
|
||||
| `` + `` | 下一屏模式(正常/半屏/全屏) | |
|
||||
| `` _ `` | 上一屏模式 | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | 取消 | |
|
||||
| `` ? `` | 打开菜单 | |
|
||||
| `` <c-s> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 |
|
||||
@@ -216,7 +217,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` D `` | 重置 | 查看工作树的重置选项(例如:清除工作树)。 |
|
||||
| `` ` `` | 切换文件树视图 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | 抓取 | 从远程获取变更 |
|
||||
| `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
|
||||
| `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
|
||||
@@ -305,7 +306,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` z `` | 撤销 | 撤消上次合并冲突解决 |
|
||||
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | 返回文件面板 | |
|
||||
|
||||
## 正在暂存
|
||||
|
||||
@@ -24,6 +24,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` R `` | 重新整理 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
|
||||
| `` + `` | 下一個螢幕模式(常規/半螢幕/全螢幕) | |
|
||||
| `` _ `` | 上一個螢幕模式 | |
|
||||
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
|
||||
| `` <esc> `` | 取消 | |
|
||||
| `` ? `` | 開啟選單 | |
|
||||
| `` <c-s> `` | 檢視篩選路徑選項 | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
@@ -97,7 +98,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` z `` | 復原 | Undo last merge conflict resolution. |
|
||||
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` <esc> `` | 返回檔案面板 | |
|
||||
|
||||
## 主面板(預存)
|
||||
@@ -347,7 +348,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` D `` | 重設 | View reset options for working tree (e.g. nuking the working tree). |
|
||||
| `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
|
||||
| `` M `` | View merge conflict options | View options for resolving merge conflicts. |
|
||||
| `` f `` | 擷取 | 同步遠端異動 |
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
|
||||
Generated
+127
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"revCount": 69,
|
||||
"type": "tarball",
|
||||
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.1.0/01948eb7-9cba-704f-bbf3-3fa956735b52/source.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759362264,
|
||||
"narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "758cf7296bee11f1706a574c77d072b8a7baa881",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1759831965,
|
||||
"narHash": "sha256-vgPm2xjOmKdZ0xKA6yLXPJpjOtQPHfaZDRtH+47XEBo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c9b6fb798541223bbb396d287d16f43520250518",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1754788789,
|
||||
"narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "a73b9c743612e4244d865a2fdee11865283c04e6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1754340878,
|
||||
"narHash": "sha256-lgmUyVQL9tSnvvIvBp7x1euhkkCho7n3TMzgjdvgPoU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cab778239e705082fe97bb4990e0d24c50924c04",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758728421,
|
||||
"narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
description = "A simple terminal UI for git commands";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
systems.url = "github:nix-systems/default";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz";
|
||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||
};
|
||||
|
||||
outputs =
|
||||
inputs@{ flake-parts, systems, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
systems = import systems;
|
||||
imports = [
|
||||
inputs.treefmt-nix.flakeModule
|
||||
];
|
||||
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
let
|
||||
goMod = builtins.readFile ./go.mod;
|
||||
versionMatch = builtins.match ".*go[[:space:]]([0-9]+\\.[0-9]+)(\\.[0-9]+)?.*" goMod;
|
||||
|
||||
goVersion =
|
||||
if versionMatch != null then
|
||||
builtins.head versionMatch
|
||||
else
|
||||
throw "Could not extract Go version from go.mod";
|
||||
|
||||
goOverlay = final: prev: {
|
||||
go = prev."go_${builtins.replaceStrings [ "." ] [ "_" ] goVersion}";
|
||||
};
|
||||
|
||||
lazygit = pkgs.buildGoModule rec {
|
||||
pname = "lazygit";
|
||||
version = "dev";
|
||||
|
||||
gitCommit = inputs.self.rev or inputs.self.dirtyRev or "dev";
|
||||
|
||||
src = ./.;
|
||||
vendorHash = null;
|
||||
|
||||
# Disable integration tests that require specific environment
|
||||
doCheck = false;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
git
|
||||
makeWrapper
|
||||
];
|
||||
buildInputs = [ pkgs.git ];
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
"-X main.commit=${gitCommit}"
|
||||
"-X main.version=${version}"
|
||||
"-X main.buildSource=nix"
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
wrapProgram $out/bin/lazygit \
|
||||
--prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.git ]}
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "A simple terminal UI for git commands";
|
||||
homepage = "https://github.com/jesseduffield/lazygit";
|
||||
license = pkgs.lib.licenses.mit;
|
||||
maintainers = [ "jesseduffield" ];
|
||||
platforms = pkgs.lib.platforms.unix;
|
||||
mainProgram = "lazygit";
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
_module.args.pkgs = import inputs.nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ goOverlay ];
|
||||
config = { };
|
||||
};
|
||||
|
||||
packages = {
|
||||
default = lazygit;
|
||||
inherit lazygit;
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
name = "lazygit-dev";
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
# Go toolchain
|
||||
go
|
||||
gotools
|
||||
|
||||
# Development tools
|
||||
git
|
||||
gnumake
|
||||
];
|
||||
|
||||
# Environment variables for development
|
||||
CGO_ENABLED = "0";
|
||||
};
|
||||
|
||||
treefmt = {
|
||||
programs.nixfmt.enable = pkgs.lib.meta.availableOn pkgs.stdenv.buildPlatform pkgs.nixfmt-rfc-style.compiler;
|
||||
programs.nixfmt.package = pkgs.nixfmt-rfc-style;
|
||||
programs.gofmt.enable = true;
|
||||
};
|
||||
|
||||
checks.build = lazygit;
|
||||
};
|
||||
|
||||
flake = {
|
||||
overlays.default = final: prev: {
|
||||
lazygit = inputs.self.packages.${final.system}.lazygit;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
module github.com/jesseduffield/lazygit
|
||||
|
||||
go 1.24.0
|
||||
go 1.25.0
|
||||
|
||||
// This is necessary to ignore test files when executing gofumpt.
|
||||
ignore ./test
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1
|
||||
@@ -9,26 +12,26 @@ 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.8.1
|
||||
github.com/gdamore/tcell/v2 v2.9.0
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c
|
||||
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250711082438-4aa4fd0b4d22
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
|
||||
github.com/kyokomi/emoji/v2 v2.2.8
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/mattn/go-runewidth v0.0.16
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0
|
||||
github.com/mattn/go-runewidth v0.0.19
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
github.com/samber/lo v1.31.0
|
||||
github.com/sanity-io/litter v1.5.2
|
||||
github.com/sasha-s/go-deadlock v0.3.5
|
||||
github.com/sasha-s/go-deadlock v0.3.6
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/afero v1.9.5
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
@@ -36,8 +39,8 @@ require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/sys v0.34.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/sys v0.36.0
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -47,6 +50,7 @@ require (
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@@ -68,18 +72,17 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
|
||||
@@ -65,6 +65,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
||||
@@ -96,8 +98,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
|
||||
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
|
||||
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
|
||||
github.com/gdamore/tcell/v2 v2.8.0/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
|
||||
github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU=
|
||||
github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
|
||||
github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys=
|
||||
github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
@@ -194,8 +196,8 @@ github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c h1:tC2Paiis
|
||||
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c/go.mod h1:F2fEBk0ddf6ixrBrJjY7phfQ3hL9rXG0uSjvwYe50bE=
|
||||
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd h1:ViKj6qth8FgcIWizn9KiACWwPemWSymx62OPN0tHT+Q=
|
||||
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd/go.mod h1:lRhCiBr6XjQrvcQVa+UYsy/99d3wMXn/a0nSQlhnhlA=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250711082438-4aa4fd0b4d22 h1:vhMwEsLlMtuKKo9/z3Qcggycgad8oV7+siwOZEnJDOs=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250711082438-4aa4fd0b4d22/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a h1:z3NQvFXAWGT4B/MwQBZc+1ej8WJ/Nv35xngQRvwzPuI=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
|
||||
github.com/jesseduffield/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=
|
||||
@@ -224,8 +226,9 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -237,8 +240,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
@@ -247,8 +251,8 @@ github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE=
|
||||
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -260,7 +264,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
@@ -271,8 +274,8 @@ github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
|
||||
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
|
||||
github.com/sanity-io/litter v1.5.2 h1:AnC8s9BMORWH5a4atZ4D6FPVvKGzHcnc5/IVTa87myw=
|
||||
github.com/sanity-io/litter v1.5.2/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0=
|
||||
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
|
||||
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
|
||||
github.com/sasha-s/go-deadlock v0.3.6 h1:TR7sfOnZ7x00tWPfD397Peodt57KzMDo+9Ae9rMiUmw=
|
||||
github.com/sasha-s/go-deadlock v0.3.6/go.mod h1:CUqNyyvMxTyjFqDT7MRg9mb4Dv/btmGTqSR+rky/UXo=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
@@ -434,8 +437,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -488,8 +491,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/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=
|
||||
@@ -499,8 +502,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/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=
|
||||
@@ -515,8 +518,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -125,8 +125,13 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp(getTempDirBase(), "lazygit-*")
|
||||
tempDirBase := getTempDirBase()
|
||||
tempDir, err := os.MkdirTemp(tempDirBase, "lazygit-*")
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
log.Fatalf("Your temp directory (%s) is not writeable. Try if rebooting your machine fixes this.", tempDirBase)
|
||||
}
|
||||
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
@@ -225,6 +225,11 @@ func formatBinding(binding *types.Binding) string {
|
||||
// Replace newlines with <br> tags for proper markdown table formatting
|
||||
tooltip := strings.ReplaceAll(binding.Tooltip, "\n", "<br>")
|
||||
|
||||
// Escape pipe characters to avoid breaking the table format
|
||||
action = strings.ReplaceAll(action, `|`, `\|`)
|
||||
description = strings.ReplaceAll(description, `|`, `\|`)
|
||||
tooltip = strings.ReplaceAll(tooltip, `|`, `\|`)
|
||||
|
||||
// Use backticks for keyboard keys. Two backticks are needed with an inner space
|
||||
// to escape a key that is itself a backtick.
|
||||
return fmt.Sprintf("| `` %s `` | %s | %s |\n", action, description, tooltip)
|
||||
|
||||
+5
-1
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -59,6 +60,7 @@ func NewGitCommand(
|
||||
version *git_commands.GitVersion,
|
||||
osCommand *oscommands.OSCommand,
|
||||
gitConfig git_config.IGitConfig,
|
||||
pagerConfig *config.PagerConfig,
|
||||
) (*GitCommand, error) {
|
||||
repoPaths, err := git_commands.GetRepoPaths(osCommand.Cmd, version)
|
||||
if err != nil {
|
||||
@@ -88,6 +90,7 @@ func NewGitCommand(
|
||||
gitConfig,
|
||||
repoPaths,
|
||||
repository,
|
||||
pagerConfig,
|
||||
), nil
|
||||
}
|
||||
|
||||
@@ -98,6 +101,7 @@ func NewGitCommandAux(
|
||||
gitConfig git_config.IGitConfig,
|
||||
repoPaths *git_commands.RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
pagerConfig *config.PagerConfig,
|
||||
) *GitCommand {
|
||||
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd)
|
||||
|
||||
@@ -108,7 +112,7 @@ func NewGitCommandAux(
|
||||
// common ones are: cmn, osCommand, dotGitDir, configCommands
|
||||
configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo)
|
||||
|
||||
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands)
|
||||
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands, pagerConfig)
|
||||
|
||||
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
|
||||
statusCommands := git_commands.NewStatusCommands(gitCommon)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
type BranchCommands struct {
|
||||
*GitCommon
|
||||
allBranchesLogCmdIndex uint8 // keeps track of current all branches log command
|
||||
allBranchesLogCmdIndex int // keeps track of current all branches log command
|
||||
}
|
||||
|
||||
func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
|
||||
@@ -116,6 +116,22 @@ func (self *BranchCommands) CurrentBranchName() (string, error) {
|
||||
return strings.TrimSpace(output), nil
|
||||
}
|
||||
|
||||
// Gets the full ref name of the previously checked out branch. Can return an empty string (but no
|
||||
// error) e.g. when the previously checked out thing was a detached head.
|
||||
func (self *BranchCommands) PreviousRef() (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-parse").
|
||||
Arg("--symbolic-full-name").
|
||||
Arg("@{-1}").
|
||||
ToArgv()
|
||||
|
||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(output), nil
|
||||
}
|
||||
|
||||
// LocalDelete delete branch locally
|
||||
func (self *BranchCommands) LocalDelete(branches []string, force bool) error {
|
||||
cmdArgs := NewGitCmd("branch").
|
||||
@@ -234,26 +250,51 @@ func (self *BranchCommands) Rename(oldName string, newName string) error {
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
type MergeOpts struct {
|
||||
FastForwardOnly bool
|
||||
Squash bool
|
||||
}
|
||||
type MergeVariant int
|
||||
|
||||
const (
|
||||
MERGE_VARIANT_REGULAR MergeVariant = iota
|
||||
MERGE_VARIANT_FAST_FORWARD
|
||||
MERGE_VARIANT_NON_FAST_FORWARD
|
||||
MERGE_VARIANT_SQUASH
|
||||
)
|
||||
|
||||
func (self *BranchCommands) Merge(branchName string, variant MergeVariant) error {
|
||||
extraArgs := func() []string {
|
||||
switch variant {
|
||||
case MERGE_VARIANT_REGULAR:
|
||||
return []string{}
|
||||
case MERGE_VARIANT_FAST_FORWARD:
|
||||
return []string{"--ff"}
|
||||
case MERGE_VARIANT_NON_FAST_FORWARD:
|
||||
return []string{"--no-ff"}
|
||||
case MERGE_VARIANT_SQUASH:
|
||||
return []string{"--squash", "--ff"}
|
||||
}
|
||||
|
||||
panic("shouldn't get here")
|
||||
}()
|
||||
|
||||
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(extraArgs...).
|
||||
Arg(branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
// Returns whether refName can be fast-forward merged into the current branch
|
||||
func (self *BranchCommands) CanDoFastForwardMerge(refName string) bool {
|
||||
cmdArgs := NewGitCmd("merge-base").
|
||||
Arg("--is-ancestor").
|
||||
Arg("HEAD", refName).
|
||||
ToArgv()
|
||||
err := self.cmd.New(cmdArgs).DontLog().Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Only choose between non-empty, non-identical commands
|
||||
func (self *BranchCommands) allBranchesLogCandidates() []string {
|
||||
return lo.Uniq(lo.WithoutEmpty(self.UserConfig().Git.AllBranchesLogCmds))
|
||||
@@ -262,6 +303,10 @@ func (self *BranchCommands) allBranchesLogCandidates() []string {
|
||||
func (self *BranchCommands) AllBranchesLogCmdObj() *oscommands.CmdObj {
|
||||
candidates := self.allBranchesLogCandidates()
|
||||
|
||||
if self.allBranchesLogCmdIndex >= len(candidates) {
|
||||
self.allBranchesLogCmdIndex = 0
|
||||
}
|
||||
|
||||
i := self.allBranchesLogCmdIndex
|
||||
return self.cmd.New(str.ToArgv(candidates[i])).DontLog()
|
||||
}
|
||||
@@ -269,7 +314,13 @@ func (self *BranchCommands) AllBranchesLogCmdObj() *oscommands.CmdObj {
|
||||
func (self *BranchCommands) RotateAllBranchesLogIdx() {
|
||||
n := len(self.allBranchesLogCandidates())
|
||||
i := self.allBranchesLogCmdIndex
|
||||
self.allBranchesLogCmdIndex = uint8((int(i) + 1) % n)
|
||||
self.allBranchesLogCmdIndex = (i + 1) % n
|
||||
}
|
||||
|
||||
func (self *BranchCommands) GetAllBranchesLogIdxAndCount() (int, int) {
|
||||
n := len(self.allBranchesLogCandidates())
|
||||
i := self.allBranchesLogCmdIndex
|
||||
return i, n
|
||||
}
|
||||
|
||||
func (self *BranchCommands) IsBranchMerged(branch *models.Branch, mainBranches *MainBranches) (bool, error) {
|
||||
@@ -288,7 +339,7 @@ func (self *BranchCommands) IsBranchMerged(branch *models.Branch, mainBranches *
|
||||
Arg("--").
|
||||
ToArgv()
|
||||
|
||||
stdout, _, err := self.cmd.New(cmdArgs).RunWithOutputs()
|
||||
stdout, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -122,14 +122,14 @@ func TestBranchMerge(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
userConfig *config.UserConfig
|
||||
opts MergeOpts
|
||||
variant MergeVariant
|
||||
branchName string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
testName: "basic",
|
||||
userConfig: &config.UserConfig{},
|
||||
opts: MergeOpts{},
|
||||
variant: MERGE_VARIANT_REGULAR,
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "mybranch"},
|
||||
},
|
||||
@@ -142,7 +142,7 @@ func TestBranchMerge(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
opts: MergeOpts{},
|
||||
variant: MERGE_VARIANT_REGULAR,
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "--merging-args", "mybranch"},
|
||||
},
|
||||
@@ -155,16 +155,30 @@ func TestBranchMerge(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
opts: MergeOpts{},
|
||||
variant: MERGE_VARIANT_REGULAR,
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "--arg1", "--arg2", "mybranch"},
|
||||
},
|
||||
{
|
||||
testName: "fast forward only",
|
||||
testName: "fast-forward merge",
|
||||
userConfig: &config.UserConfig{},
|
||||
opts: MergeOpts{FastForwardOnly: true},
|
||||
variant: MERGE_VARIANT_FAST_FORWARD,
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "--ff-only", "mybranch"},
|
||||
expected: []string{"merge", "--no-edit", "--ff", "mybranch"},
|
||||
},
|
||||
{
|
||||
testName: "non-fast-forward merge",
|
||||
userConfig: &config.UserConfig{},
|
||||
variant: MERGE_VARIANT_NON_FAST_FORWARD,
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "--no-ff", "mybranch"},
|
||||
},
|
||||
{
|
||||
testName: "squash merge",
|
||||
userConfig: &config.UserConfig{},
|
||||
variant: MERGE_VARIANT_SQUASH,
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "--squash", "--ff", "mybranch"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -174,7 +188,7 @@ func TestBranchMerge(t *testing.T) {
|
||||
ExpectGitArgs(s.expected, "", nil)
|
||||
instance := buildBranchCommands(commonDeps{runner: runner, userConfig: s.userConfig})
|
||||
|
||||
assert.NoError(t, instance.Merge(s.branchName, s.opts))
|
||||
assert.NoError(t, instance.Merge(s.branchName, s.variant))
|
||||
runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -256,14 +256,14 @@ func (self *CommitCommands) AmendHeadCmdObj() *oscommands.CmdObj {
|
||||
func (self *CommitCommands) ShowCmdObj(hash string, filterPaths []string) *oscommands.CmdObj {
|
||||
contextSize := self.UserConfig().Git.DiffContextSize
|
||||
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
|
||||
extDiffCmd := self.pagerConfig.GetExternalDiffCommand()
|
||||
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig()
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Config("diff.noprefix=false").
|
||||
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
|
||||
ArgIfElse(extDiffCmd != "" || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
Arg("--color="+self.UserConfig().Git.Paging.ColorArg).
|
||||
Arg("--color="+self.pagerConfig.GetColorArg()).
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg("--stat").
|
||||
Arg("--decorate").
|
||||
|
||||
@@ -255,8 +255,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize uint64
|
||||
similarityThreshold int
|
||||
ignoreWhitespace bool
|
||||
extDiffCmd string
|
||||
useExtDiffGitConfig bool
|
||||
pagerConfig *config.PagingConfig
|
||||
expected []string
|
||||
}
|
||||
|
||||
@@ -267,7 +266,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
pagerConfig: nil,
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
|
||||
},
|
||||
{
|
||||
@@ -276,7 +275,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
pagerConfig: nil,
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--", "file.txt"},
|
||||
},
|
||||
{
|
||||
@@ -285,7 +284,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 77,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
pagerConfig: nil,
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
|
||||
},
|
||||
{
|
||||
@@ -294,7 +293,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
similarityThreshold: 33,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
pagerConfig: nil,
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%", "--"},
|
||||
},
|
||||
{
|
||||
@@ -303,7 +302,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 77,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: true,
|
||||
extDiffCmd: "",
|
||||
pagerConfig: nil,
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%", "--"},
|
||||
},
|
||||
{
|
||||
@@ -312,7 +311,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "difft --color=always",
|
||||
pagerConfig: &config.PagingConfig{ExternalDiffCommand: "difft --color=always"},
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
|
||||
},
|
||||
{
|
||||
@@ -321,7 +320,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
useExtDiffGitConfig: true,
|
||||
pagerConfig: &config.PagingConfig{UseExternalDiffGitConfig: true},
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
|
||||
},
|
||||
}
|
||||
@@ -329,11 +328,12 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
userConfig := config.GetDefaultConfig()
|
||||
userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd
|
||||
if s.pagerConfig != nil {
|
||||
userConfig.Git.Pagers = []config.PagingConfig{*s.pagerConfig}
|
||||
}
|
||||
userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
userConfig.Git.DiffContextSize = s.contextSize
|
||||
userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold
|
||||
userConfig.Git.Paging.UseExternalDiffGitConfig = s.useExtDiffGitConfig
|
||||
|
||||
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
|
||||
repoPaths := RepoPaths{
|
||||
|
||||
@@ -4,16 +4,18 @@ import (
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
)
|
||||
|
||||
type GitCommon struct {
|
||||
*common.Common
|
||||
version *GitVersion
|
||||
cmd oscommands.ICmdObjBuilder
|
||||
os *oscommands.OSCommand
|
||||
repoPaths *RepoPaths
|
||||
repo *gogit.Repository
|
||||
config *ConfigCommands
|
||||
version *GitVersion
|
||||
cmd oscommands.ICmdObjBuilder
|
||||
os *oscommands.OSCommand
|
||||
repoPaths *RepoPaths
|
||||
repo *gogit.Repository
|
||||
config *ConfigCommands
|
||||
pagerConfig *config.PagerConfig
|
||||
}
|
||||
|
||||
func NewGitCommon(
|
||||
@@ -24,14 +26,16 @@ func NewGitCommon(
|
||||
repoPaths *RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
config *ConfigCommands,
|
||||
pagerConfig *config.PagerConfig,
|
||||
) *GitCommon {
|
||||
return &GitCommon{
|
||||
Common: cmn,
|
||||
version: version,
|
||||
cmd: cmd,
|
||||
os: osCommand,
|
||||
repoPaths: repoPaths,
|
||||
repo: repo,
|
||||
config: config,
|
||||
Common: cmn,
|
||||
version: version,
|
||||
cmd: cmd,
|
||||
os: osCommand,
|
||||
repoPaths: repoPaths,
|
||||
repo: repo,
|
||||
config: config,
|
||||
pagerConfig: pagerConfig,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/go-git/v5/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type ConfigCommands struct {
|
||||
@@ -31,26 +26,6 @@ func NewConfigCommands(
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) ConfiguredPager() string {
|
||||
if os.Getenv("GIT_PAGER") != "" {
|
||||
return os.Getenv("GIT_PAGER")
|
||||
}
|
||||
if os.Getenv("PAGER") != "" {
|
||||
return os.Getenv("PAGER")
|
||||
}
|
||||
output := self.gitConfig.Get("core.pager")
|
||||
return strings.Split(output, "\n")[0]
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetPager(width int) string {
|
||||
templateValues := map[string]string{
|
||||
"columnWidth": strconv.Itoa(width/2 - 6),
|
||||
}
|
||||
|
||||
pagerTemplate := string(self.UserConfig().Git.Paging.Pager)
|
||||
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
|
||||
}
|
||||
|
||||
type GpgConfigKey string
|
||||
|
||||
const (
|
||||
@@ -122,6 +97,10 @@ func (self *ConfigCommands) GetRebaseUpdateRefs() bool {
|
||||
return self.gitConfig.GetBool("rebase.updateRefs")
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetMergeFF() string {
|
||||
return self.gitConfig.Get("merge.ff")
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) DropConfigCache() {
|
||||
self.gitConfig.DropCache()
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ func buildGitCommon(deps commonDeps) *GitCommon {
|
||||
gitCommon.Common.SetUserConfig(config.GetDefaultConfig())
|
||||
}
|
||||
|
||||
gitCommon.pagerConfig = config.NewPagerConfig(func() *config.UserConfig {
|
||||
return gitCommon.Common.UserConfig()
|
||||
})
|
||||
|
||||
gitCommon.version = deps.gitVersion
|
||||
if gitCommon.version == nil {
|
||||
gitCommon.version = &GitVersion{2, 0, 0, ""}
|
||||
|
||||
@@ -19,9 +19,9 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
|
||||
// This is for generating diffs to be shown in the UI (e.g. rendering a range
|
||||
// diff to the main view). It uses a custom pager if one is configured.
|
||||
func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj {
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
extDiffCmd := self.pagerConfig.GetExternalDiffCommand()
|
||||
useExtDiff := extDiffCmd != ""
|
||||
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
|
||||
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig()
|
||||
ignoreWhitespace := self.UserConfig().Git.IgnoreWhitespaceInDiffView
|
||||
|
||||
return self.cmd.New(
|
||||
@@ -30,7 +30,7 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj {
|
||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||
ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
|
||||
Arg(fmt.Sprintf("--color=%s", self.pagerConfig.GetColorArg())).
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("--unified=%d", self.UserConfig().Git.DiffContextSize)).
|
||||
Arg(diffArgs...).
|
||||
|
||||
@@ -201,8 +201,8 @@ func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||
PreviousPath: "",
|
||||
}
|
||||
|
||||
if strings.HasPrefix(status.Change, "R") {
|
||||
// if a line starts with 'R' then the next line is the original file.
|
||||
if strings.HasPrefix(status.Change, "R") || strings.HasPrefix(status.Change, "C") {
|
||||
// if a line starts with 'R' (rename) or 'C' (copy) then the next line is the original file.
|
||||
status.PreviousPath = splitLines[i+1]
|
||||
status.StatusString = fmt.Sprintf("%s %s -> %s", status.Change, status.PreviousPath, status.Path)
|
||||
i++
|
||||
|
||||
@@ -192,6 +192,43 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Copied files",
|
||||
similarityThreshold: 50,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||
"C copy1.txt\x00original.txt\x00CM copy2.txt\x00original.txt",
|
||||
nil,
|
||||
),
|
||||
expectedFiles: []*models.File{
|
||||
{
|
||||
Path: "copy1.txt",
|
||||
PreviousPath: "original.txt",
|
||||
HasStagedChanges: true,
|
||||
HasUnstagedChanges: false,
|
||||
Tracked: true,
|
||||
Added: false,
|
||||
Deleted: false,
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "C original.txt -> copy1.txt",
|
||||
ShortStatus: "C ",
|
||||
},
|
||||
{
|
||||
Path: "copy2.txt",
|
||||
PreviousPath: "original.txt",
|
||||
HasStagedChanges: true,
|
||||
HasUnstagedChanges: true,
|
||||
Tracked: true,
|
||||
Added: false,
|
||||
Deleted: false,
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "CM original.txt -> copy2.txt",
|
||||
ShortStatus: "CM",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
@@ -244,8 +244,9 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
|
||||
|
||||
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
|
||||
"LANG=C", // Force using English language
|
||||
"LC_ALL=C", // Force using English language
|
||||
"LC_MESSAGES=C", // Force using English language
|
||||
"GIT_SEQUENCE_EDITOR="+gitSequenceEditor,
|
||||
)
|
||||
|
||||
@@ -277,8 +278,9 @@ func (self *RebaseCommands) GitRebaseEditTodo(todosFileContent []byte) error {
|
||||
|
||||
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
|
||||
"LANG=C", // Force using English language
|
||||
"LC_ALL=C", // Force using English language
|
||||
"LC_MESSAGES=C", // Force using English language
|
||||
"GIT_EDITOR="+ex,
|
||||
"GIT_SEQUENCE_EDITOR="+ex,
|
||||
)
|
||||
@@ -519,10 +521,17 @@ func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, comm
|
||||
}
|
||||
|
||||
for _, filePath := range filePaths {
|
||||
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
||||
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+filePath).ToArgv()
|
||||
|
||||
if err := self.cmd.New(cmdArgs).Run(); err != nil {
|
||||
doesFileExistInPreviousCommit := false
|
||||
if commitIndex < len(commits)-1 {
|
||||
// check if file exists in previous commit (this command returns an empty string if the file doesn't exist)
|
||||
cmdArgs := NewGitCmd("ls-tree").Arg("--name-only", "HEAD^", "--", filePath).ToArgv()
|
||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
doesFileExistInPreviousCommit = strings.TrimRight(output, "\n") == filePath
|
||||
}
|
||||
if !doesFileExistInPreviousCommit {
|
||||
if err := self.os.Remove(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
fileName: []string{"test999.txt"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "abcdef"}, "", nil).
|
||||
ExpectGitArgs([]string{"cat-file", "-e", "HEAD^:test999.txt"}, "", nil).
|
||||
ExpectGitArgs([]string{"ls-tree", "--name-only", "HEAD^", "--", "test999.txt"}, "test999.txt\n", nil).
|
||||
ExpectGitArgs([]string{"checkout", "HEAD^", "--", "test999.txt"}, "", nil).
|
||||
ExpectGitArgs([]string{"commit", "--amend", "--no-edit", "--allow-empty", "--allow-empty-message"}, "", nil).
|
||||
ExpectGitArgs([]string{"rebase", "--continue"}, "", nil),
|
||||
|
||||
@@ -81,8 +81,8 @@ func (self *StashCommands) Hash(index int) (string, error) {
|
||||
}
|
||||
|
||||
func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj {
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
|
||||
extDiffCmd := self.pagerConfig.GetExternalDiffCommand()
|
||||
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig()
|
||||
|
||||
// "-u" is the same as "--include-untracked", but the latter fails in older git versions for some reason
|
||||
cmdArgs := NewGitCmd("stash").Arg("show").
|
||||
@@ -91,7 +91,7 @@ func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj {
|
||||
Arg("-u").
|
||||
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
|
||||
ArgIfElse(extDiffCmd != "" || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
|
||||
Arg(fmt.Sprintf("--color=%s", self.pagerConfig.GetColorArg())).
|
||||
Arg(fmt.Sprintf("--unified=%d", self.UserConfig().Git.DiffContextSize)).
|
||||
ArgIf(self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("--find-renames=%d%%", self.UserConfig().Git.RenameSimilarityThreshold)).
|
||||
|
||||
@@ -103,8 +103,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
contextSize uint64
|
||||
similarityThreshold int
|
||||
ignoreWhitespace bool
|
||||
extDiffCmd string
|
||||
useExtDiffGitConfig bool
|
||||
pagerConfig *config.PagingConfig
|
||||
expected []string
|
||||
}
|
||||
|
||||
@@ -139,7 +138,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "difft --color=always",
|
||||
pagerConfig: &config.PagingConfig{ExternalDiffCommand: "difft --color=always"},
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "stash", "show", "-p", "--stat", "-u", "--ext-diff", "--color=always", "--unified=3", "--find-renames=50%", "refs/stash@{5}"},
|
||||
},
|
||||
{
|
||||
@@ -148,7 +147,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
similarityThreshold: 50,
|
||||
ignoreWhitespace: false,
|
||||
useExtDiffGitConfig: true,
|
||||
pagerConfig: &config.PagingConfig{UseExternalDiffGitConfig: true},
|
||||
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "-u", "--ext-diff", "--color=always", "--unified=3", "--find-renames=50%", "refs/stash@{5}"},
|
||||
},
|
||||
{
|
||||
@@ -167,8 +166,9 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
userConfig.Git.DiffContextSize = s.contextSize
|
||||
userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold
|
||||
userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd
|
||||
userConfig.Git.Paging.UseExternalDiffGitConfig = s.useExtDiffGitConfig
|
||||
if s.pagerConfig != nil {
|
||||
userConfig.Git.Pagers = []config.PagingConfig{*s.pagerConfig}
|
||||
}
|
||||
repoPaths := RepoPaths{
|
||||
worktreePath: "/path/to/worktree",
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
@@ -257,7 +258,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool,
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool) *oscommands.CmdObj {
|
||||
colorArg := self.UserConfig().Git.Paging.ColorArg
|
||||
colorArg := self.pagerConfig.GetColorArg()
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
@@ -265,9 +266,9 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
|
||||
contextSize := self.UserConfig().Git.DiffContextSize
|
||||
prevPath := node.GetPreviousPath()
|
||||
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
extDiffCmd := self.pagerConfig.GetExternalDiffCommand()
|
||||
useExtDiff := extDiffCmd != "" && !plain
|
||||
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
|
||||
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() && !plain
|
||||
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||
@@ -298,14 +299,14 @@ func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bo
|
||||
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) *oscommands.CmdObj {
|
||||
contextSize := self.UserConfig().Git.DiffContextSize
|
||||
|
||||
colorArg := self.UserConfig().Git.Paging.ColorArg
|
||||
colorArg := self.pagerConfig.GetColorArg()
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
|
||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||
extDiffCmd := self.pagerConfig.GetExternalDiffCommand()
|
||||
useExtDiff := extDiffCmd != "" && !plain
|
||||
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
|
||||
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() && !plain
|
||||
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
Config("diff.noprefix=false").
|
||||
@@ -407,3 +408,46 @@ func (self *WorkingTreeCommands) ResetMixed(ref string) error {
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) ShowFileAtStage(path string, stage int) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg(fmt.Sprintf(":%d:%s", stage, path)).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) ObjectIDAtStage(path string, stage int) (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-parse").
|
||||
Arg(fmt.Sprintf(":%d:%s", stage, path)).
|
||||
ToArgv()
|
||||
|
||||
output, err := self.cmd.New(cmdArgs).RunWithOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(output), nil
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) MergeFileForFiles(strategy string, oursFilepath string, baseFilepath string, theirsFilepath string) (string, error) {
|
||||
cmdArgs := NewGitCmd("merge-file").
|
||||
Arg(strategy).
|
||||
Arg("--stdout").
|
||||
Arg(oursFilepath, baseFilepath, theirsFilepath).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).RunWithOutput()
|
||||
}
|
||||
|
||||
// OIDs mode (Git 2.43+)
|
||||
func (self *WorkingTreeCommands) MergeFileForObjectIDs(strategy string, oursID string, baseID string, theirsID string) (string, error) {
|
||||
cmdArgs := NewGitCmd("merge-file").
|
||||
Arg(strategy).
|
||||
Arg("--stdout").
|
||||
Arg("--object-id").
|
||||
Arg(oursID, baseID, theirsID).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).RunWithOutput()
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ func (self *cmdObjRunner) runAndDetectCredentialRequest(
|
||||
promptUserForCredential func(CredentialType) <-chan string,
|
||||
) error {
|
||||
// setting the output to english so we can parse it for a username/password request
|
||||
cmdObj.AddEnvVars("LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")
|
||||
cmdObj.AddEnvVars("LANG=C", "LC_ALL=C", "LC_MESSAGES=C")
|
||||
|
||||
return self.runAndStreamAux(cmdObj, func(handler *cmdHandler, cmdWriter io.Writer) {
|
||||
tr := io.TeeReader(handler.stdoutPipe, cmdWriter)
|
||||
|
||||
@@ -35,16 +35,16 @@ import (
|
||||
// destination file exists, all it's contents will be replaced by the contents
|
||||
// of the source file. The file mode will be copied from the source and
|
||||
// the copied data is synced/flushed to stable storage.
|
||||
func CopyFile(src, dst string) (err error) {
|
||||
func CopyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if e := out.Close(); e != nil {
|
||||
@@ -54,30 +54,30 @@ func CopyFile(src, dst string) (err error) {
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
err = out.Sync()
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
si, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(dst, si.Mode())
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
// CopyDir recursively copies a directory tree, attempting to preserve permissions.
|
||||
// Source directory must exist. If destination already exists we'll clobber it.
|
||||
// Symlinks are ignored and skipped.
|
||||
func CopyDir(src string, dst string) (err error) {
|
||||
func CopyDir(src string, dst string) error {
|
||||
src = filepath.Clean(src)
|
||||
dst = filepath.Clean(dst)
|
||||
|
||||
@@ -91,7 +91,7 @@ func CopyDir(src string, dst string) (err error) {
|
||||
|
||||
_, err = os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// it exists so let's remove it
|
||||
@@ -102,12 +102,12 @@ func CopyDir(src string, dst string) (err error) {
|
||||
|
||||
err = os.MkdirAll(dst, si.Mode())
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(src)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
@@ -117,13 +117,13 @@ func CopyDir(src string, dst string) (err error) {
|
||||
if entry.IsDir() {
|
||||
err = CopyDir(srcPath, dstPath)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var info os.FileInfo
|
||||
info, err = entry.Info()
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip symlinks.
|
||||
@@ -133,10 +133,10 @@ func CopyDir(src string, dst string) (err error) {
|
||||
|
||||
err = CopyFile(srcPath, dstPath)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return //nolint: nakedret
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -272,6 +272,7 @@ func computeMigratedConfig(path string, content []byte, changes *ChangesSet) ([]
|
||||
{[]string{"gui", "skipUnstageLineWarning"}, "skipDiscardChangeWarning"},
|
||||
{[]string{"keybinding", "universal", "executeCustomCommand"}, "executeShellCommand"},
|
||||
{[]string{"gui", "windowSize"}, "screenMode"},
|
||||
{[]string{"keybinding", "files", "openMergeTool"}, "openMergeOptions"},
|
||||
}
|
||||
|
||||
for _, pathToReplace := range pathsToReplace {
|
||||
@@ -309,6 +310,11 @@ func computeMigratedConfig(path string, content []byte, changes *ChangesSet) ([]
|
||||
return nil, false, fmt.Errorf("Couldn't migrate config file at `%s`: %w", path, err)
|
||||
}
|
||||
|
||||
err = migratePagers(&rootNode, changes)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("Couldn't migrate config file at `%s`: %w", path, err)
|
||||
}
|
||||
|
||||
// Add more migrations here...
|
||||
|
||||
if reflect.DeepEqual(rootNode, originalCopy) {
|
||||
@@ -468,6 +474,37 @@ func migrateAllBranchesLogCmd(rootNode *yaml.Node, changes *ChangesSet) error {
|
||||
})
|
||||
}
|
||||
|
||||
func migratePagers(rootNode *yaml.Node, changes *ChangesSet) error {
|
||||
return yaml_utils.TransformNode(rootNode, []string{"git"}, func(gitNode *yaml.Node) error {
|
||||
pagingKeyNode, pagingValueNode := yaml_utils.LookupKey(gitNode, "paging")
|
||||
if pagingKeyNode == nil || pagingValueNode.Kind != yaml.MappingNode {
|
||||
// If there's no "paging" section (or it's not an object), there's nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
pagersKeyNode, _ := yaml_utils.LookupKey(gitNode, "pagers")
|
||||
if pagersKeyNode != nil {
|
||||
// Conversely, if there *is* already a "pagers" array, we also have nothing to do.
|
||||
// This covers the case where the user keeps both the "paging" section and the "pagers"
|
||||
// array for the sake of easier testing of old versions.
|
||||
return nil
|
||||
}
|
||||
|
||||
pagingKeyNode.Value = "pagers"
|
||||
pagingContentCopy := pagingValueNode.Content
|
||||
pagingValueNode.Kind = yaml.SequenceNode
|
||||
pagingValueNode.Tag = "!!seq"
|
||||
pagingValueNode.Content = []*yaml.Node{{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: pagingContentCopy,
|
||||
}}
|
||||
|
||||
changes.Add("Moved git.paging object to git.pagers array")
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetDebug() bool {
|
||||
return c.debug
|
||||
}
|
||||
|
||||
@@ -897,7 +897,7 @@ keybinding:
|
||||
toggleStagedAll: a
|
||||
viewResetOptions: D
|
||||
fetch: f
|
||||
openMergeTool: M
|
||||
openMergeOptions: M
|
||||
openStatusFilter: <c-b>
|
||||
copyFileInfoToClipboard: "y"
|
||||
collapseAll: '-'
|
||||
@@ -1089,3 +1089,98 @@ func TestAllBranchesLogCmdMigrations(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPagerMigration(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
expectedDidChange bool
|
||||
expectedChanges []string
|
||||
}{
|
||||
{
|
||||
name: "Incomplete Configuration Passes uneventfully",
|
||||
input: "git:",
|
||||
expectedDidChange: false,
|
||||
expectedChanges: []string{},
|
||||
},
|
||||
{
|
||||
name: "No paging section",
|
||||
input: `git:
|
||||
autoFetch: true
|
||||
`,
|
||||
expected: `git:
|
||||
autoFetch: true
|
||||
`,
|
||||
expectedDidChange: false,
|
||||
expectedChanges: []string{},
|
||||
},
|
||||
{
|
||||
name: "Both paging and pagers exist",
|
||||
input: `git:
|
||||
paging:
|
||||
pager: delta --dark --paging=never
|
||||
pagers:
|
||||
- diff: diff-so-fancy
|
||||
`,
|
||||
expected: `git:
|
||||
paging:
|
||||
pager: delta --dark --paging=never
|
||||
pagers:
|
||||
- diff: diff-so-fancy
|
||||
`,
|
||||
expectedDidChange: false,
|
||||
expectedChanges: []string{},
|
||||
},
|
||||
{
|
||||
name: "paging is not an object",
|
||||
input: `git:
|
||||
paging: 5
|
||||
`,
|
||||
expected: `git:
|
||||
paging: 5
|
||||
`,
|
||||
expectedDidChange: false,
|
||||
expectedChanges: []string{},
|
||||
},
|
||||
{
|
||||
name: "paging is moved to pagers array (keeping the order)",
|
||||
input: `git:
|
||||
paging:
|
||||
pager: delta --dark --paging=never
|
||||
autoFetch: true
|
||||
`,
|
||||
expected: `git:
|
||||
pagers:
|
||||
- pager: delta --dark --paging=never
|
||||
autoFetch: true
|
||||
`,
|
||||
expectedDidChange: true,
|
||||
expectedChanges: []string{"Moved git.paging object to git.pagers array"},
|
||||
},
|
||||
{
|
||||
name: "paging is moved to pagers array even if empty",
|
||||
input: `git:
|
||||
paging: {}
|
||||
`,
|
||||
expected: `git:
|
||||
pagers: [{}]
|
||||
`,
|
||||
expectedDidChange: true,
|
||||
expectedChanges: []string{"Moved git.paging object to git.pagers array"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
changes := NewChangesSet()
|
||||
actual, didChange, err := computeMigratedConfig("path doesn't matter", []byte(s.input), changes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, s.expectedDidChange, didChange)
|
||||
if didChange {
|
||||
assert.Equal(t, s.expected, string(actual))
|
||||
}
|
||||
assert.Equal(t, s.expectedChanges, changes.ToSliceFromOldest())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type PagerConfig struct {
|
||||
getUserConfig func() *UserConfig
|
||||
pagerIndex int
|
||||
}
|
||||
|
||||
func NewPagerConfig(getUserConfig func() *UserConfig) *PagerConfig {
|
||||
return &PagerConfig{getUserConfig: getUserConfig}
|
||||
}
|
||||
|
||||
func (self *PagerConfig) currentPagerConfig() *PagingConfig {
|
||||
pagers := self.getUserConfig().Git.Pagers
|
||||
if len(pagers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Guard against the pager index being out of range, which can happen if the user
|
||||
// has removed pagers from their config file while lazygit is running.
|
||||
if self.pagerIndex >= len(pagers) {
|
||||
self.pagerIndex = 0
|
||||
}
|
||||
|
||||
return &pagers[self.pagerIndex]
|
||||
}
|
||||
|
||||
func (self *PagerConfig) GetPagerCommand(width int) string {
|
||||
currentPagerConfig := self.currentPagerConfig()
|
||||
if currentPagerConfig == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
templateValues := map[string]string{
|
||||
"columnWidth": strconv.Itoa(width/2 - 6),
|
||||
}
|
||||
|
||||
pagerTemplate := string(currentPagerConfig.Pager)
|
||||
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
|
||||
}
|
||||
|
||||
func (self *PagerConfig) GetColorArg() string {
|
||||
currentPagerConfig := self.currentPagerConfig()
|
||||
if currentPagerConfig == nil {
|
||||
return "always"
|
||||
}
|
||||
|
||||
colorArg := currentPagerConfig.ColorArg
|
||||
if colorArg == "" {
|
||||
return "always"
|
||||
}
|
||||
return colorArg
|
||||
}
|
||||
|
||||
func (self *PagerConfig) GetExternalDiffCommand() string {
|
||||
currentPagerConfig := self.currentPagerConfig()
|
||||
if currentPagerConfig == nil {
|
||||
return ""
|
||||
}
|
||||
return currentPagerConfig.ExternalDiffCommand
|
||||
}
|
||||
|
||||
func (self *PagerConfig) GetUseExternalDiffGitConfig() bool {
|
||||
currentPagerConfig := self.currentPagerConfig()
|
||||
if currentPagerConfig == nil {
|
||||
return false
|
||||
}
|
||||
return currentPagerConfig.UseExternalDiffGitConfig
|
||||
}
|
||||
|
||||
func (self *PagerConfig) CyclePagers() {
|
||||
self.pagerIndex = (self.pagerIndex + 1) % len(self.getUserConfig().Git.Pagers)
|
||||
}
|
||||
|
||||
func (self *PagerConfig) CurrentPagerIndex() (int, int) {
|
||||
return self.pagerIndex, len(self.getUserConfig().Git.Pagers)
|
||||
}
|
||||
+68
-57
@@ -84,13 +84,14 @@ type GuiConfig struct {
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
// If true, do not show a warning when rewording a commit via an external editor
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
// If true, switch to a different worktree without confirmation when checking out a branch that is checked out in that worktree
|
||||
SkipSwitchWorktreeOnCheckoutWarning bool `yaml:"skipSwitchWorktreeOnCheckoutWarning"`
|
||||
// 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 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.
|
||||
// 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:
|
||||
@@ -103,13 +104,11 @@ type GuiConfig struct {
|
||||
// - '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 string `yaml:"enlargedSideViewLocation"`
|
||||
// If true, wrap lines in the staging view to the width of the view. This
|
||||
// makes it much easier to work with diffs that have long lines, e.g.
|
||||
// paragraphs of markdown text.
|
||||
// If true, wrap lines in the staging view to the width of the view. This makes it much easier to work with diffs that have long lines, e.g. paragraphs of markdown text.
|
||||
WrapLinesInStagingView bool `yaml:"wrapLinesInStagingView"`
|
||||
// If true, hunk selection mode will be enabled by default when entering the staging view.
|
||||
UseHunkModeInStagingView bool `yaml:"useHunkModeInStagingView"`
|
||||
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru' | 'pt'
|
||||
Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`
|
||||
// Format used when displaying time e.g. commit time.
|
||||
// Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
|
||||
@@ -236,8 +235,30 @@ type SpinnerConfig struct {
|
||||
}
|
||||
|
||||
type GitConfig struct {
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
|
||||
Paging PagingConfig `yaml:"paging"`
|
||||
// Array of pagers. Each entry has the following format:
|
||||
// [dev] The following documentation is duplicated from the PagingConfig struct below.
|
||||
//
|
||||
// # 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: ""
|
||||
//
|
||||
// # e.g. 'difft --color=always'
|
||||
// externalDiffCommand: ""
|
||||
//
|
||||
// # If true, Lazygit will use git's `diff.external` config for paging.
|
||||
// # The advantage over `externalDiffCommand` is that this can be
|
||||
// # configured per file type in .gitattributes; see
|
||||
// # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
|
||||
// useExternalDiffGitConfig: false
|
||||
//
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md for more information.
|
||||
Pagers []PagingConfig `yaml:"pagers"`
|
||||
// Config relating to committing
|
||||
Commit CommitConfig `yaml:"commit"`
|
||||
// Config relating to merging
|
||||
@@ -255,10 +276,7 @@ type GitConfig struct {
|
||||
AutoForwardBranches string `yaml:"autoForwardBranches" jsonschema:"enum=none,enum=onlyMainBranches,enum=allBranches"`
|
||||
// If true, pass the --all arg to git fetch
|
||||
FetchAll bool `yaml:"fetchAll"`
|
||||
// If true, lazygit will automatically stage files that used to have merge
|
||||
// conflicts but no longer do; and it will also ask you if you want to
|
||||
// continue a merge or rebase if you've resolved all conflicts. If false, it
|
||||
// won't do either of these things.
|
||||
// If true, lazygit will automatically stage files that used to have merge conflicts but no longer do; and it will also ask you if you want to continue a merge or rebase if you've resolved all conflicts. If false, it won't do either of these things.
|
||||
AutoStageResolvedConflicts bool `yaml:"autoStageResolvedConflicts"`
|
||||
// Command used when displaying the current branch git log in the main window
|
||||
BranchLogCmd string `yaml:"branchLogCmd"`
|
||||
@@ -293,8 +311,7 @@ type GitConfig struct {
|
||||
// One of: 'date' (default) | 'alphabetical'
|
||||
// Can be changed from within Lazygit with the Sort Order menu (`s`) in the remote branches panel.
|
||||
RemoteBranchSortOrder string `yaml:"remoteBranchSortOrder" jsonschema:"enum=date,enum=alphabetical"`
|
||||
// When copying commit hashes to the clipboard, truncate them to this
|
||||
// length. Set to 40 to disable truncation.
|
||||
// When copying commit hashes to the clipboard, truncate them to this length. Set to 40 to disable truncation.
|
||||
TruncateCopiedCommitHashesTo int `yaml:"truncateCopiedCommitHashesTo"`
|
||||
}
|
||||
|
||||
@@ -308,6 +325,7 @@ func (PagerType) JSONSchemaExtend(schema *jsonschema.Schema) {
|
||||
}
|
||||
}
|
||||
|
||||
// [dev] This documentation is duplicated in the GitConfig struct. If you make changes here, make them there too.
|
||||
type PagingConfig struct {
|
||||
// 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 string `yaml:"colorArg" jsonschema:"enum=always,enum=never"`
|
||||
@@ -343,8 +361,7 @@ type MergingConfig struct {
|
||||
|
||||
type LogConfig struct {
|
||||
// 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/
|
||||
// 'topo-order' makes it easier to read the git log graph, but commits may not appear chronologically. See https://git-scm.com/docs/
|
||||
//
|
||||
// Can be changed from within Lazygit with `Log menu -> Commit sort order` (`<c-l>` in the commits window by default).
|
||||
Order string `yaml:"order" jsonschema:"enum=date-order,enum=author-date-order,enum=topo-order,enum=default"`
|
||||
@@ -449,6 +466,7 @@ type KeybindingUniversalConfig struct {
|
||||
PrevTab string `yaml:"prevTab"`
|
||||
NextScreenMode string `yaml:"nextScreenMode"`
|
||||
PrevScreenMode string `yaml:"prevScreenMode"`
|
||||
CyclePagers string `yaml:"cyclePagers"`
|
||||
Undo string `yaml:"undo"`
|
||||
Redo string `yaml:"redo"`
|
||||
FilteringMenu string `yaml:"filteringMenu"`
|
||||
@@ -487,7 +505,7 @@ type KeybindingFilesConfig struct {
|
||||
ViewResetOptions string `yaml:"viewResetOptions"`
|
||||
Fetch string `yaml:"fetch"`
|
||||
ToggleTreeView string `yaml:"toggleTreeView"`
|
||||
OpenMergeTool string `yaml:"openMergeTool"`
|
||||
OpenMergeOptions string `yaml:"openMergeOptions"`
|
||||
OpenStatusFilter string `yaml:"openStatusFilter"`
|
||||
CopyFileInfoToClipboard string `yaml:"copyFileInfoToClipboard"`
|
||||
CollapseAll string `yaml:"collapseAll"`
|
||||
@@ -582,12 +600,10 @@ type OSConfig struct {
|
||||
// Command for editing a file. Should contain "{{filename}}".
|
||||
Edit string `yaml:"edit,omitempty"`
|
||||
|
||||
// Command for editing a file at a given line number. Should contain
|
||||
// "{{filename}}", and may optionally contain "{{line}}".
|
||||
// Command for editing a file at a given line number. Should contain "{{filename}}", and may optionally contain "{{line}}".
|
||||
EditAtLine string `yaml:"editAtLine,omitempty"`
|
||||
|
||||
// Same as EditAtLine, except that the command needs to wait until the
|
||||
// window is closed.
|
||||
// Same as EditAtLine, except that the command needs to wait until the window is closed.
|
||||
EditAtLineAndWait string `yaml:"editAtLineAndWait,omitempty"`
|
||||
|
||||
// Whether lazygit suspends until an edit process returns
|
||||
@@ -598,12 +614,10 @@ type OSConfig struct {
|
||||
// For opening a directory in an editor
|
||||
OpenDirInEditor string `yaml:"openDirInEditor,omitempty"`
|
||||
|
||||
// A built-in preset that sets all of the above settings. Supported presets
|
||||
// are defined in the getPreset function in editor_presets.go.
|
||||
// A built-in preset that sets all of the above settings. Supported presets are defined in the getPreset function in editor_presets.go.
|
||||
EditPreset string `yaml:"editPreset,omitempty" jsonschema:"example=vim,example=nvim,example=emacs,example=nano,example=vscode,example=sublime,example=kakoune,example=helix,example=xcode,example=zed,example=acme"`
|
||||
|
||||
// Command for opening a file, as if the file is double-clicked. Should
|
||||
// contain "{{filename}}", but doesn't support "{{line}}".
|
||||
// Command for opening a file, as if the file is double-clicked. Should contain "{{filename}}", but doesn't support "{{line}}".
|
||||
Open string `yaml:"open,omitempty"`
|
||||
|
||||
// Command for opening a link. Should contain "{{link}}".
|
||||
@@ -760,32 +774,33 @@ func GetDefaultConfig() *UserConfig {
|
||||
UnstagedChangesColor: []string{"red"},
|
||||
DefaultFgColor: []string{"default"},
|
||||
},
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowPanelJumps: true,
|
||||
ShowFileTree: true,
|
||||
ShowRootItemInFileTree: true,
|
||||
ShowNumstatInFilesView: false,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ShowFileIcons: true,
|
||||
CommitAuthorShortLength: 2,
|
||||
CommitAuthorLongLength: 17,
|
||||
CommitHashLength: 8,
|
||||
ShowBranchCommitHash: false,
|
||||
ShowDivergenceFromBaseBranch: "none",
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
ScreenMode: "normal",
|
||||
Border: "rounded",
|
||||
AnimateExplosion: true,
|
||||
PortraitMode: "auto",
|
||||
FilterMode: "substring",
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowPanelJumps: true,
|
||||
ShowFileTree: true,
|
||||
ShowRootItemInFileTree: true,
|
||||
ShowNumstatInFilesView: false,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ShowFileIcons: true,
|
||||
CommitAuthorShortLength: 2,
|
||||
CommitAuthorLongLength: 17,
|
||||
CommitHashLength: 8,
|
||||
ShowBranchCommitHash: false,
|
||||
ShowDivergenceFromBaseBranch: "none",
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
SkipSwitchWorktreeOnCheckoutWarning: false,
|
||||
ScreenMode: "normal",
|
||||
Border: "rounded",
|
||||
AnimateExplosion: true,
|
||||
PortraitMode: "auto",
|
||||
FilterMode: "substring",
|
||||
Spinner: SpinnerConfig{
|
||||
Frames: []string{"|", "/", "-", "\\"},
|
||||
Rate: 50,
|
||||
@@ -796,11 +811,6 @@ func GetDefaultConfig() *UserConfig {
|
||||
SwitchTabsWithPanelJumpKeys: false,
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
ColorArg: "always",
|
||||
Pager: "",
|
||||
ExternalDiffCommand: "",
|
||||
},
|
||||
Commit: CommitConfig{
|
||||
SignOff: false,
|
||||
AutoWrapCommitMessage: true,
|
||||
@@ -916,6 +926,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
PrevTab: "[",
|
||||
NextScreenMode: "+",
|
||||
PrevScreenMode: "_",
|
||||
CyclePagers: "|",
|
||||
Undo: "z",
|
||||
Redo: "Z",
|
||||
FilteringMenu: "<c-s>",
|
||||
@@ -950,7 +961,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
ViewResetOptions: "D",
|
||||
Fetch: "f",
|
||||
ToggleTreeView: "`",
|
||||
OpenMergeTool: "M",
|
||||
OpenMergeOptions: "M",
|
||||
OpenStatusFilter: "<c-b>",
|
||||
ConfirmDiscard: "x",
|
||||
CopyFileInfoToClipboard: "y",
|
||||
|
||||
@@ -124,8 +124,8 @@ func (gui *Gui) getRandomTip() string {
|
||||
formattedKey(config.Universal.Remove),
|
||||
),
|
||||
fmt.Sprintf(
|
||||
"If you need to pull out the big guns to resolve merge conflicts, you can press '%s' in the files panel to open 'git mergetool'",
|
||||
formattedKey(config.Files.OpenMergeTool),
|
||||
"If you need to pull out the big guns to resolve merge conflicts, you can press '%s' in the files panel to open merge options",
|
||||
formattedKey(config.Files.OpenMergeOptions),
|
||||
),
|
||||
fmt.Sprintf(
|
||||
"To revert a commit, press '%s' on that commit",
|
||||
|
||||
+1
-1
@@ -196,7 +196,7 @@ func (self *ContextMgr) Activate(c types.Context, opts types.OnFocusOpts) {
|
||||
|
||||
v.Visible = true
|
||||
|
||||
self.gui.c.GocuiGui().Cursor = v.Editable
|
||||
self.gui.c.GocuiGui().Cursor = v.Editable && v.Mask == 0
|
||||
|
||||
c.HandleFocus(opts)
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
Bisect: bisectHelper,
|
||||
Suggestions: suggestionsHelper,
|
||||
Files: helpers.NewFilesHelper(helperCommon),
|
||||
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper),
|
||||
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper, rebaseHelper),
|
||||
Tags: helpers.NewTagsHelper(helperCommon, commitsHelper, gpgHelper),
|
||||
BranchesHelper: helpers.NewBranchesHelper(helperCommon, worktreeHelper),
|
||||
GPG: helpers.NewGpgHelper(helperCommon),
|
||||
|
||||
@@ -420,15 +420,13 @@ func (self *BranchesController) promptToCheckoutWorktree(worktree *models.Worktr
|
||||
"worktreeName": worktree.Name,
|
||||
})
|
||||
|
||||
self.c.Confirm(types.ConfirmOpts{
|
||||
return self.c.ConfirmIf(!self.c.UserConfig().Gui.SkipSwitchWorktreeOnCheckoutWarning, types.ConfirmOpts{
|
||||
Title: self.c.Tr.SwitchToWorktree,
|
||||
Prompt: prompt,
|
||||
HandleConfirm: func() error {
|
||||
return self.c.Helpers().Worktree.Switch(worktree, context.LOCAL_BRANCHES_CONTEXT_KEY)
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BranchesController) handleCreatePullRequest(selectedBranch *models.Branch) error {
|
||||
@@ -490,7 +488,7 @@ func (self *BranchesController) forceCheckout() error {
|
||||
|
||||
func (self *BranchesController) checkoutPreviousBranch() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutBranch)
|
||||
return self.c.Helpers().Refs.CheckoutRef("-", types.CheckoutRefOptions{})
|
||||
return self.c.Helpers().Refs.CheckoutPreviousRef()
|
||||
}
|
||||
|
||||
func (self *BranchesController) checkoutByName() error {
|
||||
|
||||
@@ -173,7 +173,7 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error
|
||||
|
||||
self.returnFocusFromPatchExplorerIfNecessary()
|
||||
|
||||
mustStash := self.c.Helpers().WorkingTree.IsWorkingTreeDirty()
|
||||
mustStash := self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules()
|
||||
return self.c.ConfirmIf(mustStash, types.ConfirmOpts{
|
||||
Title: self.c.Tr.MustStashTitle,
|
||||
Prompt: self.c.Tr.MustStashWarning,
|
||||
|
||||
@@ -178,10 +178,13 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
Description: self.c.Tr.OpenDiffTool,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
||||
Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
|
||||
Description: self.c.Tr.OpenMergeTool,
|
||||
Tooltip: self.c.Tr.OpenMergeToolTooltip,
|
||||
Key: opts.GetKey(opts.Config.Files.OpenMergeOptions),
|
||||
Handler: self.withItems(self.openMergeConflictMenu),
|
||||
Description: self.c.Tr.ViewMergeConflictOptions,
|
||||
Tooltip: self.c.Tr.ViewMergeConflictOptionsTooltip,
|
||||
GetDisabledReason: self.require(self.itemsSelected(self.canOpenMergeConflictMenu)),
|
||||
OpensMenu: true,
|
||||
DisplayOnScreen: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.Fetch),
|
||||
@@ -573,7 +576,7 @@ func (self *FilesController) handleNonInlineConflict(file *models.File) error {
|
||||
OnPress: func() error {
|
||||
return handle(self.c.Git().WorkingTree.StageFile, self.c.Tr.Actions.ResolveConflictByKeepingFile)
|
||||
},
|
||||
Key: 'k',
|
||||
Key: 'p',
|
||||
}
|
||||
deleteItem := &types.MenuItem{
|
||||
Label: self.c.Tr.MergeConflictDeleteFile,
|
||||
@@ -970,7 +973,7 @@ func (self *FilesController) createStashMenu() error {
|
||||
{
|
||||
Label: self.c.Tr.StashAllChanges,
|
||||
OnPress: func() error {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules() {
|
||||
return errors.New(self.c.Tr.NoFilesToStash)
|
||||
}
|
||||
return self.handleStashSave(self.c.Git().Stash.Push, self.c.Tr.Actions.StashAllChanges)
|
||||
@@ -980,7 +983,7 @@ func (self *FilesController) createStashMenu() error {
|
||||
{
|
||||
Label: self.c.Tr.StashAllChangesKeepIndex,
|
||||
OnPress: func() error {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules() {
|
||||
return errors.New(self.c.Tr.NoFilesToStash)
|
||||
}
|
||||
// if there are no staged files it behaves the same as Stash.Save
|
||||
@@ -999,7 +1002,7 @@ func (self *FilesController) createStashMenu() error {
|
||||
Label: self.c.Tr.StashStagedChanges,
|
||||
OnPress: func() error {
|
||||
// there must be something in staging otherwise the current implementation mucks the stash up
|
||||
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||
if !self.c.Helpers().WorkingTree.AnyStagedFilesExceptSubmodules() {
|
||||
return errors.New(self.c.Tr.NoTrackedStagedFilesStash)
|
||||
}
|
||||
return self.handleStashSave(self.c.Git().Stash.SaveStagedChanges, self.c.Tr.Actions.StashStagedChanges)
|
||||
@@ -1009,10 +1012,10 @@ func (self *FilesController) createStashMenu() error {
|
||||
{
|
||||
Label: self.c.Tr.StashUnstagedChanges,
|
||||
OnPress: func() error {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules() {
|
||||
return errors.New(self.c.Tr.NoFilesToStash)
|
||||
}
|
||||
if self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||
if self.c.Helpers().WorkingTree.AnyStagedFilesExceptSubmodules() {
|
||||
return self.handleStashSave(self.c.Git().Stash.StashUnstagedChanges, self.c.Tr.Actions.StashUnstagedChanges)
|
||||
}
|
||||
// ordinary stash
|
||||
@@ -1024,6 +1027,34 @@ func (self *FilesController) createStashMenu() error {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *FilesController) openMergeConflictMenu(nodes []*filetree.FileNode) error {
|
||||
normalizedNodes := flattenSelectedNodesToFiles(nodes)
|
||||
|
||||
fileNodesWithConflicts := lo.Filter(normalizedNodes, func(node *filetree.FileNode, _ int) bool {
|
||||
return node.File != nil && node.File.HasInlineMergeConflicts
|
||||
})
|
||||
|
||||
filepaths := lo.Map(fileNodesWithConflicts, func(node *filetree.FileNode, _ int) string {
|
||||
return node.GetPath()
|
||||
})
|
||||
|
||||
return self.c.Helpers().WorkingTree.CreateMergeConflictMenu(filepaths)
|
||||
}
|
||||
|
||||
func (self *FilesController) canOpenMergeConflictMenu(nodes []*filetree.FileNode) *types.DisabledReason {
|
||||
normalizedNodes := flattenSelectedNodesToFiles(nodes)
|
||||
|
||||
hasFileNodesWithConflicts := lo.SomeBy(normalizedNodes, func(node *filetree.FileNode) bool {
|
||||
return node.File != nil && node.File.HasInlineMergeConflicts
|
||||
})
|
||||
|
||||
if !hasFileNodesWithConflicts {
|
||||
return &types.DisabledReason{Text: self.c.Tr.NoFilesWithMergeConflicts}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FilesController) openCopyMenu() error {
|
||||
node := self.context().GetSelected()
|
||||
|
||||
@@ -1221,13 +1252,14 @@ func normalisedSelectedNodes(selectedNodes []*filetree.FileNode) []*filetree.Fil
|
||||
}
|
||||
|
||||
func isDescendentOfSelectedNodes(node *filetree.FileNode, selectedNodes []*filetree.FileNode) bool {
|
||||
nodePath := node.GetInternalPath()
|
||||
|
||||
for _, selectedNode := range selectedNodes {
|
||||
if selectedNode.IsFile() {
|
||||
continue
|
||||
}
|
||||
|
||||
selectedNodePath := selectedNode.GetPath()
|
||||
nodePath := node.GetPath()
|
||||
selectedNodePath := selectedNode.GetInternalPath()
|
||||
|
||||
if strings.HasPrefix(nodePath, selectedNodePath+"/") {
|
||||
return true
|
||||
@@ -1236,6 +1268,38 @@ func isDescendentOfSelectedNodes(node *filetree.FileNode, selectedNodes []*filet
|
||||
return false
|
||||
}
|
||||
|
||||
// BFS algorithm for expanding directories into their children,
|
||||
// and for collecting the unique file nodes
|
||||
func flattenSelectedNodesToFiles(selectedNodes []*filetree.FileNode) []*filetree.FileNode {
|
||||
queue := append(make([]*filetree.FileNode, 0, len(selectedNodes)), selectedNodes...)
|
||||
visited := set.New[string]()
|
||||
var files []*filetree.FileNode
|
||||
|
||||
for len(queue) > 0 {
|
||||
// pop node from queue
|
||||
node := queue[0]
|
||||
queue = queue[1:]
|
||||
|
||||
nodeID := node.ID()
|
||||
if visited.Includes(nodeID) {
|
||||
continue
|
||||
}
|
||||
visited.Add(nodeID)
|
||||
|
||||
if node.File != nil {
|
||||
// unique file node -> collect it
|
||||
files = append(files, node)
|
||||
continue
|
||||
}
|
||||
|
||||
// directory node -> enqueue children
|
||||
for _, ch := range node.Children {
|
||||
queue = append(queue, &filetree.FileNode{Node: ch})
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func someNodesHaveUnstagedChanges(nodes []*filetree.FileNode) bool {
|
||||
return lo.SomeBy(nodes, (*filetree.FileNode).GetHasUnstagedChanges)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
@@ -58,6 +60,13 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
||||
Handler: opts.Guards.NoPopupPanel(self.prevScreenMode),
|
||||
Description: self.c.Tr.PrevScreenMode,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.CyclePagers),
|
||||
Handler: opts.Guards.NoPopupPanel(self.cyclePagers),
|
||||
GetDisabledReason: self.canCyclePagers,
|
||||
Description: self.c.Tr.CyclePagers,
|
||||
Tooltip: self.c.Tr.CyclePagersTooltip,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Modifier: gocui.ModNone,
|
||||
@@ -171,6 +180,27 @@ func (self *GlobalController) prevScreenMode() error {
|
||||
return (&ScreenModeActions{c: self.c}).Prev()
|
||||
}
|
||||
|
||||
func (self *GlobalController) cyclePagers() error {
|
||||
self.c.State().GetPagerConfig().CyclePagers()
|
||||
if self.c.Context().CurrentSide().GetKey() == self.c.Context().Current().GetKey() {
|
||||
self.c.Context().CurrentSide().HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
current, total := self.c.State().GetPagerConfig().CurrentPagerIndex()
|
||||
self.c.Toast(fmt.Sprintf("Selected pager %d of %d", current+1, total))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *GlobalController) canCyclePagers() *types.DisabledReason {
|
||||
_, total := self.c.State().GetPagerConfig().CurrentPagerIndex()
|
||||
if total <= 1 {
|
||||
return &types.DisabledReason{
|
||||
Text: self.c.Tr.CyclePagersDisabledReason,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *GlobalController) createOptionsMenu() error {
|
||||
return (&OptionsMenuAction{c: self.c}).Call()
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func (self *CherryPickHelper) Paste() error {
|
||||
}),
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithWaitingStatusSync(self.c.Tr.CherryPickingStatus, func() error {
|
||||
mustStash := IsWorkingTreeDirty(self.c.Model().Files)
|
||||
mustStash := IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CherryPick)
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@ func NewConfirmationHelper(c *HelperCommon) *ConfirmationHelper {
|
||||
|
||||
func (self *ConfirmationHelper) wrappedConfirmationFunction(cancel goContext.CancelFunc, function func() error) func() error {
|
||||
return func() error {
|
||||
if self.c.GocuiGui().IsPasting {
|
||||
// The user is pasting multi-line text into a prompt; we don't want to handle the
|
||||
// line feeds as "confirm" keybindings. Simply ignoring them is the best we can do; this
|
||||
// will cause the entire pasted text to appear as a single line in the prompt. Hopefully
|
||||
// the user knows that ctrl-u allows them to delete it again...
|
||||
return nil
|
||||
}
|
||||
|
||||
cancel()
|
||||
|
||||
self.c.Context().Pop()
|
||||
|
||||
@@ -194,7 +194,7 @@ func parseDiff(diff string) ([]*hunk, []*hunk) {
|
||||
if strings.HasPrefix(line, "diff --git") {
|
||||
finishHunk()
|
||||
currentHunk = nil
|
||||
} else if strings.HasPrefix(line, "--- ") {
|
||||
} else if currentHunk == nil && strings.HasPrefix(line, "--- ") {
|
||||
// For some reason, the line ends with a tab character if the file
|
||||
// name contains spaces
|
||||
filename = strings.TrimRight(line[6:], "\t")
|
||||
|
||||
@@ -78,6 +78,26 @@ index 9ce8efb33..fb5e469e7 100644
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hunk with dashed lines",
|
||||
diff: `
|
||||
diff --git a/file1.txt b/file1.txt
|
||||
index 9ce8efb33..fb5e469e7 100644
|
||||
--- a/file1.txt
|
||||
+++ b/file1.txt
|
||||
@@ -3,1 +3,1 @@
|
||||
--- xxx
|
||||
+-- yyy
|
||||
`,
|
||||
expectedDeletedLineHunks: []*hunk{
|
||||
{
|
||||
filename: "file1.txt",
|
||||
startLineIdx: 3,
|
||||
numLines: 1,
|
||||
},
|
||||
},
|
||||
expectedAddedLineHunks: []*hunk{},
|
||||
},
|
||||
{
|
||||
name: "several hunks in different files",
|
||||
diff: `
|
||||
|
||||
@@ -157,7 +157,7 @@ func (self *MergeAndRebaseHelper) CheckMergeOrRebaseWithRefreshOptions(result er
|
||||
} else if strings.Contains(result.Error(), "No changes - did you forget to use") {
|
||||
return self.genericMergeCommand(REBASE_OPTION_SKIP)
|
||||
} else if strings.Contains(result.Error(), "The previous cherry-pick is now empty") {
|
||||
return self.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
||||
return self.genericMergeCommand(REBASE_OPTION_SKIP)
|
||||
} else if strings.Contains(result.Error(), "No rebase in progress?") {
|
||||
// assume in this case that we're already done
|
||||
return nil
|
||||
@@ -381,38 +381,103 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e
|
||||
return errors.New(self.c.Tr.CantMergeBranchIntoItself)
|
||||
}
|
||||
|
||||
wantFastForward, wantNonFastForward := self.fastForwardMergeUserPreference()
|
||||
canFastForward := self.c.Git().Branch.CanDoFastForwardMerge(refName)
|
||||
|
||||
var firstRegularMergeItem *types.MenuItem
|
||||
var secondRegularMergeItem *types.MenuItem
|
||||
var fastForwardMergeItem *types.MenuItem
|
||||
|
||||
if !wantNonFastForward && (wantFastForward || canFastForward) {
|
||||
firstRegularMergeItem = &types.MenuItem{
|
||||
Label: self.c.Tr.RegularMergeFastForward,
|
||||
OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR),
|
||||
Key: 'm',
|
||||
Tooltip: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.RegularMergeFastForwardTooltip,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": refName,
|
||||
},
|
||||
),
|
||||
}
|
||||
fastForwardMergeItem = firstRegularMergeItem
|
||||
|
||||
secondRegularMergeItem = &types.MenuItem{
|
||||
Label: self.c.Tr.RegularMergeNonFastForward,
|
||||
OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_NON_FAST_FORWARD),
|
||||
Key: 'n',
|
||||
Tooltip: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.RegularMergeNonFastForwardTooltip,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": refName,
|
||||
},
|
||||
),
|
||||
}
|
||||
} else {
|
||||
firstRegularMergeItem = &types.MenuItem{
|
||||
Label: self.c.Tr.RegularMergeNonFastForward,
|
||||
OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR),
|
||||
Key: 'm',
|
||||
Tooltip: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.RegularMergeNonFastForwardTooltip,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": refName,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
secondRegularMergeItem = &types.MenuItem{
|
||||
Label: self.c.Tr.RegularMergeFastForward,
|
||||
OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_FAST_FORWARD),
|
||||
Key: 'f',
|
||||
Tooltip: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.RegularMergeFastForwardTooltip,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": refName,
|
||||
},
|
||||
),
|
||||
}
|
||||
fastForwardMergeItem = secondRegularMergeItem
|
||||
}
|
||||
|
||||
if !canFastForward {
|
||||
fastForwardMergeItem.DisabledReason = &types.DisabledReason{
|
||||
Text: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.CannotFastForwardMerge,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": refName,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.Merge,
|
||||
Items: []*types.MenuItem{
|
||||
firstRegularMergeItem,
|
||||
secondRegularMergeItem,
|
||||
{
|
||||
Label: self.c.Tr.RegularMerge,
|
||||
OnPress: self.RegularMerge(refName),
|
||||
Key: 'm',
|
||||
Tooltip: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.RegularMergeTooltip,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": refName,
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.SquashMergeUncommittedTitle,
|
||||
Label: self.c.Tr.SquashMergeUncommitted,
|
||||
OnPress: self.SquashMergeUncommitted(refName),
|
||||
Key: 's',
|
||||
Tooltip: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.SquashMergeUncommitted,
|
||||
self.c.Tr.SquashMergeUncommittedTooltip,
|
||||
map[string]string{
|
||||
"selectedBranch": refName,
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.SquashMergeCommittedTitle,
|
||||
Label: self.c.Tr.SquashMergeCommitted,
|
||||
OnPress: self.SquashMergeCommitted(refName, checkedOutBranchName),
|
||||
Key: 'S',
|
||||
Tooltip: utils.ResolvePlaceholderString(
|
||||
self.c.Tr.SquashMergeCommitted,
|
||||
self.c.Tr.SquashMergeCommittedTooltip,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": refName,
|
||||
@@ -423,10 +488,10 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e
|
||||
})
|
||||
}
|
||||
|
||||
func (self *MergeAndRebaseHelper) RegularMerge(refName string) func() error {
|
||||
func (self *MergeAndRebaseHelper) RegularMerge(refName string, variant git_commands.MergeVariant) func() error {
|
||||
return func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.Merge)
|
||||
err := self.c.Git().Branch.Merge(refName, git_commands.MergeOpts{})
|
||||
err := self.c.Git().Branch.Merge(refName, variant)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
}
|
||||
}
|
||||
@@ -434,7 +499,7 @@ func (self *MergeAndRebaseHelper) RegularMerge(refName string) func() error {
|
||||
func (self *MergeAndRebaseHelper) SquashMergeUncommitted(refName string) func() error {
|
||||
return func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.SquashMerge)
|
||||
err := self.c.Git().Branch.Merge(refName, git_commands.MergeOpts{Squash: true})
|
||||
err := self.c.Git().Branch.Merge(refName, git_commands.MERGE_VARIANT_SQUASH)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
}
|
||||
}
|
||||
@@ -442,7 +507,7 @@ func (self *MergeAndRebaseHelper) SquashMergeUncommitted(refName string) func()
|
||||
func (self *MergeAndRebaseHelper) SquashMergeCommitted(refName, checkedOutBranchName string) func() error {
|
||||
return func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.SquashMerge)
|
||||
err := self.c.Git().Branch.Merge(refName, git_commands.MergeOpts{Squash: true})
|
||||
err := self.c.Git().Branch.Merge(refName, git_commands.MERGE_VARIANT_SQUASH)
|
||||
if err = self.CheckMergeOrRebase(err); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -459,6 +524,31 @@ func (self *MergeAndRebaseHelper) SquashMergeCommitted(refName, checkedOutBranch
|
||||
}
|
||||
}
|
||||
|
||||
// Returns wantsFastForward, wantsNonFastForward. These will never both be true, but they can both be false.
|
||||
func (self *MergeAndRebaseHelper) fastForwardMergeUserPreference() (bool, bool) {
|
||||
// Check user config first, because it takes precedence over git config
|
||||
mergingArgs := self.c.UserConfig().Git.Merging.Args
|
||||
if strings.Contains(mergingArgs, "--ff") { // also covers "--ff-only"
|
||||
return true, false
|
||||
}
|
||||
|
||||
if strings.Contains(mergingArgs, "--no-ff") {
|
||||
return false, true
|
||||
}
|
||||
|
||||
// Then check git config
|
||||
mergeFfConfig := self.c.Git().Config.GetMergeFF()
|
||||
if mergeFfConfig == "true" || mergeFfConfig == "only" {
|
||||
return true, false
|
||||
}
|
||||
|
||||
if mergeFfConfig == "false" {
|
||||
return false, true
|
||||
}
|
||||
|
||||
return false, false
|
||||
}
|
||||
|
||||
func (self *MergeAndRebaseHelper) ResetMarkedBaseCommit() error {
|
||||
self.c.Modes().MarkedBaseCommit.Reset()
|
||||
self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
|
||||
@@ -163,6 +163,15 @@ func (self *RefsHelper) CheckoutRemoteBranch(fullBranchName string, localBranchN
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RefsHelper) CheckoutPreviousRef() error {
|
||||
previousRef, err := self.c.Git().Branch.PreviousRef()
|
||||
if err == nil && strings.HasPrefix(previousRef, "refs/heads/") {
|
||||
return self.CheckoutRef(strings.TrimPrefix(previousRef, "refs/heads/"), types.CheckoutRefOptions{})
|
||||
}
|
||||
|
||||
return self.CheckoutRef("-", types.CheckoutRefOptions{})
|
||||
}
|
||||
|
||||
func (self *RefsHelper) GetCheckedOutRef() *models.Branch {
|
||||
if len(self.c.Model().Branches) == 0 {
|
||||
return nil
|
||||
@@ -249,7 +258,7 @@ func (self *RefsHelper) CreateGitResetMenu(name string, ref string) error {
|
||||
style.FgRed.Sprintf("reset --%s %s", row.strength, name),
|
||||
},
|
||||
OnPress: func() error {
|
||||
return self.c.ConfirmIf(row.strength == "hard" && IsWorkingTreeDirty(self.c.Model().Files),
|
||||
return self.c.ConfirmIf(row.strength == "hard" && IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules),
|
||||
types.ConfirmOpts{
|
||||
Title: self.c.Tr.Actions.HardReset,
|
||||
Prompt: self.c.Tr.ResetHardConfirmation,
|
||||
@@ -475,7 +484,7 @@ func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(newBranchNa
|
||||
return err
|
||||
}
|
||||
|
||||
mustStash := IsWorkingTreeDirty(self.c.Model().Files)
|
||||
mustStash := IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
if mustStash {
|
||||
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
|
||||
return err
|
||||
@@ -508,7 +517,7 @@ func (self *RefsHelper) moveCommitsToNewBranchOffOfMainBranch(newBranchName stri
|
||||
return commit.Status == models.StatusUnpushed
|
||||
})
|
||||
|
||||
mustStash := IsWorkingTreeDirty(self.c.Model().Files)
|
||||
mustStash := IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
if mustStash {
|
||||
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
|
||||
return err
|
||||
|
||||
@@ -43,10 +43,9 @@ type WindowArrangementArgs struct {
|
||||
Height int
|
||||
// User config
|
||||
UserConfig *config.UserConfig
|
||||
// Name of the currently focused window
|
||||
// Name of the currently focused window. (It's actually the current static window, meaning
|
||||
// popups are ignored)
|
||||
CurrentWindow string
|
||||
// Name of the current static window (meaning popups are ignored)
|
||||
CurrentStaticWindow string
|
||||
// Name of the current side window (i.e. the current window in the left
|
||||
// section of the UI)
|
||||
CurrentSideWindow string
|
||||
@@ -86,21 +85,20 @@ func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string,
|
||||
}
|
||||
|
||||
args := WindowArrangementArgs{
|
||||
Width: width,
|
||||
Height: height,
|
||||
UserConfig: self.c.UserConfig(),
|
||||
CurrentWindow: self.windowHelper.CurrentWindow(),
|
||||
CurrentSideWindow: self.c.Context().CurrentSide().GetWindowName(),
|
||||
CurrentStaticWindow: self.c.Context().CurrentStatic().GetWindowName(),
|
||||
SplitMainPanel: repoState.GetSplitMainPanel(),
|
||||
ScreenMode: repoState.GetScreenMode(),
|
||||
AppStatus: appStatus,
|
||||
InformationStr: informationStr,
|
||||
ShowExtrasWindow: self.c.State().GetShowExtrasWindow(),
|
||||
InDemo: self.c.InDemo(),
|
||||
IsAnyModeActive: self.modeHelper.IsAnyModeActive(),
|
||||
InSearchPrompt: repoState.InSearchPrompt(),
|
||||
SearchPrefix: searchPrefix,
|
||||
Width: width,
|
||||
Height: height,
|
||||
UserConfig: self.c.UserConfig(),
|
||||
CurrentWindow: self.c.Context().CurrentStatic().GetWindowName(),
|
||||
CurrentSideWindow: self.c.Context().CurrentSide().GetWindowName(),
|
||||
SplitMainPanel: repoState.GetSplitMainPanel(),
|
||||
ScreenMode: repoState.GetScreenMode(),
|
||||
AppStatus: appStatus,
|
||||
InformationStr: informationStr,
|
||||
ShowExtrasWindow: self.c.State().GetShowExtrasWindow(),
|
||||
InDemo: self.c.InDemo(),
|
||||
IsAnyModeActive: self.modeHelper.IsAnyModeActive(),
|
||||
InSearchPrompt: repoState.InSearchPrompt(),
|
||||
SearchPrefix: searchPrefix,
|
||||
}
|
||||
|
||||
return GetWindowDimensions(args)
|
||||
@@ -393,7 +391,7 @@ func splitMainPanelSideBySide(args WindowArrangementArgs) bool {
|
||||
func getExtrasWindowSize(args WindowArrangementArgs) int {
|
||||
var baseSize int
|
||||
// The 'extras' window contains the command log context
|
||||
if args.CurrentStaticWindow == "extras" {
|
||||
if args.CurrentWindow == "extras" {
|
||||
baseSize = 1000 // my way of saying 'fill the available space'
|
||||
} else if args.Height < 40 {
|
||||
baseSize = 1
|
||||
|
||||
@@ -19,21 +19,20 @@ import (
|
||||
func TestGetWindowDimensions(t *testing.T) {
|
||||
getDefaultArgs := func() WindowArrangementArgs {
|
||||
return WindowArrangementArgs{
|
||||
Width: 75,
|
||||
Height: 30,
|
||||
UserConfig: config.GetDefaultConfig(),
|
||||
CurrentWindow: "files",
|
||||
CurrentSideWindow: "files",
|
||||
CurrentStaticWindow: "files",
|
||||
SplitMainPanel: false,
|
||||
ScreenMode: types.SCREEN_NORMAL,
|
||||
AppStatus: "",
|
||||
InformationStr: "information",
|
||||
ShowExtrasWindow: false,
|
||||
InDemo: false,
|
||||
IsAnyModeActive: false,
|
||||
InSearchPrompt: false,
|
||||
SearchPrefix: "",
|
||||
Width: 75,
|
||||
Height: 30,
|
||||
UserConfig: config.GetDefaultConfig(),
|
||||
CurrentWindow: "files",
|
||||
CurrentSideWindow: "files",
|
||||
SplitMainPanel: false,
|
||||
ScreenMode: types.SCREEN_NORMAL,
|
||||
AppStatus: "",
|
||||
InformationStr: "information",
|
||||
ShowExtrasWindow: false,
|
||||
InDemo: false,
|
||||
IsAnyModeActive: false,
|
||||
InSearchPrompt: false,
|
||||
SearchPrefix: "",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,21 +3,24 @@ package helpers
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type WorkingTreeHelper struct {
|
||||
c *HelperCommon
|
||||
refHelper *RefsHelper
|
||||
commitsHelper *CommitsHelper
|
||||
gpgHelper *GpgHelper
|
||||
c *HelperCommon
|
||||
refHelper *RefsHelper
|
||||
commitsHelper *CommitsHelper
|
||||
gpgHelper *GpgHelper
|
||||
mergeAndRebaseHelper *MergeAndRebaseHelper
|
||||
}
|
||||
|
||||
func NewWorkingTreeHelper(
|
||||
@@ -25,12 +28,14 @@ func NewWorkingTreeHelper(
|
||||
refHelper *RefsHelper,
|
||||
commitsHelper *CommitsHelper,
|
||||
gpgHelper *GpgHelper,
|
||||
mergeAndRebaseHelper *MergeAndRebaseHelper,
|
||||
) *WorkingTreeHelper {
|
||||
return &WorkingTreeHelper{
|
||||
c: c,
|
||||
refHelper: refHelper,
|
||||
commitsHelper: commitsHelper,
|
||||
gpgHelper: gpgHelper,
|
||||
c: c,
|
||||
refHelper: refHelper,
|
||||
commitsHelper: commitsHelper,
|
||||
gpgHelper: gpgHelper,
|
||||
mergeAndRebaseHelper: mergeAndRebaseHelper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +47,14 @@ func AnyStagedFiles(files []*models.File) bool {
|
||||
return lo.SomeBy(files, func(f *models.File) bool { return f.HasStagedChanges })
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) AnyStagedFilesExceptSubmodules() bool {
|
||||
return AnyStagedFilesExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
}
|
||||
|
||||
func AnyStagedFilesExceptSubmodules(files []*models.File, submoduleConfigs []*models.SubmoduleConfig) bool {
|
||||
return lo.SomeBy(files, func(f *models.File) bool { return f.HasStagedChanges && !f.IsSubmodule(submoduleConfigs) })
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) AnyTrackedFiles() bool {
|
||||
return AnyTrackedFiles(self.c.Model().Files)
|
||||
}
|
||||
@@ -50,12 +63,20 @@ func AnyTrackedFiles(files []*models.File) bool {
|
||||
return lo.SomeBy(files, func(f *models.File) bool { return f.Tracked })
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) IsWorkingTreeDirty() bool {
|
||||
return IsWorkingTreeDirty(self.c.Model().Files)
|
||||
func (self *WorkingTreeHelper) AnyTrackedFilesExceptSubmodules() bool {
|
||||
return AnyTrackedFilesExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
}
|
||||
|
||||
func IsWorkingTreeDirty(files []*models.File) bool {
|
||||
return AnyStagedFiles(files) || AnyTrackedFiles(files)
|
||||
func AnyTrackedFilesExceptSubmodules(files []*models.File, submoduleConfigs []*models.SubmoduleConfig) bool {
|
||||
return lo.SomeBy(files, func(f *models.File) bool { return f.Tracked && !f.IsSubmodule(submoduleConfigs) })
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) IsWorkingTreeDirtyExceptSubmodules() bool {
|
||||
return IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
}
|
||||
|
||||
func IsWorkingTreeDirtyExceptSubmodules(files []*models.File, submoduleConfigs []*models.SubmoduleConfig) bool {
|
||||
return AnyStagedFilesExceptSubmodules(files, submoduleConfigs) || AnyTrackedFilesExceptSubmodules(files, submoduleConfigs)
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) FileForSubmodule(submodule *models.SubmoduleConfig) *models.File {
|
||||
@@ -247,3 +268,135 @@ func (self *WorkingTreeHelper) commitPrefixConfigsForRepo() []config.CommitPrefi
|
||||
|
||||
return self.c.UserConfig().Git.CommitPrefix
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) mergeFile(filepath string, strategy string) (string, error) {
|
||||
if self.c.Git().Version.IsOlderThan(2, 43, 0) {
|
||||
return self.mergeFileWithTempFiles(filepath, strategy)
|
||||
}
|
||||
|
||||
return self.mergeFileWithObjectIDs(filepath, strategy)
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) mergeFileWithTempFiles(filepath string, strategy string) (string, error) {
|
||||
showToTempFile := func(stage int, label string) (string, error) {
|
||||
output, err := self.c.Git().WorkingTree.ShowFileAtStage(filepath, stage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f, err := os.CreateTemp(self.c.GetConfig().GetTempDir(), "mergefile-"+label+"-*")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write([]byte(output)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
baseFilepath, err := showToTempFile(1, "base")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(baseFilepath)
|
||||
|
||||
oursFilepath, err := showToTempFile(2, "ours")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(oursFilepath)
|
||||
|
||||
theirsFilepath, err := showToTempFile(3, "theirs")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(theirsFilepath)
|
||||
|
||||
return self.c.Git().WorkingTree.MergeFileForFiles(strategy, oursFilepath, baseFilepath, theirsFilepath)
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) mergeFileWithObjectIDs(filepath, strategy string) (string, error) {
|
||||
baseID, err := self.c.Git().WorkingTree.ObjectIDAtStage(filepath, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
oursID, err := self.c.Git().WorkingTree.ObjectIDAtStage(filepath, 2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
theirsID, err := self.c.Git().WorkingTree.ObjectIDAtStage(filepath, 3)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return self.c.Git().WorkingTree.MergeFileForObjectIDs(strategy, oursID, baseID, theirsID)
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []string) error {
|
||||
onMergeStrategySelected := func(strategy string) error {
|
||||
for _, filepath := range selectedFilepaths {
|
||||
output, err := self.mergeFile(filepath, strategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath, []byte(output), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := self.c.Git().WorkingTree.StageFiles(selectedFilepaths, nil)
|
||||
self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
return err
|
||||
}
|
||||
|
||||
cmdColor := style.FgBlue
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.MergeConflictOptionsTitle,
|
||||
Items: []*types.MenuItem{
|
||||
{
|
||||
LabelColumns: []string{
|
||||
self.c.Tr.UseCurrentChanges,
|
||||
cmdColor.Sprint("git merge-file --ours"),
|
||||
},
|
||||
OnPress: func() error {
|
||||
return onMergeStrategySelected("--ours")
|
||||
},
|
||||
Key: 'c',
|
||||
},
|
||||
{
|
||||
LabelColumns: []string{
|
||||
self.c.Tr.UseIncomingChanges,
|
||||
cmdColor.Sprint("git merge-file --theirs"),
|
||||
},
|
||||
OnPress: func() error {
|
||||
return onMergeStrategySelected("--theirs")
|
||||
},
|
||||
Key: 'i',
|
||||
},
|
||||
{
|
||||
LabelColumns: []string{
|
||||
self.c.Tr.UseBothChanges,
|
||||
cmdColor.Sprint("git merge-file --union"),
|
||||
},
|
||||
OnPress: func() error {
|
||||
return onMergeStrategySelected("--union")
|
||||
},
|
||||
Key: 'b',
|
||||
},
|
||||
{
|
||||
LabelColumns: []string{
|
||||
self.c.Tr.OpenMergeTool,
|
||||
cmdColor.Sprint("git mergetool"),
|
||||
},
|
||||
OnPress: self.OpenMergeTool,
|
||||
Key: 'm',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -189,7 +189,8 @@ func (self *WorktreeHelper) Remove(worktree *models.Worktree, force bool) error
|
||||
self.c.LogAction(self.c.Tr.RemoveWorktree)
|
||||
if err := self.c.Git().Worktree.Delete(worktree.Path, force); err != nil {
|
||||
errMessage := err.Error()
|
||||
if !strings.Contains(errMessage, "--force") {
|
||||
if !strings.Contains(errMessage, "--force") &&
|
||||
!strings.Contains(errMessage, "fatal: working trees containing submodules cannot be moved or removed") {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -856,7 +856,7 @@ func (self *LocalCommitsController) revert(commits []*models.Commit, start, end
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
|
||||
return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error {
|
||||
mustStash := helpers.IsWorkingTreeDirty(self.c.Model().Files)
|
||||
mustStash := helpers.IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
|
||||
if mustStash {
|
||||
if err := self.c.Git().Stash.Push(self.c.Tr.AutoStashForReverting); err != nil {
|
||||
|
||||
@@ -112,10 +112,11 @@ func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts)
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
||||
Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
|
||||
Description: self.c.Tr.OpenMergeTool,
|
||||
Tooltip: self.c.Tr.OpenMergeToolTooltip,
|
||||
Key: opts.GetKey(opts.Config.Files.OpenMergeOptions),
|
||||
Handler: self.openMergeConflictMenu,
|
||||
Description: self.c.Tr.ViewMergeConflictOptions,
|
||||
Tooltip: self.c.Tr.ViewMergeConflictOptionsTooltip,
|
||||
OpensMenu: true,
|
||||
DisplayOnScreen: true,
|
||||
},
|
||||
{
|
||||
@@ -320,6 +321,11 @@ func (self *MergeConflictsController) onLastConflictResolved() {
|
||||
self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
|
||||
func (self *MergeConflictsController) openMergeConflictMenu() error {
|
||||
filepath := self.context().GetState().GetPath()
|
||||
return self.c.Helpers().WorkingTree.CreateMergeConflictMenu([]string{filepath})
|
||||
}
|
||||
|
||||
func (self *MergeConflictsController) withRenderAndFocus(f func() error) func() error {
|
||||
return self.withLock(func() error {
|
||||
if err := f(); err != nil {
|
||||
|
||||
@@ -51,6 +51,7 @@ func (self *OptionsMenuAction) Call() error {
|
||||
HideCancel: true,
|
||||
ColumnAlignment: []utils.Alignment{utils.AlignRight, utils.AlignLeft},
|
||||
AllowFilteringKeybindings: true,
|
||||
KeepConfirmKeybindings: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -181,10 +181,14 @@ func (self *StatusController) showAllBranchLogs() {
|
||||
cmdObj := self.c.Git().Branch.AllBranchesLogCmdObj()
|
||||
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
|
||||
title := self.c.Tr.LogTitle
|
||||
if i, n := self.c.Git().Branch.GetAllBranchesLogIdxAndCount(); n > 1 {
|
||||
title = fmt.Sprintf(self.c.Tr.LogXOfYTitle, i+1, n)
|
||||
}
|
||||
self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
Pair: self.c.MainViewPairs().Normal,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Title: self.c.Tr.LogTitle,
|
||||
Title: title,
|
||||
Task: task,
|
||||
},
|
||||
})
|
||||
@@ -196,7 +200,7 @@ func (self *StatusController) switchToOrRotateAllBranchesLogs() {
|
||||
// A bit of a hack to ensure we only rotate to the next branch log command
|
||||
// if we currently are looking at a branch log. Otherwise, we should just show
|
||||
// the current index (if we are coming from the dashboard).
|
||||
if self.c.Views().Main.Title == self.c.Tr.LogTitle {
|
||||
if self.c.Views().Main.Title != self.c.Tr.StatusTitle {
|
||||
self.c.Git().Branch.RotateAllBranchesLogIdx()
|
||||
}
|
||||
self.showAllBranchLogs()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
@@ -90,6 +92,15 @@ func (self *SwitchToDiffFilesController) enter() error {
|
||||
Scope: []types.RefreshableView{types.COMMIT_FILES},
|
||||
})
|
||||
|
||||
if filterPath := self.c.Modes().Filtering.GetPath(); filterPath != "" {
|
||||
path, err := filepath.Rel(self.c.Git().RepoPaths.RepoPath(), filterPath)
|
||||
if err != nil {
|
||||
path = filterPath
|
||||
}
|
||||
commitFilesContext.CommitFileTreeViewModel.SelectPath(
|
||||
filepath.ToSlash(path), self.c.UserConfig().Gui.ShowRootItemInFileTree)
|
||||
}
|
||||
|
||||
self.c.Context().Push(commitFilesContext, types.OnFocusOpts{})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ func (self *UndoController) hardResetWithAutoStash(commitHash string, options ha
|
||||
}
|
||||
|
||||
// if we have any modified tracked files we need to auto-stash
|
||||
dirtyWorkingTree := self.c.Helpers().WorkingTree.IsWorkingTreeDirty()
|
||||
dirtyWorkingTree := self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules()
|
||||
if dirtyWorkingTree {
|
||||
return self.c.WithWaitingStatus(options.WaitingStatus, func(gocui.Task) error {
|
||||
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForUndo, utils.ShortHash(commitHash))); err != nil {
|
||||
|
||||
@@ -100,7 +100,7 @@ func (self *FilesController) createResetMenu() error {
|
||||
Tooltip: self.c.Tr.DiscardStagedChangesDescription,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RemoveStagedFiles)
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules() {
|
||||
return errors.New(self.c.Tr.NoTrackedStagedFilesStash)
|
||||
}
|
||||
if err := self.c.Git().Stash.SaveStagedChanges("[lazygit] tmp stash"); err != nil {
|
||||
@@ -159,7 +159,7 @@ func (self *FilesController) createResetMenu() error {
|
||||
red.Sprint("git reset --hard HEAD"),
|
||||
},
|
||||
OnPress: func() error {
|
||||
return self.c.ConfirmIf(helpers.IsWorkingTreeDirty(self.c.Model().Files),
|
||||
return self.c.ConfirmIf(helpers.IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules),
|
||||
types.ConfirmOpts{
|
||||
Title: self.c.Tr.Actions.HardReset,
|
||||
Prompt: self.c.Tr.ResetHardConfirmation,
|
||||
|
||||
@@ -162,9 +162,13 @@ func join(strs []string) string {
|
||||
}
|
||||
|
||||
func SplitFileTreePath(path string, showRootItem bool) []string {
|
||||
return split(InternalTreePathForFilePath(path, showRootItem))
|
||||
}
|
||||
|
||||
func InternalTreePathForFilePath(path string, showRootItem bool) string {
|
||||
if showRootItem {
|
||||
return split("./" + path)
|
||||
return "./" + path
|
||||
}
|
||||
|
||||
return split(path)
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -191,3 +191,15 @@ func (self *CommitFileTreeViewModel) ExpandAll() {
|
||||
self.SetSelectedLineIdx(index)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to select the given path if present. If it doesn't exist, or one of the parent directories is
|
||||
// collapsed, do nothing.
|
||||
// Note that filepath is an actual file path, not an internal tree path as with e.g.
|
||||
// ToggleCollapsed. It must be a relative path (relative to the repo root), and it must contain
|
||||
// forward slashes rather than backslashes even on Windows.
|
||||
func (self *CommitFileTreeViewModel) SelectPath(filepath string, showRootItem bool) {
|
||||
index, found := self.GetIndexForPath(InternalTreePathForFilePath(filepath, showRootItem))
|
||||
if found {
|
||||
self.SetSelection(index)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -69,6 +69,8 @@ type Gui struct {
|
||||
// this is the state of the GUI for the current repo
|
||||
State *GuiRepoState
|
||||
|
||||
pagerConfig *config.PagerConfig
|
||||
|
||||
CustomCommandsClient *custom_commands.Client
|
||||
|
||||
// this is a mapping of repos to gui states, so that we can restore the original
|
||||
@@ -169,6 +171,10 @@ func (self *StateAccessor) GetRepoState() types.IRepoStateAccessor {
|
||||
return self.gui.State
|
||||
}
|
||||
|
||||
func (self *StateAccessor) GetPagerConfig() *config.PagerConfig {
|
||||
return self.gui.pagerConfig
|
||||
}
|
||||
|
||||
func (self *StateAccessor) GetIsRefreshingFiles() bool {
|
||||
return self.gui.IsRefreshingFiles
|
||||
}
|
||||
@@ -307,6 +313,7 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context
|
||||
gui.gitVersion,
|
||||
gui.os,
|
||||
git_config.NewStdCachedGitConfig(gui.Log),
|
||||
gui.pagerConfig,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -653,7 +660,7 @@ func (gui *Gui) Contexts() *context.ContextTree {
|
||||
// NewGui builds a new gui handler
|
||||
func NewGui(
|
||||
cmn *common.Common,
|
||||
config config.AppConfigurer,
|
||||
configurer config.AppConfigurer,
|
||||
gitVersion *git_commands.GitVersion,
|
||||
updater *updates.Updater,
|
||||
showRecentRepos bool,
|
||||
@@ -663,7 +670,7 @@ func NewGui(
|
||||
gui := &Gui{
|
||||
Common: cmn,
|
||||
gitVersion: gitVersion,
|
||||
Config: config,
|
||||
Config: configurer,
|
||||
Updater: updater,
|
||||
statusManager: status.NewStatusManager(),
|
||||
viewBufferManagerMap: map[string]*tasks.ViewBufferManager{},
|
||||
@@ -713,7 +720,7 @@ func NewGui(
|
||||
credentialsHelper.PromptUserForCredential,
|
||||
)
|
||||
|
||||
osCommand := oscommands.NewOSCommand(cmn, config, oscommands.GetPlatform(), guiIO)
|
||||
osCommand := oscommands.NewOSCommand(cmn, configurer, oscommands.GetPlatform(), guiIO)
|
||||
|
||||
gui.os = osCommand
|
||||
|
||||
@@ -724,6 +731,8 @@ func NewGui(
|
||||
gui.BackgroundRoutineMgr = &BackgroundRoutineMgr{gui: gui}
|
||||
gui.stateAccessor = &StateAccessor{gui: gui}
|
||||
|
||||
gui.pagerConfig = config.NewPagerConfig(func() *config.UserConfig { return gui.UserConfig() })
|
||||
|
||||
return gui, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
|
||||
maxColumnSize = max(maxColumnSize, len(item.LabelColumns))
|
||||
|
||||
// Remove all item keybindings that are the same as the confirm binding
|
||||
if item.Key == confirmKey {
|
||||
if item.Key == confirmKey && !opts.KeepConfirmKeybindings {
|
||||
item.Key = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@ import (
|
||||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func makeAtomic(v int32) (result atomic.Int32) {
|
||||
func makeAtomic(v int32) *atomic.Int32 {
|
||||
var result atomic.Int32
|
||||
result.Store(v)
|
||||
return //nolint: nakedret
|
||||
return &result
|
||||
}
|
||||
|
||||
func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
@@ -109,7 +110,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
branch: &models.Branch{
|
||||
Name: "branch_name",
|
||||
Recency: "1m",
|
||||
BehindBaseBranch: makeAtomic(2),
|
||||
BehindBaseBranch: *makeAtomic(2),
|
||||
},
|
||||
itemOperation: types.ItemOperationNone,
|
||||
fullDescription: false,
|
||||
@@ -126,7 +127,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
UpstreamRemote: "origin",
|
||||
AheadForPull: "0",
|
||||
BehindForPull: "0",
|
||||
BehindBaseBranch: makeAtomic(2),
|
||||
BehindBaseBranch: *makeAtomic(2),
|
||||
},
|
||||
itemOperation: types.ItemOperationNone,
|
||||
fullDescription: false,
|
||||
@@ -143,7 +144,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
UpstreamRemote: "origin",
|
||||
AheadForPull: "3",
|
||||
BehindForPull: "5",
|
||||
BehindBaseBranch: makeAtomic(2),
|
||||
BehindBaseBranch: *makeAtomic(2),
|
||||
},
|
||||
itemOperation: types.ItemOperationNone,
|
||||
fullDescription: false,
|
||||
@@ -247,7 +248,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
UpstreamRemote: "origin",
|
||||
AheadForPull: "3",
|
||||
BehindForPull: "5",
|
||||
BehindBaseBranch: makeAtomic(4),
|
||||
BehindBaseBranch: *makeAtomic(4),
|
||||
},
|
||||
itemOperation: types.ItemOperationNone,
|
||||
fullDescription: false,
|
||||
|
||||
+6
-5
@@ -45,11 +45,12 @@ func (gui *Gui) onResize() error {
|
||||
// command.
|
||||
func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error {
|
||||
width := view.InnerWidth()
|
||||
pager := gui.git.Config.GetPager(width)
|
||||
externalDiffCommand := gui.Config.GetUserConfig().Git.Paging.ExternalDiffCommand
|
||||
pager := gui.stateAccessor.GetPagerConfig().GetPagerCommand(width)
|
||||
externalDiffCommand := gui.stateAccessor.GetPagerConfig().GetExternalDiffCommand()
|
||||
useExtDiffGitConfig := gui.stateAccessor.GetPagerConfig().GetUseExternalDiffGitConfig()
|
||||
|
||||
if pager == "" && externalDiffCommand == "" {
|
||||
// if we're not using a custom pager we don't need to use a pty
|
||||
if pager == "" && externalDiffCommand == "" && !useExtDiffGitConfig {
|
||||
// If we're not using a custom pager nor external diff command, then we don't need to use a pty
|
||||
return gui.newCmdTask(view, cmd, prefix)
|
||||
}
|
||||
|
||||
@@ -58,7 +59,7 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
|
||||
// Need to get the width and the pager again because the layout might have
|
||||
// changed the size of the view
|
||||
width = view.InnerWidth()
|
||||
pager = gui.git.Config.GetPager(width)
|
||||
pager := gui.stateAccessor.GetPagerConfig().GetPagerCommand(width)
|
||||
|
||||
cmdStr := strings.Join(cmd.Args, " ")
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
@@ -11,5 +12,6 @@ func (gui *Gui) onResize() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error {
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("LAZYGIT_COLUMNS=%d", view.InnerWidth()))
|
||||
return gui.newCmdTask(view, cmd, prefix)
|
||||
}
|
||||
|
||||
@@ -156,6 +156,7 @@ type CreateMenuOptions struct {
|
||||
HideCancel bool
|
||||
ColumnAlignment []utils.Alignment
|
||||
AllowFilteringKeybindings bool
|
||||
KeepConfirmKeybindings bool // if true, the keybindings that match the confirm binding will not be removed from menu items
|
||||
}
|
||||
|
||||
type CreatePopupPanelOpts struct {
|
||||
@@ -355,6 +356,7 @@ type HasUrn interface {
|
||||
type IStateAccessor interface {
|
||||
GetRepoPathStack() *utils.StringStack
|
||||
GetRepoState() IRepoStateAccessor
|
||||
GetPagerConfig() *config.PagerConfig
|
||||
// tells us whether we're currently updating lazygit
|
||||
GetUpdating() bool
|
||||
SetUpdating(bool)
|
||||
|
||||
@@ -157,6 +157,11 @@ func (gui *Gui) postRefreshUpdate(c types.Context) {
|
||||
sidePanelContext.HandleRenderToMain()
|
||||
}
|
||||
}
|
||||
} else if c.GetKey() == gui.State.ContextMgr.CurrentStatic().GetKey() {
|
||||
// If our view is not the current one, but it is the current static context, then this
|
||||
// can only mean that a popup is showing. In that case we want to refresh the main view
|
||||
// behind the popup.
|
||||
c.HandleRenderToMain()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-16
@@ -23,13 +23,9 @@ type TranslationSet struct {
|
||||
StagedChanges string
|
||||
StagingTitle string
|
||||
MergingTitle string
|
||||
SquashMergeUncommittedTitle string
|
||||
SquashMergeCommittedTitle string
|
||||
SquashMergeUncommitted string
|
||||
SquashMergeCommitted string
|
||||
RegularMergeTooltip string
|
||||
NormalTitle string
|
||||
LogTitle string
|
||||
LogXOfYTitle string
|
||||
CommitSummary string
|
||||
CredentialsUsername string
|
||||
CredentialsPassword string
|
||||
@@ -63,7 +59,6 @@ type TranslationSet struct {
|
||||
ToggleTreeViewTooltip string
|
||||
OpenDiffTool string
|
||||
OpenMergeTool string
|
||||
OpenMergeToolTooltip string
|
||||
Refresh string
|
||||
RefreshTooltip string
|
||||
Push string
|
||||
@@ -268,8 +263,16 @@ type TranslationSet struct {
|
||||
RefreshFiles string
|
||||
FocusMainView string
|
||||
Merge string
|
||||
RegularMerge string
|
||||
MergeBranchTooltip string
|
||||
RegularMergeFastForward string
|
||||
RegularMergeFastForwardTooltip string
|
||||
CannotFastForwardMerge string
|
||||
RegularMergeNonFastForward string
|
||||
RegularMergeNonFastForwardTooltip string
|
||||
SquashMergeUncommitted string
|
||||
SquashMergeUncommittedTooltip string
|
||||
SquashMergeCommitted string
|
||||
SquashMergeCommittedTooltip string
|
||||
ConfirmQuit string
|
||||
SwitchRepo string
|
||||
AllBranchesLogGraph string
|
||||
@@ -585,6 +588,9 @@ type TranslationSet struct {
|
||||
ViewResetToUpstreamOptions string
|
||||
NextScreenMode string
|
||||
PrevScreenMode string
|
||||
CyclePagers string
|
||||
CyclePagersTooltip string
|
||||
CyclePagersDisabledReason string
|
||||
StartSearch string
|
||||
StartFilter string
|
||||
Keybindings string
|
||||
@@ -898,6 +904,13 @@ type TranslationSet struct {
|
||||
BreakingChangesTitle string
|
||||
BreakingChangesMessage string
|
||||
BreakingChangesByVersion map[string]string
|
||||
ViewMergeConflictOptions string
|
||||
ViewMergeConflictOptionsTooltip string
|
||||
NoFilesWithMergeConflicts string
|
||||
MergeConflictOptionsTitle string
|
||||
UseCurrentChanges string
|
||||
UseIncomingChanges string
|
||||
UseBothChanges string
|
||||
}
|
||||
|
||||
type Bisect struct {
|
||||
@@ -1098,12 +1111,11 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
EasterEgg: "Easter egg",
|
||||
UnstagedChanges: "Unstaged changes",
|
||||
StagedChanges: "Staged changes",
|
||||
SquashMergeUncommittedTitle: "Squash merge and leave uncommitted",
|
||||
SquashMergeCommittedTitle: "Squash merge and commit",
|
||||
StagingTitle: "Main panel (staging)",
|
||||
MergingTitle: "Main panel (merging)",
|
||||
NormalTitle: "Main panel (normal)",
|
||||
LogTitle: "Log",
|
||||
LogXOfYTitle: "Log (%d of %d)",
|
||||
CommitSummary: "Commit summary",
|
||||
CredentialsUsername: "Username",
|
||||
CredentialsPassword: "Password",
|
||||
@@ -1136,7 +1148,6 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
ToggleTreeViewTooltip: "Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.\n\nThe default can be changed in the config file with the key 'gui.showFileTree'.",
|
||||
OpenDiffTool: "Open external diff tool (git difftool)",
|
||||
OpenMergeTool: "Open external merge tool",
|
||||
OpenMergeToolTooltip: "Run `git mergetool`.",
|
||||
Refresh: "Refresh",
|
||||
RefreshTooltip: "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`.",
|
||||
Push: "Push",
|
||||
@@ -1223,7 +1234,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
NewBranchFromStashTooltip: "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.",
|
||||
MoveCommitsToNewBranch: "Move commits to new branch",
|
||||
MoveCommitsToNewBranchTooltip: "Create a new branch and move the unpushed commits of the current branch to it. Useful if you meant to start new work and forgot to create a new branch first.\n\nNote that this disregards the selection, the new branch is always created either from the main branch or stacked on top of the current branch (you get to choose which).",
|
||||
MoveCommitsToNewBranchFromMainPrompt: "This will take all unpushed commits and move them to a new branch (off of {{.baseBranchName}}). It will then hard-reset the current branch its the upstream branch. Do you want to continue?",
|
||||
MoveCommitsToNewBranchFromMainPrompt: "This will take all unpushed commits and move them to a new branch (off of {{.baseBranchName}}). It will then hard-reset the current branch to its upstream branch. Do you want to continue?",
|
||||
MoveCommitsToNewBranchMenuPrompt: "This will take all unpushed commits and move them to a new branch. This new branch can either be created from the main branch ({{.baseBranchName}}) or stacked on top of the current branch. Which of these would you like to do?",
|
||||
MoveCommitsToNewBranchFromBaseItem: "New branch from base branch (%s)",
|
||||
MoveCommitsToNewBranchStackedItem: "New branch stacked on current branch (%s)",
|
||||
@@ -1342,8 +1353,16 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
RefreshFiles: `Refresh files`,
|
||||
FocusMainView: "Focus main view",
|
||||
Merge: `Merge`,
|
||||
RegularMerge: "Regular merge",
|
||||
MergeBranchTooltip: "View options for merging the selected item into the current branch (regular merge, squash merge)",
|
||||
RegularMergeFastForward: "Regular merge (fast-forward)",
|
||||
RegularMergeFastForwardTooltip: "Fast-forward '{{.checkedOutBranch}}' to '{{.selectedBranch}}' without creating a merge commit.",
|
||||
CannotFastForwardMerge: "Cannot fast-forward '{{.checkedOutBranch}}' to '{{.selectedBranch}}'",
|
||||
RegularMergeNonFastForward: "Regular merge (with merge commit)",
|
||||
RegularMergeNonFastForwardTooltip: "Merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}', creating a merge commit.",
|
||||
SquashMergeUncommitted: "Squash merge and leave uncommitted",
|
||||
SquashMergeUncommittedTooltip: "Squash merge '{{.selectedBranch}}' into the working tree.",
|
||||
SquashMergeCommitted: "Squash merge and commit",
|
||||
SquashMergeCommittedTooltip: "Squash merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}' as a single commit.",
|
||||
ConfirmQuit: `Are you sure you want to quit?`,
|
||||
SwitchRepo: `Switch to a recent repo`,
|
||||
AllBranchesLogGraph: `Show/cycle all branch logs`,
|
||||
@@ -1428,9 +1447,6 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
InteractiveRebaseTooltip: "Begin an interactive rebase with a break at the start, so you can update the TODO commits before continuing.",
|
||||
RebaseOntoBaseBranchTooltip: "Rebase the checked out branch onto its base branch (i.e. the closest main branch).",
|
||||
MustSelectTodoCommits: "When rebasing, this action only works on a selection of TODO commits.",
|
||||
SquashMergeUncommitted: "Squash merge '{{.selectedBranch}}' into the working tree.",
|
||||
SquashMergeCommitted: "Squash merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}' as a single commit.",
|
||||
RegularMergeTooltip: "Merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'.",
|
||||
FwdNoUpstream: "Cannot fast-forward a branch with no upstream",
|
||||
FwdNoLocalUpstream: "Cannot fast-forward a branch whose remote is not registered locally",
|
||||
FwdCommitsToPush: "Cannot fast-forward a branch with commits to push",
|
||||
@@ -1671,6 +1687,9 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
ViewResetToUpstreamOptions: "View upstream reset options",
|
||||
NextScreenMode: "Next screen mode (normal/half/fullscreen)",
|
||||
PrevScreenMode: "Prev screen mode",
|
||||
CyclePagers: "Cycle pagers",
|
||||
CyclePagersTooltip: "Choose the next pager in the list of configured pagers",
|
||||
CyclePagersDisabledReason: "No other pagers configured",
|
||||
StartSearch: "Search the current view by text",
|
||||
StartFilter: "Filter the current view by text",
|
||||
KeybindingsLegend: "Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b",
|
||||
@@ -1920,7 +1939,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
RemoveWorktree: "Remove worktree",
|
||||
RemoveWorktreeTitle: "Remove worktree",
|
||||
RemoveWorktreePrompt: "Are you sure you want to remove worktree '{{.worktreeName}}'?",
|
||||
ForceRemoveWorktreePrompt: "'{{.worktreeName}}' contains modified or untracked files (to be honest, it could contain both). Are you sure you want to remove it?",
|
||||
ForceRemoveWorktreePrompt: "'{{.worktreeName}}' contains modified or untracked files, or submodules (or all of these). Are you sure you want to remove it?",
|
||||
RemovingWorktree: "Deleting worktree",
|
||||
DetachWorktree: "Detach worktree",
|
||||
DetachingWorktree: "Detaching worktree",
|
||||
@@ -1970,6 +1989,13 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
CustomCommands: "Custom commands",
|
||||
NoApplicableCommandsInThisContext: "(No applicable commands in this context)",
|
||||
SelectCommitsOfCurrentBranch: "Select commits of current branch",
|
||||
ViewMergeConflictOptions: "View merge conflict options",
|
||||
ViewMergeConflictOptionsTooltip: "View options for resolving merge conflicts.",
|
||||
NoFilesWithMergeConflicts: "There are no files with merge conflicts.",
|
||||
MergeConflictOptionsTitle: "Resolve merge conflicts",
|
||||
UseCurrentChanges: "Use current changes",
|
||||
UseIncomingChanges: "Use incoming changes",
|
||||
UseBothChanges: "Use both",
|
||||
|
||||
Actions: Actions{
|
||||
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
|
||||
|
||||
@@ -11,11 +11,6 @@
|
||||
"StagedChanges": "ステージされた変更",
|
||||
"StagingTitle": "メインパネル(ステージング)",
|
||||
"MergingTitle": "メインパネル(マージ中)",
|
||||
"SquashMergeUncommittedTitle": "スカッシュマージして未コミットのままにする",
|
||||
"SquashMergeCommittedTitle": "スカッシュマージしてコミットする",
|
||||
"SquashMergeUncommitted": "'{{.selectedBranch}}'をワーキングツリーにスカッシュマージします。",
|
||||
"SquashMergeCommitted": "'{{.selectedBranch}}'を'{{.checkedOutBranch}}'に単一のコミットとしてスカッシュマージします。",
|
||||
"RegularMergeTooltip": "'{{.selectedBranch}}'を'{{.checkedOutBranch}}'にマージします。",
|
||||
"NormalTitle": "メインパネル(通常)",
|
||||
"LogTitle": "ログ",
|
||||
"CommitSummary": "コミット概要",
|
||||
@@ -50,7 +45,6 @@
|
||||
"ToggleTreeView": "ファイルツリービューを切り替え",
|
||||
"OpenDiffTool": "外部差分ツールを開く(git difftool)",
|
||||
"OpenMergeTool": "外部マージツールを開く",
|
||||
"OpenMergeToolTooltip": "`git mergetool`を実行します。",
|
||||
"Refresh": "更新",
|
||||
"RefreshTooltip": "Gitの状態を更新します(`git status`、`git branch`などをバックグラウンドで実行してパネルの内容を更新します)。これは`git fetch`を実行しません。",
|
||||
"Push": "プッシュ",
|
||||
@@ -247,7 +241,6 @@
|
||||
"RefreshFiles": "ファイルを更新",
|
||||
"FocusMainView": "メインビューにフォーカス",
|
||||
"Merge": "マージ",
|
||||
"RegularMerge": "通常のマージ",
|
||||
"MergeBranchTooltip": "選択した項目を現在のブランチにマージするためのオプションを表示します(通常のマージ、スカッシュマージ)",
|
||||
"ConfirmQuit": "本当に終了してよろしいですか?",
|
||||
"SwitchRepo": "最近のリポジトリをチェックアウト",
|
||||
@@ -781,7 +774,6 @@
|
||||
"WorktreesTitle": "ワークツリー",
|
||||
"WorktreeTitle": "ワークツリー",
|
||||
"RemoveWorktreePrompt": "ワークツリー '{{.worktreeName}}' を削除してよろしいですか?",
|
||||
"ForceRemoveWorktreePrompt": "'{{.worktreeName}}' には変更されたファイルまたは追跡されていないファイルが含まれています(正直なところ、両方含まれている可能性があります)。削除してよろしいですか?",
|
||||
"RemovingWorktree": "ワークツリーを削除中",
|
||||
"AddingWorktree": "ワークツリーを追加中",
|
||||
"CantDeleteCurrentWorktree": "現在のワークツリーは削除できません!",
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"StagedChanges": "Staged된 변경 내용",
|
||||
"StagingTitle": "메인 패널 (Staging)",
|
||||
"MergingTitle": "메인 패널 (Merging)",
|
||||
"SquashMergeCommittedTitle": "스쿼시 병합 및 커밋",
|
||||
"NormalTitle": "메인 패널 (Normal)",
|
||||
"LogTitle": "로그",
|
||||
"CommitSummary": "커밋 메시지",
|
||||
@@ -17,13 +16,19 @@
|
||||
"CredentialsPassword": "패스워드",
|
||||
"CredentialsPassphrase": "SSH키의 passphrase 입력",
|
||||
"CredentialsPIN": "SSH키\u001d의 PIN\u001d을 입력",
|
||||
"CredentialsToken": "SSH 키를 위한 토큰 입력",
|
||||
"PassUnameWrong": "패스워드, passphrase 또는 사용자 이름이 잘못되었습니다.",
|
||||
"Commit": "커밋 변경내용",
|
||||
"CommitTooltip": "스테이징된 변경 사항 커밋.",
|
||||
"AmendLastCommit": "마지맛 커밋 수정",
|
||||
"AmendLastCommitTitle": "마지막 커밋 수정",
|
||||
"SureToAmend": "마지막 커밋을 수정하시겠습니까? 그런 다음 커밋 패널에서 커밋 메시지를 변경할 수 있습니다.",
|
||||
"NoCommitToAmend": "Amend 가능한 커밋이 없습니다.",
|
||||
"CommitChangesWithEditor": "Git 편집기를 사용하여 변경 내용을 커밋합니다.",
|
||||
"NoBaseCommitsFound": "기본 커밋을 찾을 수 없습니다",
|
||||
"MultipleBaseCommitsFoundStaged": "여러 개의 기본 커밋을 찾았습니다. (한 번에 더 적은 변경 사항을 스테이징해보세요)",
|
||||
"MultipleBaseCommitsFoundUnstaged": "여러 개의 기본 커밋을 찾았습니다. (변경 사항 중 일부를 스테이징해보세요)",
|
||||
"BaseCommitIsAlreadyOnMainBranch": "이 변경 사항의 기본 커밋은 이미 메인 브랜치에 있습니다",
|
||||
"StatusTitle": "상태",
|
||||
"GlobalTitle": "글로벌 키 바인딩",
|
||||
"Execute": "실행",
|
||||
@@ -46,7 +51,7 @@
|
||||
"AllFilesDiffCopiedToast": "모든 파일의 변경점을 클립보드에 복사했습니다.",
|
||||
"FilterStagedFiles": "Staged된 파일만 표시",
|
||||
"FilterUnstagedFiles": "Stage되지 않은 파일만 표시",
|
||||
"MergeConflictsTitle": "병합 충돌 내용",
|
||||
"MergeConflictsTitle": "병합 충돌",
|
||||
"Checkout": "체크아웃",
|
||||
"NoChangedFiles": "변경된 파일이 없습니다.",
|
||||
"SoftReset": "소프트 리셋",
|
||||
@@ -348,6 +353,9 @@
|
||||
"Push": "푸시",
|
||||
"Pull": "업데이트(Pull)",
|
||||
"OpenFile": "파일 열기",
|
||||
"CopyToClipboard": "클립보드에 복사",
|
||||
"CopySelectedTextToClipboard": "선택한 텍스트를 클립보드에 복사",
|
||||
"DeleteRemoteBranch": "원격 브랜치 삭제",
|
||||
"RemoveSubmodule": "서브모듈 삭제",
|
||||
"ResetSubmodule": "서브모듈 Reset",
|
||||
"AddSubmodule": "서브모듈 추가",
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
"ToggleTreeView": "Przełącz widok drzewa plików",
|
||||
"OpenDiffTool": "Otwórz zewnętrzne narzędzie różnic (git difftool)",
|
||||
"OpenMergeTool": "Otwórz zewnętrzne narzędzie scalania",
|
||||
"OpenMergeToolTooltip": "Uruchom `git mergetool`.",
|
||||
"Refresh": "Odśwież",
|
||||
"RefreshTooltip": "Odśwież stan git (tj. uruchom `git status`, `git branch`, itp. w tle, aby zaktualizować zawartość paneli). To nie uruchamia `git fetch`.",
|
||||
"Push": "Wypchnij",
|
||||
@@ -634,7 +633,6 @@
|
||||
"WorktreesTitle": "Drzewa pracy",
|
||||
"WorktreeTitle": "Drzewo pracy",
|
||||
"RemoveWorktreePrompt": "Czy na pewno chcesz usunąć drzewo pracy '{{.worktreeName}}'?",
|
||||
"ForceRemoveWorktreePrompt": "'{{.worktreeName}}' zawiera zmodyfikowane lub nieśledzone pliki (szczerze mówiąc, może zawierać oba). Czy na pewno chcesz to usunąć?",
|
||||
"RemovingWorktree": "Usuwanie drzewa pracy",
|
||||
"AddingWorktree": "Dodawanie drzewa pracy",
|
||||
"CantDeleteCurrentWorktree": "Nie możesz usunąć bieżącego drzewa pracy!",
|
||||
|
||||
@@ -11,11 +11,6 @@
|
||||
"StagedChanges": "Alterações preparadas",
|
||||
"StagingTitle": "Painel Principal (preparação)",
|
||||
"MergingTitle": "Painel principal (mesclagem)",
|
||||
"SquashMergeUncommittedTitle": "Mesclar Squash e sair sem commit",
|
||||
"SquashMergeCommittedTitle": "Mesclar Squash e commit",
|
||||
"SquashMergeUncommitted": "Mesclar Squash '{{.selectedBranch}}' na árvore de trabalho",
|
||||
"SquashMergeCommitted": "Mesclar Squash '{{.selectedBranch}}' em '{{.checkedOutBranch}}' como um único commit.",
|
||||
"RegularMergeTooltip": "Mesclar '{{.selectedBranch}}' em '{{.checkedOutBranch}}'.",
|
||||
"NormalTitle": "Painel Principal (Normal)",
|
||||
"LogTitle": "Log",
|
||||
"CommitSummary": "Sumário do commit",
|
||||
@@ -50,7 +45,6 @@
|
||||
"ToggleTreeView": "Alternar exibição de árvore de arquivo",
|
||||
"OpenDiffTool": "Abrir ferramenta de diff externa (git difftool)",
|
||||
"OpenMergeTool": "Abrir ferramenta de merge externa",
|
||||
"OpenMergeToolTooltip": "Execute `git mergetool`.",
|
||||
"Refresh": "Atualizar",
|
||||
"RefreshTooltip": "Atualize o estado do git (ou seja, execute `git status`, `git branch`, etc em segundo plano para atualizar o conteúdo de painéis). Isso não executa `git fetch`.",
|
||||
"Push": "Empurre (Push)",
|
||||
@@ -239,7 +233,6 @@
|
||||
"ExcludeFile": "Adicionar ao .git/info/exclui",
|
||||
"RefreshFiles": "Atualizar arquivos",
|
||||
"Merge": "Mesclar",
|
||||
"RegularMerge": "Mesclagem regular",
|
||||
"MergeBranchTooltip": "Ver opções para mesclar o item selecionado no branch atual (mesclar regularmente, mesclar squash)",
|
||||
"ConfirmQuit": "Tem a certeza de que pretende sair?",
|
||||
"SwitchRepo": "Mudar para um repositório recente",
|
||||
@@ -296,6 +289,7 @@
|
||||
"InformationTitle": "Informações",
|
||||
"SecondaryTitle": "Secundário",
|
||||
"ReflogCommitsTitle": "Reflog",
|
||||
"ConflictsResolved": "Todos os conflitos de merge resolvidos. Continuar?",
|
||||
"Continue": "Continuar",
|
||||
"UnstagedFilesAfterConflictsResolved": "Os arquivos foram modificados desde que os conflitos foram resolvidos. Auto-encapsulá-los e continuar?",
|
||||
"RebasingTitle": "Rebase '{{.checkedOutBranch}}'",
|
||||
|
||||
@@ -11,11 +11,6 @@
|
||||
"StagedChanges": "已暂存变更",
|
||||
"StagingTitle": "正在暂存",
|
||||
"MergingTitle": "正在合并",
|
||||
"SquashMergeUncommittedTitle": "压缩合并并保持未提交状态",
|
||||
"SquashMergeCommittedTitle": "压缩合并,然后提交",
|
||||
"SquashMergeUncommitted": "将分支 ‘{{.selectedBranch}}’ 压缩合并到工作树中",
|
||||
"SquashMergeCommitted": "将分支 '{{.selectedBranch}}' 压缩合并为单个提交,到 '{{.checkedOutBranch}}' 分支中。",
|
||||
"RegularMergeTooltip": "将分支 '{{.selectedBranch}}' 合并到 '{{.checkedOutBranch}}'",
|
||||
"NormalTitle": "正常",
|
||||
"LogTitle": "日志",
|
||||
"CommitSummary": "提交信息",
|
||||
@@ -50,7 +45,6 @@
|
||||
"ToggleTreeView": "切换文件树视图",
|
||||
"OpenDiffTool": "使用外部差异比较工具(git difftool)",
|
||||
"OpenMergeTool": "打开外部合并工具(git mergetool)",
|
||||
"OpenMergeToolTooltip": "执行 `git mergetool`.",
|
||||
"Refresh": "刷新",
|
||||
"RefreshTooltip": "刷新git状态(即在后台上运行`git status`,`git branch`等命令以更新面板内容) 不会运行`git fetch`",
|
||||
"Push": "推送",
|
||||
@@ -242,7 +236,6 @@
|
||||
"ExcludeFile": "添加到 .git/info/exclude",
|
||||
"RefreshFiles": "刷新文件",
|
||||
"Merge": "合并到当前检出的分支",
|
||||
"RegularMerge": "常规合并",
|
||||
"MergeBranchTooltip": "Merge selected branch into currently checked out branch.",
|
||||
"ConfirmQuit": "您确定要退出吗?",
|
||||
"SwitchRepo": "切换到最近的仓库",
|
||||
@@ -757,7 +750,6 @@
|
||||
"WorktreesTitle": "工作区",
|
||||
"WorktreeTitle": "工作区",
|
||||
"RemoveWorktreePrompt": "您确定要删除工作树 {{.worktreeName}}' ?",
|
||||
"ForceRemoveWorktreePrompt": "'{{.worktreeName}}' 包含已修改或未跟踪的文件(说实话,它可能包含两者)。您确定要删除它吗?",
|
||||
"RemovingWorktree": "正在删除工作树",
|
||||
"AddingWorktree": "添加工作区",
|
||||
"CantDeleteCurrentWorktree": "您不能删除当前的工作树!",
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
"StagedChanges": "已預存變更",
|
||||
"StagingTitle": "主面板(預存)",
|
||||
"MergingTitle": "主面板(合併)",
|
||||
"SquashMergeUncommittedTitle": "壓縮合併但不提交",
|
||||
"SquashMergeCommittedTitle": "壓縮合併並提交",
|
||||
"NormalTitle": "主面板(一般)",
|
||||
"LogTitle": "版本記錄",
|
||||
"CommitSummary": "提交摘要",
|
||||
@@ -36,7 +34,6 @@
|
||||
"ToggleTreeView": "顯示檔案樹狀視圖",
|
||||
"OpenDiffTool": "開啟外部差異工具 (git difftool)",
|
||||
"OpenMergeTool": "開啟外部合併工具",
|
||||
"OpenMergeToolTooltip": "執行 `git mergetool`。",
|
||||
"Refresh": "重新整理",
|
||||
"Push": "推送",
|
||||
"Pull": "拉取",
|
||||
@@ -164,7 +161,6 @@
|
||||
"ExcludeFile": "添加到 .git/info/exclude",
|
||||
"RefreshFiles": "重新整理檔案",
|
||||
"Merge": "合併到當前檢出的分支",
|
||||
"RegularMerge": "一般合併",
|
||||
"ConfirmQuit": "是否結束?",
|
||||
"SwitchRepo": "切換到最近使用的版本庫",
|
||||
"UnsupportedGitService": "不支援的 git 服務",
|
||||
@@ -539,7 +535,6 @@
|
||||
"WorktreesTitle": "工作目錄",
|
||||
"WorktreeTitle": "工作目錄",
|
||||
"RemoveWorktreePrompt": "是否刪除 {{.worktreeName}} 工作目錄?",
|
||||
"ForceRemoveWorktreePrompt": "'{{.worktreeName}}' 包括已更動或未追蹤的檔案。是否繼續刪除工作目錄?",
|
||||
"RemovingWorktree": "正在刪除工作目錄",
|
||||
"AddingWorktree": "正在建立工作目錄",
|
||||
"CantDeleteCurrentWorktree": "無法刪除當前工作目錄!",
|
||||
|
||||
@@ -170,6 +170,10 @@ func (self *Shell) Commit(message string) *Shell {
|
||||
return self.RunCommand([]string{"git", "commit", "-m", message})
|
||||
}
|
||||
|
||||
func (self *Shell) CommitInWorktreeOrSubmodule(worktreePath string, message string) *Shell {
|
||||
return self.RunCommand([]string{"git", "-C", worktreePath, "commit", "-m", message})
|
||||
}
|
||||
|
||||
func (self *Shell) EmptyCommit(message string) *Shell {
|
||||
return self.RunCommand([]string{"git", "commit", "--allow-empty", "-m", message})
|
||||
}
|
||||
@@ -428,11 +432,21 @@ func (self *Shell) AddWorktreeCheckout(base string, path string) *Shell {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *Shell) AddFileInWorktree(worktreePath string) *Shell {
|
||||
self.CreateFile(filepath.Join(worktreePath, "content"), "content")
|
||||
func (self *Shell) AddFileInWorktreeOrSubmodule(worktreePath string, filePath string, content string) *Shell {
|
||||
self.CreateFile(filepath.Join(worktreePath, filePath), content)
|
||||
|
||||
self.RunCommand([]string{
|
||||
"git", "-C", worktreePath, "add", "content",
|
||||
"git", "-C", worktreePath, "add", filePath,
|
||||
})
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Shell) UpdateFileInWorktreeOrSubmodule(worktreePath string, filePath string, content string) *Shell {
|
||||
self.UpdateFile(filepath.Join(worktreePath, filePath), content)
|
||||
|
||||
self.RunCommand([]string{
|
||||
"git", "-C", worktreePath, "add", filePath,
|
||||
})
|
||||
|
||||
return self
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package branch
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var MergeFastForward = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Merge a branch into another using fast-forward merge",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.LocalBranchSortOrder = "alphabetical"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.NewBranch("original-branch").
|
||||
EmptyCommit("one").
|
||||
NewBranch("branch1").
|
||||
EmptyCommit("branch1").
|
||||
Checkout("original-branch").
|
||||
NewBranchFrom("branch2", "original-branch").
|
||||
EmptyCommit("branch2").
|
||||
Checkout("original-branch")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("original-branch").IsSelected(),
|
||||
Contains("branch1"),
|
||||
Contains("branch2"),
|
||||
).
|
||||
SelectNextItem().
|
||||
Press(keys.Branches.MergeIntoCurrentBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Merge")).
|
||||
TopLines(
|
||||
Contains("Regular merge (fast-forward)"),
|
||||
Contains("Regular merge (with merge commit)"),
|
||||
).
|
||||
Select(Contains("Regular merge (fast-forward)")).
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
Contains("branch1").IsSelected(),
|
||||
Contains("one"),
|
||||
)
|
||||
|
||||
// Check that branch2 can't be merged using fast-forward
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
NavigateToLine(Contains("branch2")).
|
||||
Press(keys.Branches.MergeIntoCurrentBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Merge")).
|
||||
TopLines(
|
||||
Contains("Regular merge (with merge commit)"),
|
||||
Contains("Regular merge (fast-forward)"),
|
||||
).
|
||||
Select(Contains("Regular merge (fast-forward)")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectToast(Contains("Cannot fast-forward 'original-branch' to 'branch2'"))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
package branch
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var MergeNonFastForward = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Merge a branch into another using non-fast-forward merge",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.LocalBranchSortOrder = "alphabetical"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.NewBranch("original-branch").
|
||||
EmptyCommit("one").
|
||||
NewBranch("branch1").
|
||||
EmptyCommit("branch1").
|
||||
Checkout("original-branch").
|
||||
NewBranchFrom("branch2", "original-branch").
|
||||
EmptyCommit("branch2").
|
||||
Checkout("original-branch")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("original-branch").IsSelected(),
|
||||
Contains("branch1"),
|
||||
Contains("branch2"),
|
||||
).
|
||||
SelectNextItem().
|
||||
Press(keys.Branches.MergeIntoCurrentBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Merge")).
|
||||
TopLines(
|
||||
Contains("Regular merge (fast-forward)"),
|
||||
Contains("Regular merge (with merge commit)"),
|
||||
).
|
||||
Select(Contains("Regular merge (with merge commit)")).
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
Contains("⏣─╮ Merge branch 'branch1' into original-branch").IsSelected(),
|
||||
Contains("│ ◯ * branch1"),
|
||||
Contains("◯─╯ one"),
|
||||
)
|
||||
|
||||
// Check that branch2 shows the non-fast-forward option first
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
NavigateToLine(Contains("branch2")).
|
||||
Press(keys.Branches.MergeIntoCurrentBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Merge")).
|
||||
TopLines(
|
||||
Contains("Regular merge (with merge commit)"),
|
||||
Contains("Regular merge (fast-forward)"),
|
||||
).
|
||||
Select(Contains("Regular merge (with merge commit)")).
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
Contains("⏣─╮ Merge branch 'branch2' into original-branch").IsSelected(),
|
||||
Contains("│ ◯ * branch2"),
|
||||
Contains("⏣─│─╮ Merge branch 'branch1' into original-branch"),
|
||||
Contains("│ │ ◯ * branch1"),
|
||||
Contains("◯─┴─╯ one"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,99 @@
|
||||
package cherry_pick
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var CherryPickCommitThatBecomesEmpty = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Cherry-pick a commit that becomes empty at the destination",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
EmptyCommit("base").
|
||||
CreateFileAndAdd("file1", "change 1\n").
|
||||
CreateFileAndAdd("file2", "change 2\n").
|
||||
Commit("two changes in one commit").
|
||||
NewBranchFrom("branch", "HEAD^").
|
||||
CreateFileAndAdd("file1", "change 1\n").
|
||||
Commit("single change").
|
||||
CreateFileAndAdd("file3", "change 3\n").
|
||||
Commit("unrelated change").
|
||||
Checkout("master")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("master").IsSelected(),
|
||||
Contains("branch"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressEnter()
|
||||
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("unrelated change").IsSelected(),
|
||||
Contains("single change"),
|
||||
Contains("base"),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Commits.CherryPickCopy).
|
||||
Tap(func() {
|
||||
t.Views().Information().Content(Contains("2 commits copied"))
|
||||
})
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("two changes in one commit").IsSelected(),
|
||||
Contains("base"),
|
||||
).
|
||||
Press(keys.Commits.PasteCommits).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Cherry-pick")).
|
||||
Content(Contains("Are you sure you want to cherry-pick the 2 copied commit(s) onto this branch?")).
|
||||
Confirm()
|
||||
})
|
||||
|
||||
if t.Git().Version().IsAtLeast(2, 45, 0) {
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
Contains("unrelated change"),
|
||||
Contains("single change"),
|
||||
Contains("two changes in one commit").IsSelected(),
|
||||
Contains("base"),
|
||||
).
|
||||
SelectPreviousItem()
|
||||
|
||||
// Cherry-picked commit is empty
|
||||
t.Views().Main().Content(DoesNotContain("diff --git"))
|
||||
} else {
|
||||
t.Views().Commits().
|
||||
// We have a bug with how the selection is updated in this case; normally you would
|
||||
// expect the "two changes in one commit" commit to be selected because it was
|
||||
// selected before pasting, and we try to maintain that selection. This is broken
|
||||
// for two reasons:
|
||||
// 1. We increment the selected line index after pasting by the number of pasted
|
||||
// commits; this is wrong because we skipped the commit that became empty. So
|
||||
// according to this bug, the "base" commit should be selected.
|
||||
// 2. We only update the selected line index after pasting if the currently selected
|
||||
// commit is not a rebase TODO commit, on the assumption that if it is, we are in a
|
||||
// rebase and the cherry-picked commits end up below the selection. In this case,
|
||||
// however, we still think we are cherry-picking because the final refresh after the
|
||||
// CheckMergeOrRebase in CherryPickHelper.Paste is async and hasn't completed yet;
|
||||
// so the "unrelated change" still has a "pick" action.
|
||||
//
|
||||
// Since this only happens for older git versions, we don't bother fixing it.
|
||||
Lines(
|
||||
Contains("unrelated change").IsSelected(),
|
||||
Contains("two changes in one commit"),
|
||||
Contains("base"),
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package cherry_pick
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
var CherryPickConflictsEmptyCommitAfterResolving = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Cherry pick commits with conflicts, resolve them so that the commit becomes empty",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.LocalBranchSortOrder = "recency"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shared.MergeConflictsSetup(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("first-change-branch"),
|
||||
Contains("second-change-branch"),
|
||||
Contains("original-branch"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressEnter()
|
||||
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
TopLines(
|
||||
Contains("second-change-branch unrelated change"),
|
||||
Contains("second change"),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Commits.CherryPickCopy)
|
||||
|
||||
t.Views().Information().Content(Contains("2 commits copied"))
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
TopLines(
|
||||
Contains("first change").IsSelected(),
|
||||
).
|
||||
Press(keys.Commits.PasteCommits)
|
||||
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Cherry-pick")).
|
||||
Content(Contains("Are you sure you want to cherry-pick the 2 copied commit(s) onto this branch?")).
|
||||
Confirm()
|
||||
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
SelectedLine(Contains("file")).
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Discard changes")).
|
||||
Select(Contains("Discard all changes")).
|
||||
Confirm()
|
||||
|
||||
t.Common().ContinueOnConflictsResolved("cherry-pick")
|
||||
|
||||
t.Views().Files().IsEmpty()
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
TopLines(
|
||||
// We have a bug with how the selection is updated in this case; normally you would
|
||||
// expect the "first change" commit to be selected because it was selected before
|
||||
// pasting, and we try to maintain that selection. This is broken for two reasons:
|
||||
// 1. We increment the selected line index after pasting by the number of pasted
|
||||
// commits; this is wrong because we skipped the commit that became empty. So
|
||||
// according to this bug, the "original" commit should be selected.
|
||||
// 2. We only update the selected line index after pasting if the currently selected
|
||||
// commit is not a rebase TODO commit, on the assumption that if it is, we are in a
|
||||
// rebase and the cherry-picked commits end up below the selection. In this case,
|
||||
// however, we still think we are cherry-picking because the final refresh after the
|
||||
// CheckMergeOrRebase in CherryPickHelper.Paste is async and hasn't completed yet;
|
||||
// so the "second-change-branch unrelated change" still has a "pick" action.
|
||||
//
|
||||
// We don't bother fixing it for now because it's a pretty niche case, and the
|
||||
// nature of the problem is only cosmetic.
|
||||
Contains("second-change-branch unrelated change").IsSelected(),
|
||||
Contains("first change"),
|
||||
Contains("original"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DiscardSubmoduleChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Discarding changes to a submodule from an old commit.",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("Initial commit")
|
||||
shell.CloneIntoSubmodule("submodule", "submodule")
|
||||
shell.Commit("Add submodule")
|
||||
|
||||
shell.AddFileInWorktreeOrSubmodule("submodule", "file", "content")
|
||||
shell.CommitInWorktreeOrSubmodule("submodule", "add file in submodule")
|
||||
shell.GitAdd("submodule")
|
||||
shell.Commit("Update submodule")
|
||||
|
||||
shell.UpdateFileInWorktreeOrSubmodule("submodule", "file", "changed content")
|
||||
shell.CommitInWorktreeOrSubmodule("submodule", "change file in submodule")
|
||||
shell.GitAdd("submodule")
|
||||
shell.Commit("Update submodule again")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("Update submodule again").IsSelected(),
|
||||
Contains("Update submodule"),
|
||||
Contains("Add submodule"),
|
||||
Contains("Initial commit"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("M submodule").IsSelected(),
|
||||
).
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Contains("Are you sure you want to remove changes to the selected file(s) from this commit?")).
|
||||
Confirm()
|
||||
|
||||
t.Shell().RunCommand([]string{"git", "submodule", "update"})
|
||||
t.FileSystem().FileContent("submodule/file", Equals("content"))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,77 @@
|
||||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
func testDataBoth() (original, current, incoming, final string) {
|
||||
original = `
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
`
|
||||
current = `
|
||||
1a
|
||||
2
|
||||
3
|
||||
4
|
||||
5a
|
||||
6
|
||||
`
|
||||
incoming = `
|
||||
1
|
||||
2
|
||||
3b
|
||||
4
|
||||
5b
|
||||
6
|
||||
`
|
||||
final = `
|
||||
1a
|
||||
2
|
||||
3b
|
||||
4
|
||||
5a
|
||||
5b
|
||||
6
|
||||
`
|
||||
return original, current, incoming, final
|
||||
}
|
||||
|
||||
var MergeFileBoth = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Conflicting file can be resolved to 'union' (both changes) version via merge-file",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
original, current, incoming, _ := testDataBoth()
|
||||
shared.CreateMergeConflictFileForMergeFileTests(shell, original, current, incoming)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
_, _, _, expected := testDataBoth()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("file").IsSelected(),
|
||||
)
|
||||
|
||||
t.GlobalPress(keys.Files.OpenMergeOptions)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Resolve merge conflicts")).
|
||||
Select(Contains("Use both")). // merge-file --union
|
||||
Confirm()
|
||||
|
||||
t.Common().ContinueOnConflictsResolved("merge")
|
||||
|
||||
t.Views().Files().IsEmpty()
|
||||
|
||||
t.FileSystem().FileContent("file", Equals(expected))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
func testDataCurrent() (original, current, incoming, final string) {
|
||||
original = `
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
`
|
||||
current = `
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5a
|
||||
6
|
||||
`
|
||||
incoming = `
|
||||
1b
|
||||
2
|
||||
3
|
||||
4
|
||||
5b
|
||||
6
|
||||
`
|
||||
final = `
|
||||
1b
|
||||
2
|
||||
3
|
||||
4
|
||||
5a
|
||||
6
|
||||
`
|
||||
return original, current, incoming, final
|
||||
}
|
||||
|
||||
var MergeFileCurrent = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Conflicting file can be resolved to 'ours' (current changes) version via merge-file",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
original, current, incoming, _ := testDataCurrent()
|
||||
shared.CreateMergeConflictFileForMergeFileTests(shell, original, current, incoming)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
_, _, _, expected := testDataCurrent()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("file").IsSelected(),
|
||||
)
|
||||
|
||||
t.GlobalPress(keys.Files.OpenMergeOptions)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Resolve merge conflicts")).
|
||||
Select(Contains("Use current changes")). // merge-file --ours
|
||||
Confirm()
|
||||
|
||||
t.Common().ContinueOnConflictsResolved("merge")
|
||||
|
||||
t.Views().Files().IsEmpty()
|
||||
|
||||
t.FileSystem().FileContent("file", Equals(expected))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
func testDataIncoming() (original, current, incoming, final string) {
|
||||
original = `
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
`
|
||||
current = `
|
||||
1a
|
||||
2
|
||||
3
|
||||
4
|
||||
5a
|
||||
6
|
||||
`
|
||||
incoming = `
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5b
|
||||
6
|
||||
`
|
||||
final = `
|
||||
1a
|
||||
2
|
||||
3
|
||||
4
|
||||
5b
|
||||
6
|
||||
`
|
||||
return original, current, incoming, final
|
||||
}
|
||||
|
||||
var MergeFileIncoming = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Conflicting file can be resolved to 'theirs' (incoming changes) version via merge-file",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
original, current, incoming, _ := testDataIncoming()
|
||||
shared.CreateMergeConflictFileForMergeFileTests(shell, original, current, incoming)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
_, _, _, expected := testDataIncoming()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("file").IsSelected(),
|
||||
)
|
||||
|
||||
t.GlobalPress(keys.Files.OpenMergeOptions)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Resolve merge conflicts")).
|
||||
Select(Contains("Use incoming changes")). // merge-file --theirs
|
||||
Confirm()
|
||||
|
||||
t.Common().ContinueOnConflictsResolved("merge")
|
||||
|
||||
t.Views().Files().IsEmpty()
|
||||
|
||||
t.FileSystem().FileContent("file", Equals(expected))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
package filter_by_path
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var SelectFilteredFileWhenEnteringCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Filter commits by file path, then enter a commit and ensure the file is selected",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file1", "")
|
||||
shell.CreateFileAndAdd("dir/file2", "")
|
||||
shell.Commit("add files")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.GlobalPress(keys.Universal.FilteringMenu)
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Enter path to filter by")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Enter path:")).
|
||||
Type("dir/file2").
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("add files").IsSelected(),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ /"),
|
||||
Equals(" ▼ dir"),
|
||||
Equals(" A file2").IsSelected(),
|
||||
Equals(" A file1"),
|
||||
)
|
||||
},
|
||||
})
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package filter_by_path
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var SelectFilteredFileWhenEnteringCommitNoRootItem = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Filter commits by file path, then enter a commit and ensure the file is selected (with the show root item config off)",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Gui.ShowRootItemInFileTree = false
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file1", "")
|
||||
shell.CreateFileAndAdd("dir/file2", "")
|
||||
shell.Commit("add files")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.GlobalPress(keys.Universal.FilteringMenu)
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Enter path to filter by")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Enter path:")).
|
||||
Type("dir/file2").
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("add files").IsSelected(),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ dir"),
|
||||
Equals(" A file2").IsSelected(),
|
||||
Equals("A file1"),
|
||||
)
|
||||
},
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user