Compare commits
73 Commits
keybinding
...
worktree-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3557bc44ad | ||
|
|
201ec5307f | ||
|
|
ab4f310ce9 | ||
|
|
40753e916d | ||
|
|
72ec7922ac | ||
|
|
c9a19f0fbc | ||
|
|
6d48c0ead3 | ||
|
|
0e91a2f16e | ||
|
|
693048eb29 | ||
|
|
873a68949e | ||
|
|
f8c18d074a | ||
|
|
9fb0fe1e35 | ||
|
|
d599511c14 | ||
|
|
180908d244 | ||
|
|
30cf274874 | ||
|
|
10287c0dc3 | ||
|
|
939c573907 | ||
|
|
30d24d0954 | ||
|
|
4a2c21bc10 | ||
|
|
df78ffd081 | ||
|
|
0eac6483ec | ||
|
|
71070827f2 | ||
|
|
fb73bb7e25 | ||
|
|
e782b77475 | ||
|
|
d764f35c5d | ||
|
|
b06f938e00 | ||
|
|
ca0b966f44 | ||
|
|
514c2f0897 | ||
|
|
cef0186b7d | ||
|
|
af5ec473f3 | ||
|
|
faa3a612eb | ||
|
|
47717802f8 | ||
|
|
b79d19e0a7 | ||
|
|
0cef35324f | ||
|
|
1638354474 | ||
|
|
96e41fa985 | ||
|
|
4c1d706a85 | ||
|
|
b6d5542b35 | ||
|
|
72d7b91640 | ||
|
|
32ceeaa43f | ||
|
|
82da8d7586 | ||
|
|
096b8a19c5 | ||
|
|
8f51c80d76 | ||
|
|
33fc54d6e2 | ||
|
|
c8dcd002e2 | ||
|
|
49b8806cc4 | ||
|
|
e2cef55f94 | ||
|
|
500c0eafb9 | ||
|
|
e0c8a56db5 | ||
|
|
002e4eedf5 | ||
|
|
a991525391 | ||
|
|
7be3658533 | ||
|
|
fd854fb7f7 | ||
|
|
64711dfc2f | ||
|
|
0776ae41d3 | ||
|
|
ad017f3bb8 | ||
|
|
9eb9ce88b3 | ||
|
|
7af9715a1b | ||
|
|
dbd32f63dd | ||
|
|
d7aca1bcbd | ||
|
|
72c856ac13 | ||
|
|
038de926b3 | ||
|
|
4c471dff83 | ||
|
|
c1ea17b66f | ||
|
|
b22f936522 | ||
|
|
53e408f156 | ||
|
|
46bb9bb60b | ||
|
|
b10670ba44 | ||
|
|
f13f85de8e | ||
|
|
141011aaae | ||
|
|
4a2c54e5d8 | ||
|
|
2f156b3438 | ||
|
|
eded08bacf |
2
.github/workflows/cd.yml
vendored
2
.github/workflows/cd.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.18.x
|
||||
- name: Run goreleaser
|
||||
uses: goreleaser/goreleaser-action@v1
|
||||
with:
|
||||
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Continuous Integration
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.20
|
||||
GO_VERSION: 1.18
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -40,5 +40,4 @@ test/results/**
|
||||
oryxBuildBinary
|
||||
__debug_bin
|
||||
|
||||
.worktrees
|
||||
demo/output/*
|
||||
.worktrees
|
||||
@@ -26,5 +26,4 @@ linters-settings:
|
||||
max-func-lines: 0
|
||||
|
||||
run:
|
||||
go: '1.20'
|
||||
timeout: 10m
|
||||
go: 1.18
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# docker build -t lazygit .
|
||||
# docker run -it lazygit:latest /bin/sh
|
||||
|
||||
FROM golang:1.20 as build
|
||||
FROM golang:1.18 as build
|
||||
WORKDIR /go/src/github.com/jesseduffield/lazygit/
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
This directory contains stuff for recording lazygit demos.
|
||||
|
||||
109
demo/config.yml
109
demo/config.yml
@@ -1,109 +0,0 @@
|
||||
# Specify a command to be executed
|
||||
# like `/bin/bash -l`, `ls`, or any other commands
|
||||
# the default is bash for Linux
|
||||
# or powershell.exe for Windows
|
||||
command: echo "YOU NEED TO SPECIFY YOUR OWN COMMAND WITH THE -d ARG"
|
||||
|
||||
# Specify the current working directory path
|
||||
# the default is the current working directory path
|
||||
cwd: null
|
||||
|
||||
# Export additional ENV variables
|
||||
env:
|
||||
recording: true
|
||||
|
||||
# Explicitly set the number of columns
|
||||
# or use `auto` to take the current
|
||||
# number of columns of your shell
|
||||
cols: 120 # 100
|
||||
|
||||
# Explicitly set the number of rows
|
||||
# or use `auto` to take the current
|
||||
# number of rows of your shell
|
||||
rows: 35 # 30
|
||||
|
||||
# Amount of times to repeat GIF
|
||||
# If value is -1, play once
|
||||
# If value is 0, loop indefinitely
|
||||
# If value is a positive number, loop n times
|
||||
repeat: 0
|
||||
|
||||
# Quality
|
||||
# 1 - 100
|
||||
# Higher quality seems to make no difference, but running it through
|
||||
# gifsicle ends up with a much better compressed version.
|
||||
quality: 100
|
||||
|
||||
# Delay between frames in ms
|
||||
# If the value is `auto` use the actual recording delays
|
||||
frameDelay: auto
|
||||
|
||||
# Maximum delay between frames in ms
|
||||
# Ignored if the `frameDelay` isn't set to `auto`
|
||||
# Set to `auto` to prevent limiting the max idle time
|
||||
maxIdleTime: 2000
|
||||
|
||||
# The surrounding frame box
|
||||
# The `type` can be null, window, floating, or solid`
|
||||
# To hide the title use the value null
|
||||
# Don't forget to add a backgroundColor style with a null as type
|
||||
frameBox:
|
||||
type: floating
|
||||
title: Lazygit
|
||||
style:
|
||||
border: 0px black solid
|
||||
backgroundColor: "#1d1d1d"
|
||||
margin: -5px
|
||||
|
||||
# Add a watermark image to the rendered gif
|
||||
# You need to specify an absolute path for
|
||||
# the image on your machine or a URL, and you can also
|
||||
# add your own CSS styles
|
||||
watermark:
|
||||
imagePath: null
|
||||
style:
|
||||
position: absolute
|
||||
right: 15px
|
||||
bottom: 15px
|
||||
width: 100px
|
||||
opacity: 0.9
|
||||
|
||||
# Cursor style can be one of
|
||||
# `block`, `underline`, or `bar`
|
||||
cursorStyle: block
|
||||
|
||||
# Font family
|
||||
# You can use any font that is installed on your machine
|
||||
# in CSS-like syntax
|
||||
fontFamily: "DejaVuSansMono Nerd Font"
|
||||
|
||||
# The size of the font
|
||||
fontSize: 8
|
||||
|
||||
# The height of lines
|
||||
lineHeight: 1
|
||||
|
||||
# The spacing between letters
|
||||
letterSpacing: 0
|
||||
|
||||
# Theme
|
||||
theme:
|
||||
background: "transparent"
|
||||
foreground: "#dddad6"
|
||||
cursor: "#c7c7c7"
|
||||
black: "#7a7a7a"
|
||||
red: "#fc4384"
|
||||
green: "#b3e33b"
|
||||
yellow: "#ffa727"
|
||||
blue: "#102895"
|
||||
magenta: "#c930c7"
|
||||
cyan: "#00c5c7"
|
||||
white: "#c7c7c7"
|
||||
brightBlack: "#676767"
|
||||
brightRed: "#ff7fac"
|
||||
brightGreen: "#c8ed71"
|
||||
brightYellow: "#ebdf86"
|
||||
brightBlue: "#6871ff"
|
||||
brightMagenta: "#ff76ff"
|
||||
brightCyan: "#5ffdff"
|
||||
brightWhite: "#fffefe"
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
TYPE=$1
|
||||
TEST=$2
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [gif|mp4] <test path>"
|
||||
echo "e.g. using full path: $0 gif pkg/integration/tests/demo/nuke_working_tree.go"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ "$#" -ne 2 ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ "$TYPE" != "gif" ] && [ "$TYPE" != "mp4" ]
|
||||
then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$TEST" ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
WORKTREE_PATH=$(git worktree list | grep assets | awk '{print $1}')
|
||||
|
||||
if [ -z "$WORKTREE_PATH" ]
|
||||
then
|
||||
echo "Could not find assets worktree. You'll need to create a worktree for the assets branch using the following command:"
|
||||
echo "git worktree add .worktrees/assets assets"
|
||||
echo "The assets branch has no shared history with the main branch: it exists to store assets which are too large to store in the main branch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUTPUT_DIR="$WORKTREE_PATH/demo"
|
||||
|
||||
if ! command -v terminalizer &> /dev/null
|
||||
then
|
||||
echo "terminalizer could not be found"
|
||||
echo "Install it with: npm install -g terminalizer"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v "gifsicle" &> /dev/null
|
||||
then
|
||||
echo "gifsicle could not be found"
|
||||
echo "Install it with: npm install -g gifsicle"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get last part of the test path and set that as the output name
|
||||
# example test path: pkg/integration/tests/01_basic_test.go
|
||||
# For that we want: NAME=01_basic_test
|
||||
NAME=$(echo "$TEST" | sed -e 's/.*\///' | sed -e 's/\..*//')
|
||||
|
||||
# Add the demo to the tests list (if missing) so that it can be run
|
||||
go generate pkg/integration/tests/tests.go
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# First we record the demo into a yaml representation
|
||||
terminalizer -c demo/config.yml record --skip-sharing -d "go run cmd/integration_test/main.go cli --slow $TEST" "$OUTPUT_DIR/$NAME"
|
||||
# Then we render it into a gif
|
||||
terminalizer render "$OUTPUT_DIR/$NAME" -o "$OUTPUT_DIR/$NAME.gif"
|
||||
|
||||
# Then we convert it to either an mp4 or gif based on the command line argument
|
||||
if [ "$TYPE" = "mp4" ]
|
||||
then
|
||||
COMPRESSED_PATH="$OUTPUT_DIR/$NAME.mp4"
|
||||
ffmpeg -y -i "$OUTPUT_DIR/$NAME.gif" -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" "$COMPRESSED_PATH"
|
||||
else
|
||||
COMPRESSED_PATH="$OUTPUT_DIR/$NAME-compressed.gif"
|
||||
gifsicle --colors 256 --use-col=web -O3 < "$OUTPUT_DIR/$NAME.gif" > "$COMPRESSED_PATH"
|
||||
fi
|
||||
|
||||
echo "Demo recorded to $COMPRESSED_PATH"
|
||||
@@ -62,6 +62,7 @@ gui:
|
||||
showListFooter: true # for seeing the '5 of 20' message in list panels
|
||||
showRandomTip: true
|
||||
showBranchCommitHash: false # show commit hashes alongside branch names
|
||||
experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list
|
||||
showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
|
||||
showCommandLog: true
|
||||
showIcons: false # deprecated: use nerdFontsVersion instead
|
||||
@@ -70,7 +71,6 @@ gui:
|
||||
splitDiff: 'auto' # one of 'auto' | 'always'
|
||||
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
||||
border: 'single' # one of 'single' | 'double' | 'rounded' | 'hidden'
|
||||
animateExplosion: true # shows an explosion animation when nuking the working tree
|
||||
git:
|
||||
paging:
|
||||
colorArg: always
|
||||
@@ -106,7 +106,6 @@ git:
|
||||
parseEmoji: false
|
||||
diffContextSize: 3 # how many lines of context are shown around a change in diffs
|
||||
os:
|
||||
copyToClipboardCmd: '' # See 'Custom Command for Copying to Clipboard' section
|
||||
editPreset: '' # see 'Configuring File Editing' section
|
||||
edit: ''
|
||||
editAtLine: ''
|
||||
@@ -279,20 +278,6 @@ os:
|
||||
open: 'open {{filename}}'
|
||||
```
|
||||
|
||||
### Custom Command for Copying to Clipboard
|
||||
```yaml
|
||||
os:
|
||||
copyToClipboardCmd: ''
|
||||
```
|
||||
Specify an external command to invoke when copying to clipboard is requested. `{{text}` will be replaced by text to be copied. Default is to copy to system clipboard.
|
||||
|
||||
If you are working on a terminal that supports OSC52, the following command will let you take advantage of it:
|
||||
```
|
||||
os:
|
||||
copyToClipboardCmd: printf "\033]52;c;$(printf {{text}} | base64)\a" > /dev/tty
|
||||
```
|
||||
|
||||
|
||||
### Configuring File Editing
|
||||
|
||||
There are two commands for opening files, `o` for "open" and `e` for "edit". `o`
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
* [Keybindings](./keybindings)
|
||||
* [Undo/Redo](./Undoing.md)
|
||||
* [Searching/Filtering](./Searching.md)
|
||||
* [Stacked Branches](./Stacked_Branches.md)
|
||||
* [Dev docs](./dev)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Working with stacked branches
|
||||
|
||||
When working on a large branch it can often be useful to break it down into
|
||||
smaller pieces, and it can help to create separate branches for each independent
|
||||
chunk of changes. For example, you could have one branch for preparatory
|
||||
refactorings, one for backend changes, and one for frontend changes. Those
|
||||
branches would then all be stacked onto each other.
|
||||
|
||||
Git has support for rebasing such a stack as a whole; you can enable it by
|
||||
setting the git config `rebase.updateRfs` to true. If you then rebase the
|
||||
topmost branch of the stack, the other ones in the stack will follow. This
|
||||
includes interactive rebases, so for example amending a commit in the first
|
||||
branch of the stack will "just work" in the sense that it keeps the other
|
||||
branches properly stacked onto it.
|
||||
|
||||
Lazygit visualizes the invidual branch heads in the stack by marking them with a
|
||||
cyan asterisk (or a cyan branch symbol if you are using [nerd
|
||||
fonts](Config.md#display-nerd-fonts-icons)).
|
||||
@@ -1,82 +0,0 @@
|
||||
# Demo Recordings
|
||||
|
||||
We want our demo recordings to be consistent and easy to update if we make changes to Lazygit's UI. Luckily for us, we have an existing recording system for the sake of our integration tests, so we can piggyback on that.
|
||||
|
||||
You'll want to familiarise yourself with how integration tests are written: see [here](../../pkg/integration/README.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Ideally we'd run this whole thing through docker but we haven't got that working. So you will need:
|
||||
```
|
||||
# for recording
|
||||
npm i -g terminalizer
|
||||
# for gif compression
|
||||
npm i -g gifsicle
|
||||
# for mp4 conversion
|
||||
brew install ffmpeg
|
||||
|
||||
# font with icons
|
||||
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/DejaVuSansMono.tar.xz && \
|
||||
tar -xf DejaVuSansMono.tar.xz -C /usr/local/share/fonts && \
|
||||
rm DejaVuSansMono.tar.xz
|
||||
```
|
||||
|
||||
## Creating a demo
|
||||
|
||||
Demos are found in `pkg/integration/tests/demo/`. They are like regular integration tests but have `IsDemo: true` which has a few effects:
|
||||
* The bottom row of the UI is quieter so that we can render captions
|
||||
* Fetch/Push/Pull have artificial latency to mimic a network request
|
||||
* The loader at the bottom-right does not appear
|
||||
|
||||
In demos, we don't need to be as strict in our assertions as we are in tests. But it's still good to have some basic assertions so that if we automate the process of updating demos we'll know if one of them has broken.
|
||||
|
||||
You can use the same flow as we use with integration tests when you're writing a demo:
|
||||
* Setup the repo
|
||||
* Run the demo in sandbox mode to get a feel of what needs to happen
|
||||
* Come back and write the code to make it happen
|
||||
|
||||
### Adding captions
|
||||
|
||||
It's good to add captions explaining what task if being performed. Use the existing demos as a guide.
|
||||
|
||||
### Setting up the assets worktree
|
||||
|
||||
We store assets (which includes demo recordings) in the `assets` branch, which is a branch that shares no history with the main branch and exists purely for storing assets. Storing them separately means we don't clog up the code branches with large binaries.
|
||||
|
||||
The scripts and demo definitions live in the code branches but the output lives in the assets branch so to be able to create a video from a demo you'll need to create a linked worktree for the assets branch which you can do with:
|
||||
|
||||
```sh
|
||||
git worktree add .worktrees/assets assets
|
||||
```
|
||||
|
||||
Outputs will be stored in `.worktrees/assets/demos/`. We'll store three separate things:
|
||||
* the yaml of the recording
|
||||
* the original gif
|
||||
* either the compressed gif or the mp4 depending on the output you chose (see below)
|
||||
|
||||
### Recording the demo
|
||||
|
||||
Once you're happy with your demo you can record it using:
|
||||
```sh
|
||||
scripts/record_demo.sh [gif|mp4] <path>
|
||||
# e.g.
|
||||
scripts/record_demo.sh gif pkg/integration/tests/demo/interactive_rebase.go
|
||||
```
|
||||
|
||||
~~The gif format is for use in the first video of the readme (it has a larger size but has auto-play and looping)~~
|
||||
~~The mp4 format is for everything else (no looping, requires clicking, but smaller size).~~
|
||||
|
||||
Turns out that you can't store mp4s in a repo and link them from a README so we're gonna just use gifs across the board for now.
|
||||
|
||||
### Including demos in README/docs
|
||||
|
||||
If you've followed the above steps you'll end up with your output in your assets worktree.
|
||||
|
||||
Within that worktree, stage all three output files and raise a PR against the assets branch.
|
||||
|
||||
Then back in the code branch, in the doc, you can embed the recording like so:
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
This means we can update assets without needing to update the docs that embed them.
|
||||
@@ -2,4 +2,3 @@
|
||||
|
||||
* [Busy/Idle tracking](./Busy.md).
|
||||
* [Integration Tests](../../pkg/integration/README.md)
|
||||
* [Demo Recordings](./Demo_Recordings.md)
|
||||
|
||||
@@ -84,7 +84,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: Move commit down one
|
||||
<kbd><c-k></kbd>: Move commit up one
|
||||
<kbd>v</kbd>: Paste commits (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Amend commit with staged changes
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: Revert commit
|
||||
|
||||
@@ -103,7 +103,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: コミットを1つ下に移動
|
||||
<kbd><c-k></kbd>: コミットを1つ上に移動
|
||||
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: ステージされた変更でamendコミット
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: コミットをrevert
|
||||
|
||||
@@ -265,7 +265,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: 커밋을 1개 아래로 이동
|
||||
<kbd><c-k></kbd>: 커밋을 1개 위로 이동
|
||||
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Amend commit with staged changes
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: 커밋 되돌리기
|
||||
|
||||
@@ -143,7 +143,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: Verplaats commit 1 naar beneden
|
||||
<kbd><c-k></kbd>: Verplaats commit 1 naar boven
|
||||
<kbd>v</kbd>: Plak commits (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Wijzig commit met staged veranderingen
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: Commit ongedaan maken
|
||||
|
||||
@@ -69,7 +69,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: Przenieś commit 1 w dół
|
||||
<kbd><c-k></kbd>: Przenieś commit 1 w górę
|
||||
<kbd>v</kbd>: Wklej commity (przebieranie)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Popraw commit zmianami z poczekalni
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: Odwróć commit
|
||||
|
||||
@@ -151,7 +151,6 @@ _Связки клавиш_
|
||||
<kbd><c-j></kbd>: Переместить коммит вниз на один
|
||||
<kbd><c-k></kbd>: Переместить коммит вверх на один
|
||||
<kbd>v</kbd>: Вставить отобранные коммиты (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Править последний коммит с проиндексированными изменениями
|
||||
<kbd>a</kbd>: Установить/убрать автора коммита
|
||||
<kbd>t</kbd>: Отменить коммит
|
||||
|
||||
@@ -147,7 +147,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: 下移提交
|
||||
<kbd><c-k></kbd>: 上移提交
|
||||
<kbd>v</kbd>: 粘贴提交(拣选)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: 用已暂存的更改来修补提交
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: 还原提交
|
||||
|
||||
@@ -191,7 +191,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd><c-j></kbd>: 向下移動提交
|
||||
<kbd><c-k></kbd>: 向上移動提交
|
||||
<kbd>v</kbd>: 貼上提交 (揀選)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: 使用已預存的更改修正提交
|
||||
<kbd>a</kbd>: 設置/重設提交作者
|
||||
<kbd>t</kbd>: 還原提交
|
||||
|
||||
20
go.mod
20
go.mod
@@ -1,21 +1,24 @@
|
||||
module github.com/jesseduffield/lazygit
|
||||
|
||||
go 1.20
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aybabtme/humanlog v0.4.1
|
||||
github.com/cli/safeexec v1.0.0
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/fsmiamoto/git-todo-parser v0.0.5
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gdamore/tcell/v2 v2.6.0
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230806095321-ac7b03108825
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230723014157-03e858e46144
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
@@ -23,7 +26,7 @@ require (
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/kyokomi/emoji/v2 v2.2.8
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/mattn/go-runewidth v0.0.15
|
||||
github.com/mattn/go-runewidth v0.0.14
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
@@ -31,12 +34,9 @@ require (
|
||||
github.com/sanity-io/litter v1.5.2
|
||||
github.com/sasha-s/go-deadlock v0.3.1
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spf13/afero v1.9.5
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -64,11 +64,13 @@ require (
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
34
go.sum
34
go.sum
@@ -55,6 +55,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
|
||||
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
|
||||
@@ -82,9 +84,13 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsmiamoto/git-todo-parser v0.0.5 h1:Bhzd/vz/6Qm3udfkd6NO9fWfD3TpwR9ucp3N75/J5I8=
|
||||
github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg=
|
||||
github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
@@ -179,8 +185,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230806095321-ac7b03108825 h1:4Ea8qV/BbZAGcXd8MAufDsbwwfz2pbRZdqIodC/XHZs=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230806095321-ac7b03108825/go.mod h1:trXE7RRGL2hTsv+Ntk+SHLtRobg9JE138n3Ug/X2Cf4=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230723014157-03e858e46144 h1:gwy5JzP6+PhcPFG1obkUSLGcTkUY88sLKlCPOFjwtak=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230723014157-03e858e46144/go.mod h1:dJ/BEUt3OWtaRg/PmuJWendRqREhre9JQ1SLvqrVJ8s=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||
@@ -212,6 +218,7 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -223,9 +230,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.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -239,14 +246,15 @@ github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
@@ -268,8 +276,6 @@ github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68 h1:NSTj9xAKUu85d6pAdNFyblL84BfiOB1rVnzxQO/cYUk=
|
||||
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68/go.mod h1:PuJ7T6QKbsU7iVOHlhRoV3D/ipIAJsyiV4dbwcVaYj8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
@@ -308,6 +314,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
@@ -451,13 +458,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/text v0.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=
|
||||
@@ -467,8 +475,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/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=
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -266,11 +267,7 @@ func (app *App) Run(startArgs appTypes.StartArgs) error {
|
||||
|
||||
// Close closes any resources
|
||||
func (app *App) Close() error {
|
||||
for _, closer := range app.closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return slices.TryForEach(app.closers, func(closer io.Closer) error {
|
||||
return closer.Close()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
@@ -92,7 +92,7 @@ func getDaemonKind() DaemonKind {
|
||||
}
|
||||
|
||||
func getCommentChar() byte {
|
||||
cmd := exec.Command("git", "config", "--get", "--null", "core.commentChar")
|
||||
cmd := secureexec.Command("git", "config", "--get", "--null", "core.commentChar")
|
||||
if output, err := cmd.Output(); err == nil && len(output) == 2 {
|
||||
return output[0]
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type TodoLine struct {
|
||||
@@ -26,11 +26,11 @@ func (self *TodoLine) ToString() string {
|
||||
}
|
||||
|
||||
func TodoLinesToString(todoLines []TodoLine) string {
|
||||
lines := lo.Map(todoLines, func(todoLine TodoLine, _ int) string {
|
||||
lines := slices.Map(todoLines, func(todoLine TodoLine) string {
|
||||
return todoLine.ToString()
|
||||
})
|
||||
|
||||
return strings.Join(lo.Reverse(lines), "")
|
||||
return strings.Join(slices.Reverse(lines), "")
|
||||
}
|
||||
|
||||
type ChangeTodoAction struct {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/logs/tail"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -280,7 +280,7 @@ func mergeBuildInfo(buildInfo *BuildInfo) {
|
||||
}
|
||||
|
||||
func getGitVersionInfo() string {
|
||||
cmd := exec.Command("git", "--version")
|
||||
cmd := secureexec.Command("git", "--version")
|
||||
stdout, _ := cmd.Output()
|
||||
gitVersion := strings.Trim(strings.TrimPrefix(string(stdout), "git version "), " \r\n")
|
||||
return gitVersion
|
||||
|
||||
@@ -3,8 +3,8 @@ package app
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type errorMapping struct {
|
||||
@@ -18,7 +18,7 @@ func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
|
||||
|
||||
knownErrorMessages := []string{tr.MinGitVersionError}
|
||||
|
||||
if lo.Contains(knownErrorMessages, errorMessage) {
|
||||
if slices.Contains(knownErrorMessages, errorMessage) {
|
||||
return errorMessage, true
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
|
||||
},
|
||||
}
|
||||
|
||||
if mapping, ok := lo.Find(mappings, func(mapping errorMapping) bool {
|
||||
if mapping, ok := slices.Find(mappings, func(mapping errorMapping) bool {
|
||||
return strings.Contains(errorMessage, mapping.originalError)
|
||||
}); ok {
|
||||
return mapping.newError, true
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/maps"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/jesseduffield/lazygit/pkg/app"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
@@ -22,7 +23,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type bindingSection struct {
|
||||
@@ -129,7 +129,7 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
||||
|
||||
func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*bindingSection {
|
||||
excludedViews := []string{"stagingSecondary", "patchBuildingSecondary"}
|
||||
bindingsToDisplay := lo.Filter(bindings, func(binding *types.Binding, _ int) bool {
|
||||
bindingsToDisplay := slices.Filter(bindings, func(binding *types.Binding) bool {
|
||||
if lo.Contains(excludedViews, binding.ViewName) {
|
||||
return false
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*b
|
||||
return a.header.title < b.header.title
|
||||
})
|
||||
|
||||
return lo.Map(bindingGroups, func(hb headerWithBindings, _ int) *bindingSection {
|
||||
return slices.Map(bindingGroups, func(hb headerWithBindings) *bindingSection {
|
||||
return &bindingSection{
|
||||
title: hb.header.title,
|
||||
bindings: hb.bindings,
|
||||
|
||||
@@ -125,15 +125,6 @@ func (self *BisectCommands) Start() error {
|
||||
return self.cmd.New(cmdArgs).StreamOutput().Run()
|
||||
}
|
||||
|
||||
func (self *BisectCommands) StartWithTerms(oldTerm string, newTerm string) error {
|
||||
cmdArgs := NewGitCmd("bisect").Arg("start").
|
||||
Arg("--term-old=" + oldTerm).
|
||||
Arg("--term-new=" + newTerm).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).StreamOutput().Run()
|
||||
}
|
||||
|
||||
// tells us whether we've found our problem commit(s). We return a string slice of
|
||||
// commit sha's if we're done, and that slice may have more that one item if
|
||||
// skipped commits are involved.
|
||||
|
||||
@@ -2,7 +2,7 @@ package git_commands
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/maps"
|
||||
"github.com/samber/lo"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -97,5 +97,5 @@ func (self *BisectInfo) Bisecting() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return lo.Contains(maps.Values(self.statusMap), BisectStatusOld)
|
||||
return slices.Contains(maps.Values(self.statusMap), BisectStatusOld)
|
||||
}
|
||||
|
||||
@@ -70,21 +70,6 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CurrentBranchName get name of current branch
|
||||
func (self *BranchCommands) CurrentBranchName() (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-parse").
|
||||
Arg("--abbrev-ref").
|
||||
Arg("--verify").
|
||||
Arg("HEAD").
|
||||
ToArgv()
|
||||
|
||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err == nil {
|
||||
return strings.TrimSpace(output), nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Delete delete branch
|
||||
func (self *BranchCommands) Delete(branch string, force bool) error {
|
||||
cmdArgs := NewGitCmd("branch").
|
||||
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/go-git/v5/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// context:
|
||||
@@ -75,7 +75,7 @@ outer:
|
||||
if strings.EqualFold(reflogBranch.Name, branch.Name) {
|
||||
branch.Recency = reflogBranch.Recency
|
||||
branchesWithRecency = append(branchesWithRecency, branch)
|
||||
branches = utils.Remove(branches, j)
|
||||
branches = slices.Remove(branches, j)
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
@@ -87,14 +87,14 @@ outer:
|
||||
return a.Name < b.Name
|
||||
})
|
||||
|
||||
branches = utils.Prepend(branches, branchesWithRecency...)
|
||||
branches = slices.Prepend(branches, branchesWithRecency...)
|
||||
|
||||
foundHead := false
|
||||
for i, branch := range branches {
|
||||
if branch.Head {
|
||||
foundHead = true
|
||||
branch.Recency = " *"
|
||||
branches = utils.Move(branches, i, 0)
|
||||
branches = slices.Move(branches, i, 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ outer:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
branches = utils.Prepend(branches, &models.Branch{Name: info.RefName, DisplayName: info.DisplayName, Head: true, DetachedHead: info.DetachedHead, Recency: " *"})
|
||||
branches = slices.Prepend(branches, &models.Branch{Name: info.RefName, DisplayName: info.DisplayName, Head: true, DetachedHead: info.DetachedHead, Recency: " *"})
|
||||
}
|
||||
|
||||
configBranches, err := self.config.Branches()
|
||||
@@ -131,7 +131,7 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
|
||||
trimmedOutput := strings.TrimSpace(output)
|
||||
outputLines := strings.Split(trimmedOutput, "\n")
|
||||
|
||||
return lo.FilterMap(outputLines, func(line string, _ int) (*models.Branch, bool) {
|
||||
return slices.FilterMap(outputLines, func(line string) (*models.Branch, bool) {
|
||||
if line == "" {
|
||||
return nil, false
|
||||
}
|
||||
@@ -171,7 +171,7 @@ var branchFields = []string{
|
||||
"upstream:short",
|
||||
"upstream:track",
|
||||
"subject",
|
||||
"objectname",
|
||||
fmt.Sprintf("objectname:short=%d", utils.COMMIT_HASH_SHORT_SIZE),
|
||||
}
|
||||
|
||||
// Obtain branch information from parsed line output of getRawBranches()
|
||||
|
||||
@@ -3,6 +3,7 @@ package git_commands
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
@@ -51,7 +52,7 @@ func getCommitFilesFromFilenames(filenames string) []*models.CommitFile {
|
||||
}
|
||||
|
||||
// typical result looks like 'A my_file' meaning my_file was added
|
||||
return lo.Map(lo.Chunk(lines, 2), func(chunk []string, _ int) *models.CommitFile {
|
||||
return slices.Map(lo.Chunk(lines, 2), func(chunk []string) *models.CommitFile {
|
||||
return &models.CommitFile{
|
||||
ChangeStatus: chunk[0],
|
||||
Name: chunk[1],
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
@@ -135,7 +136,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||
}
|
||||
|
||||
if ancestor != "" {
|
||||
commits = setCommitMergedStatuses(ancestor, commits)
|
||||
commits = self.setCommitMergedStatuses(ancestor, commits)
|
||||
}
|
||||
|
||||
return commits, nil
|
||||
@@ -232,7 +233,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
commitShas := lo.FilterMap(commits, func(commit *models.Commit, _ int) (string, bool) {
|
||||
commitShas := slices.FilterMap(commits, func(commit *models.Commit) (string, bool) {
|
||||
return commit.Sha, commit.Sha != ""
|
||||
})
|
||||
|
||||
@@ -378,7 +379,7 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
|
||||
// Command does not have a commit associated, skip
|
||||
continue
|
||||
}
|
||||
commits = utils.Prepend(commits, &models.Commit{
|
||||
commits = slices.Prepend(commits, &models.Commit{
|
||||
Sha: t.Commit,
|
||||
Name: t.Msg,
|
||||
Status: models.StatusRebasing,
|
||||
@@ -492,11 +493,10 @@ func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
|
||||
}
|
||||
}
|
||||
|
||||
func setCommitMergedStatuses(ancestor string, commits []*models.Commit) []*models.Commit {
|
||||
func (self *CommitLoader) setCommitMergedStatuses(ancestor string, commits []*models.Commit) []*models.Commit {
|
||||
passedAncestor := false
|
||||
for i, commit := range commits {
|
||||
// some commits aren't really commits and don't have sha's, such as the update-ref todo
|
||||
if commit.Sha != "" && strings.HasPrefix(ancestor, commit.Sha) {
|
||||
if strings.HasPrefix(ancestor, commit.Sha) {
|
||||
passedAncestor = true
|
||||
}
|
||||
if commit.Status != models.StatusPushed && commit.Status != models.StatusUnpushed {
|
||||
|
||||
@@ -506,50 +506,3 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitLoader_setCommitMergedStatuses(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
commits []*models.Commit
|
||||
ancestor string
|
||||
expectedCommits []*models.Commit
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "basic",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
},
|
||||
ancestor: "67890",
|
||||
expectedCommits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusMerged},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusMerged},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "with update-ref",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
},
|
||||
ancestor: "deadbeef",
|
||||
expectedCommits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.testName, func(t *testing.T) {
|
||||
expectedCommits := setCommitMergedStatuses(scenario.ancestor, scenario.commits)
|
||||
assert.Equal(t, scenario.expectedCommits, expectedCommits)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,3 @@ func (self *ConfigCommands) GetCoreCommentChar() byte {
|
||||
|
||||
return '#'
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetRebaseUpdateRefs() bool {
|
||||
return self.gitConfig.GetBool("rebase.updateRefs")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/app/daemon"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
@@ -104,13 +105,7 @@ func (self *RebaseCommands) MoveCommitDown(commits []*models.Commit, index int)
|
||||
|
||||
sha := commits[index].Sha
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.MoveCommitDown,
|
||||
map[string]string{
|
||||
"shortSha": utils.ShortSha(sha),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
self.os.LogCommand(fmt.Sprintf("Moving TODO down: %s", utils.ShortSha(sha)), false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseShaOrRoot,
|
||||
@@ -124,13 +119,7 @@ func (self *RebaseCommands) MoveCommitUp(commits []*models.Commit, index int) er
|
||||
|
||||
sha := commits[index].Sha
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.MoveCommitUp,
|
||||
map[string]string{
|
||||
"shortSha": utils.ShortSha(sha),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
self.os.LogCommand(fmt.Sprintf("Moving TODO up: %s", utils.ShortSha(sha)), false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseShaOrRoot,
|
||||
@@ -161,37 +150,15 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index in
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) EditRebase(branchRef string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.EditRebase,
|
||||
map[string]string{
|
||||
"ref": branchRef,
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
self.os.LogCommand(fmt.Sprintf("Beginning interactive rebase at '%s'", branchRef), false)
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: branchRef,
|
||||
instruction: daemon.NewInsertBreakInstruction(),
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) EditRebaseFromBaseCommit(targetBranchName string, baseCommit string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.EditRebaseFromBaseCommit,
|
||||
map[string]string{
|
||||
"baseCommit": baseCommit,
|
||||
"targetBranchName": targetBranchName,
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseCommit,
|
||||
onto: targetBranchName,
|
||||
instruction: daemon.NewInsertBreakInstruction(),
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func logTodoChanges(changes []daemon.ChangeTodoAction) string {
|
||||
changeTodoStr := strings.Join(lo.Map(changes, func(c daemon.ChangeTodoAction, _ int) string {
|
||||
changeTodoStr := strings.Join(slices.Map(changes, func(c daemon.ChangeTodoAction) string {
|
||||
return fmt.Sprintf("%s:%s", c.Sha, c.NewAction)
|
||||
}), "\n")
|
||||
return fmt.Sprintf("Changing TODO actions: %s", changeTodoStr)
|
||||
@@ -199,7 +166,6 @@ func logTodoChanges(changes []daemon.ChangeTodoAction) string {
|
||||
|
||||
type PrepareInteractiveRebaseCommandOpts struct {
|
||||
baseShaOrRoot string
|
||||
onto string
|
||||
instruction daemon.Instruction
|
||||
overrideEditor bool
|
||||
keepCommitsThatBecomeEmpty bool
|
||||
@@ -218,7 +184,6 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
|
||||
ArgIf(opts.keepCommitsThatBecomeEmpty && !self.version.IsOlderThan(2, 26, 0), "--empty=keep").
|
||||
Arg("--no-autosquash").
|
||||
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
|
||||
ArgIf(opts.onto != "", "--onto", opts.onto).
|
||||
Arg(opts.baseShaOrRoot).
|
||||
ToArgv()
|
||||
|
||||
@@ -342,13 +307,6 @@ func (self *RebaseCommands) RebaseBranch(branchName string) error {
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseShaOrRoot: branchName}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) RebaseBranchFromBaseCommit(targetBranchName string, baseCommit string) error {
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseCommit,
|
||||
onto: targetBranchName,
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd(commandType).Arg("--" + command).ToArgv()
|
||||
|
||||
@@ -437,13 +395,7 @@ func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
|
||||
commitLines := lo.Map(commits, func(commit *models.Commit, _ int) string {
|
||||
return fmt.Sprintf("%s %s", utils.ShortSha(commit.Sha), commit.Name)
|
||||
})
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.CherryPickCommits,
|
||||
map[string]string{
|
||||
"commitLines": strings.Join(commitLines, "\n"),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
self.os.LogCommand(fmt.Sprintf("Cherry-picking commits:\n%s", strings.Join(commitLines, "\n")), false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: "HEAD",
|
||||
|
||||
@@ -4,13 +4,12 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type RemoteLoader struct {
|
||||
@@ -54,7 +53,7 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
||||
return nil, remoteBranchesErr
|
||||
}
|
||||
|
||||
remotes := lo.Map(goGitRemotes, func(goGitRemote *gogit.Remote, _ int) *models.Remote {
|
||||
remotes := slices.Map(goGitRemotes, func(goGitRemote *gogit.Remote) *models.Remote {
|
||||
remoteName := goGitRemote.Config().Name
|
||||
branches := remoteBranchesByRemoteName[remoteName]
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type StashLoader struct {
|
||||
@@ -69,7 +69,7 @@ func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
|
||||
|
||||
rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return lo.Map(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||
return self.stashEntryFromLine(line, index)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -72,14 +71,3 @@ func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
|
||||
func (self *StatusCommands) IsInMergeState() (bool, error) {
|
||||
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD"))
|
||||
}
|
||||
|
||||
// Full ref (e.g. "refs/heads/mybranch") of the branch that is currently
|
||||
// being rebased, or empty string when we're not in a rebase
|
||||
func (self *StatusCommands) BranchBeingRebased() string {
|
||||
for _, dir := range []string{"rebase-merge", "rebase-apply"} {
|
||||
if bytesContent, err := os.ReadFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), dir, "head-name")); err == nil {
|
||||
return strings.TrimSpace(string(bytesContent))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -12,19 +12,16 @@ func NewTagCommands(gitCommon *GitCommon) *TagCommands {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateLightweight(tagName string, ref string, force bool) error {
|
||||
cmdArgs := NewGitCmd("tag").
|
||||
ArgIf(force, "--force").
|
||||
Arg("--", tagName).
|
||||
func (self *TagCommands) CreateLightweight(tagName string, ref string) error {
|
||||
cmdArgs := NewGitCmd("tag").Arg("--", tagName).
|
||||
ArgIf(len(ref) > 0, ref).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string, force bool) error {
|
||||
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
|
||||
cmdArgs := NewGitCmd("tag").Arg(tagName).
|
||||
ArgIf(force, "--force").
|
||||
ArgIf(len(ref) > 0, ref).
|
||||
Arg("-m", msg).
|
||||
ToArgv()
|
||||
@@ -32,15 +29,6 @@ func (self *TagCommands) CreateAnnotated(tagName, ref, msg string, force bool) e
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *TagCommands) HasTag(tagName string) bool {
|
||||
cmdArgs := NewGitCmd("show-ref").
|
||||
Arg("--tags", "--quiet", "--verify", "--").
|
||||
Arg("refs/tags/" + tagName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run() == nil
|
||||
}
|
||||
|
||||
func (self *TagCommands) Delete(tagName string) error {
|
||||
cmdArgs := NewGitCmd("tag").Arg("-d", tagName).
|
||||
ToArgv()
|
||||
|
||||
@@ -3,11 +3,11 @@ package git_commands
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type TagLoader struct {
|
||||
@@ -38,7 +38,7 @@ func (self *TagLoader) GetTags() ([]*models.Tag, error) {
|
||||
|
||||
lineRegex := regexp.MustCompile(`^([^\s]+)(\s+)?(.*)$`)
|
||||
|
||||
tags := lo.Map(split, func(line string, _ int) *models.Tag {
|
||||
tags := slices.Map(split, func(line string) *models.Tag {
|
||||
matches := lineRegex.FindStringSubmatch(line)
|
||||
tagName := matches[1]
|
||||
message := ""
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
)
|
||||
|
||||
// including license from https://github.com/tcnksm/go-gitconfig because this file is an adaptation of that repo's code
|
||||
@@ -53,10 +55,10 @@ func runGitConfigCmd(cmd *exec.Cmd) (string, error) {
|
||||
|
||||
func getGitConfigCmd(key string) *exec.Cmd {
|
||||
gitArgs := []string{"config", "--get", "--null", key}
|
||||
return exec.Command("git", gitArgs...)
|
||||
return secureexec.Command("git", gitArgs...)
|
||||
}
|
||||
|
||||
func getGitConfigGeneralCmd(args string) *exec.Cmd {
|
||||
gitArgs := append([]string{"config"}, strings.Split(args, " ")...)
|
||||
return exec.Command("git", gitArgs...)
|
||||
return secureexec.Command("git", gitArgs...)
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ import (
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
)
|
||||
|
||||
// This package is for handling logic specific to a git hosting service like github, gitlab, bitbucket, gitea, etc.
|
||||
@@ -112,7 +111,7 @@ func (self *HostingServiceMgr) getCandidateServiceDomains() []ServiceDomain {
|
||||
|
||||
serviceDefinition, ok := serviceDefinitionByProvider[provider]
|
||||
if !ok {
|
||||
providerNames := lo.Map(serviceDefinitions, func(serviceDefinition ServiceDefinition, _ int) string {
|
||||
providerNames := slices.Map(serviceDefinitions, func(serviceDefinition ServiceDefinition) string {
|
||||
return serviceDefinition.provider
|
||||
})
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@ type ICmdObj interface {
|
||||
}
|
||||
|
||||
type CmdObj struct {
|
||||
// the secureexec package will swap out the first arg with the full path to the binary,
|
||||
// so we store these args separately so that ToString() will output the original
|
||||
args []string
|
||||
|
||||
cmd *exec.Cmd
|
||||
|
||||
runner ICmdObjRunner
|
||||
@@ -117,7 +121,7 @@ func (self *CmdObj) GetCmd() *exec.Cmd {
|
||||
|
||||
func (self *CmdObj) ToString() string {
|
||||
// if a given arg contains a space, we need to wrap it in quotes
|
||||
quotedArgs := lo.Map(self.cmd.Args, func(arg string, _ int) string {
|
||||
quotedArgs := lo.Map(self.args, func(arg string, _ int) string {
|
||||
if strings.Contains(arg, " ") {
|
||||
return `"` + arg + `"`
|
||||
}
|
||||
@@ -128,7 +132,7 @@ func (self *CmdObj) ToString() string {
|
||||
}
|
||||
|
||||
func (self *CmdObj) Args() []string {
|
||||
return self.cmd.Args
|
||||
return self.args
|
||||
}
|
||||
|
||||
func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
|
||||
|
||||
@@ -3,9 +3,9 @@ package oscommands
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/mgutz/str"
|
||||
)
|
||||
|
||||
@@ -27,10 +27,11 @@ type CmdObjBuilder struct {
|
||||
var _ ICmdObjBuilder = &CmdObjBuilder{}
|
||||
|
||||
func (self *CmdObjBuilder) New(args []string) ICmdObj {
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd := secureexec.Command(args[0], args[1:]...)
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
return &CmdObj{
|
||||
args: args,
|
||||
cmd: cmd,
|
||||
runner: self.runner,
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ func TestCmdObjToString(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
cmd := exec.Command(scenario.cmdArgs[0], scenario.cmdArgs[1:]...)
|
||||
cmdObj := &CmdObj{cmd: cmd}
|
||||
cmdObj := &CmdObj{args: scenario.cmdArgs}
|
||||
actual := cmdObj.ToString()
|
||||
if actual != scenario.expected {
|
||||
t.Errorf("Expected %s, got %s", quote(scenario.expected), quote(actual))
|
||||
|
||||
@@ -3,6 +3,7 @@ package oscommands
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
@@ -105,7 +106,7 @@ func CopyDir(src string, dst string) (err error) {
|
||||
return //nolint: nakedret
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(src)
|
||||
entries, err := ioutil.ReadDir(src)
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
}
|
||||
@@ -120,14 +121,8 @@ func CopyDir(src string, dst string) (err error) {
|
||||
return //nolint: nakedret
|
||||
}
|
||||
} else {
|
||||
var info os.FileInfo
|
||||
info, err = entry.Info()
|
||||
if err != nil {
|
||||
return //nolint: nakedret
|
||||
}
|
||||
|
||||
// Skip symlinks.
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
if entry.Mode()&os.ModeSymlink != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package oscommands
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -122,7 +124,27 @@ func (self *FakeCmdObjRunner) ExpectFunc(description string, fn func(cmdObj ICmd
|
||||
func (self *FakeCmdObjRunner) ExpectArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner {
|
||||
description := fmt.Sprintf("matches args %s", strings.Join(expectedArgs, " "))
|
||||
self.ExpectFunc(description, func(cmdObj ICmdObj) bool {
|
||||
return slices.Equal(expectedArgs, cmdObj.GetCmd().Args)
|
||||
args := cmdObj.GetCmd().Args
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// thanks to the secureexec package, the first arg is something like
|
||||
// '"C:\\Program Files\\Git\\mingw64\\bin\\<command>.exe"
|
||||
// on windows so we'll just ensure it contains our program
|
||||
if !strings.Contains(args[0], expectedArgs[0]) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// first arg is the program name
|
||||
if expectedArgs[0] != args[0] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if !slices.Equal(expectedArgs[1:], args[1:]) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, output, err)
|
||||
|
||||
return self
|
||||
@@ -131,7 +153,18 @@ func (self *FakeCmdObjRunner) ExpectArgs(expectedArgs []string, output string, e
|
||||
func (self *FakeCmdObjRunner) ExpectGitArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner {
|
||||
description := fmt.Sprintf("matches git args %s", strings.Join(expectedArgs, " "))
|
||||
self.ExpectFunc(description, func(cmdObj ICmdObj) bool {
|
||||
return slices.Equal(expectedArgs, cmdObj.GetCmd().Args[1:])
|
||||
// first arg is 'git' on unix and something like '"C:\\Program Files\\Git\\mingw64\\bin\\git.exe" on windows so we'll just ensure it ends in either 'git' or 'git.exe'
|
||||
re := regexp.MustCompile(`git(\.exe)?$`)
|
||||
args := cmdObj.GetCmd().Args
|
||||
if !re.MatchString(args[0]) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !slices.Equal(expectedArgs, args[1:]) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, output, err)
|
||||
|
||||
return self
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/atotto/clipboard"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/kill"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
@@ -117,15 +118,7 @@ func (c *OSCommand) Quote(message string) string {
|
||||
|
||||
// AppendLineToFile adds a new line in file
|
||||
func (c *OSCommand) AppendLineToFile(filename, line string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
c.Tr.Log.AppendingLineToFile,
|
||||
map[string]string{
|
||||
"line": line,
|
||||
"filename": filename,
|
||||
},
|
||||
)
|
||||
c.LogCommand(msg, false)
|
||||
|
||||
c.LogCommand(fmt.Sprintf("Appending '%s' to file '%s'", line, filename), false)
|
||||
f, err := os.OpenFile(filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0o600)
|
||||
if err != nil {
|
||||
return utils.WrapError(err)
|
||||
@@ -162,13 +155,7 @@ func (c *OSCommand) AppendLineToFile(filename, line string) error {
|
||||
|
||||
// CreateFileWithContent creates a file with the given content
|
||||
func (c *OSCommand) CreateFileWithContent(path string, content string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
c.Tr.Log.CreateFileWithContent,
|
||||
map[string]string{
|
||||
"path": path,
|
||||
},
|
||||
)
|
||||
c.LogCommand(msg, false)
|
||||
c.LogCommand(fmt.Sprintf("Creating file '%s'", path), false)
|
||||
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
c.Log.Error(err)
|
||||
return err
|
||||
@@ -184,13 +171,7 @@ func (c *OSCommand) CreateFileWithContent(path string, content string) error {
|
||||
|
||||
// Remove removes a file or directory at the specified path
|
||||
func (c *OSCommand) Remove(filename string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
c.Tr.Log.Remove,
|
||||
map[string]string{
|
||||
"filename": filename,
|
||||
},
|
||||
)
|
||||
c.LogCommand(msg, false)
|
||||
c.LogCommand(fmt.Sprintf("Removing '%s'", filename), false)
|
||||
err := os.RemoveAll(filename)
|
||||
return utils.WrapError(err)
|
||||
}
|
||||
@@ -208,7 +189,7 @@ func (c *OSCommand) FileExists(path string) (bool, error) {
|
||||
|
||||
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
|
||||
func (c *OSCommand) PipeCommands(cmdObjs ...ICmdObj) error {
|
||||
cmds := lo.Map(cmdObjs, func(cmdObj ICmdObj, _ int) *exec.Cmd {
|
||||
cmds := slices.Map(cmdObjs, func(cmdObj ICmdObj) *exec.Cmd {
|
||||
return cmdObj.GetCmd()
|
||||
})
|
||||
|
||||
@@ -285,32 +266,12 @@ func PrepareForChildren(cmd *exec.Cmd) {
|
||||
func (c *OSCommand) CopyToClipboard(str string) error {
|
||||
escaped := strings.Replace(str, "\n", "\\n", -1)
|
||||
truncated := utils.TruncateWithEllipsis(escaped, 40)
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
c.Tr.Log.CopyToClipboard,
|
||||
map[string]string{
|
||||
"str": truncated,
|
||||
},
|
||||
)
|
||||
c.LogCommand(msg, false)
|
||||
if c.UserConfig.OS.CopyToClipboardCmd != "" {
|
||||
cmdStr := utils.ResolvePlaceholderString(c.UserConfig.OS.CopyToClipboardCmd, map[string]string{
|
||||
"text": c.Cmd.Quote(str),
|
||||
})
|
||||
return c.Cmd.NewShell(cmdStr).Run()
|
||||
}
|
||||
|
||||
c.LogCommand(fmt.Sprintf("Copying '%s' to clipboard", truncated), false)
|
||||
return clipboard.WriteAll(str)
|
||||
}
|
||||
|
||||
func (c *OSCommand) RemoveFile(path string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
c.Tr.Log.RemoveFile,
|
||||
map[string]string{
|
||||
"path": path,
|
||||
},
|
||||
)
|
||||
c.LogCommand(msg, false)
|
||||
c.LogCommand(fmt.Sprintf("Deleting path '%s'", path), false)
|
||||
|
||||
return c.removeFileFn(path)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package oscommands
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cli/safeexec"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -19,11 +20,13 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
||||
test func(error)
|
||||
}
|
||||
|
||||
fullCmdPath, _ := safeexec.LookPath("cmd")
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
filename: "test",
|
||||
runner: NewFakeRunner(t).
|
||||
ExpectArgs([]string{"cmd", "/c", "start", "", "test"}, "", errors.New("error")),
|
||||
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "test"}, "", errors.New("error")),
|
||||
test: func(err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
@@ -31,7 +34,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
||||
{
|
||||
filename: "test",
|
||||
runner: NewFakeRunner(t).
|
||||
ExpectArgs([]string{"cmd", "/c", "start", "", "test"}, "", nil),
|
||||
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "test"}, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
@@ -39,7 +42,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
||||
{
|
||||
filename: "filename with spaces",
|
||||
runner: NewFakeRunner(t).
|
||||
ExpectArgs([]string{"cmd", "/c", "start", "", "filename with spaces"}, "", nil),
|
||||
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "filename with spaces"}, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
@@ -47,7 +50,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
||||
{
|
||||
filename: "let's_test_with_single_quote",
|
||||
runner: NewFakeRunner(t).
|
||||
ExpectArgs([]string{"cmd", "/c", "start", "", "let's_test_with_single_quote"}, "", nil),
|
||||
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "let's_test_with_single_quote"}, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
@@ -55,7 +58,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
||||
{
|
||||
filename: "$USER.txt",
|
||||
runner: NewFakeRunner(t).
|
||||
ExpectArgs([]string{"cmd", "/c", "start", "", "$USER.txt"}, "", nil),
|
||||
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "$USER.txt"}, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/maps"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -208,10 +209,10 @@ func (p *PatchBuilder) renderEachFilePatch(plain bool) []string {
|
||||
filenames := maps.Keys(p.fileInfoMap)
|
||||
|
||||
sort.Strings(filenames)
|
||||
patches := lo.Map(filenames, func(filename string, _ int) string {
|
||||
patches := slices.Map(filenames, func(filename string) string {
|
||||
return p.RenderPatchForFile(filename, plain, false)
|
||||
})
|
||||
output := lo.Filter(patches, func(patch string, _ int) bool {
|
||||
output := slices.Filter(patches, func(patch string) bool {
|
||||
return patch != ""
|
||||
})
|
||||
|
||||
|
||||
@@ -318,9 +318,8 @@ type AppState struct {
|
||||
StartupPopupVersion int
|
||||
|
||||
// these are for custom commands typed in directly, not for custom commands in the lazygit config
|
||||
CustomCommandsHistory []string
|
||||
HideCommandLog bool
|
||||
IgnoreWhitespaceInDiffView bool
|
||||
CustomCommandsHistory []string
|
||||
HideCommandLog bool
|
||||
}
|
||||
|
||||
func getDefaultAppState() *AppState {
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func isWSL() bool {
|
||||
data, err := os.ReadFile("/proc/sys/kernel/osrelease")
|
||||
data, err := ioutil.ReadFile("/proc/sys/kernel/osrelease")
|
||||
return err == nil && strings.Contains(string(data), "microsoft")
|
||||
}
|
||||
|
||||
func isContainer() bool {
|
||||
data, err := os.ReadFile("/proc/1/cgroup")
|
||||
data, err := ioutil.ReadFile("/proc/1/cgroup")
|
||||
|
||||
if strings.Contains(string(data), "docker") ||
|
||||
strings.Contains(string(data), "/lxc/") ||
|
||||
|
||||
@@ -27,36 +27,36 @@ type RefresherConfig struct {
|
||||
}
|
||||
|
||||
type GuiConfig struct {
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipDiscardChangeWarning bool `yaml:"skipDiscardChangeWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
Language string `yaml:"language"`
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
ShortTimeFormat string `yaml:"shortTimeFormat"`
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
NerdFontsVersion string `yaml:"nerdFontsVersion"`
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
Border string `yaml:"border"`
|
||||
AnimateExplosion bool `yaml:"animateExplosion"`
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipDiscardChangeWarning bool `yaml:"skipDiscardChangeWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
Language string `yaml:"language"`
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
ShortTimeFormat string `yaml:"shortTimeFormat"`
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
NerdFontsVersion string `yaml:"nerdFontsVersion"`
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"`
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
Border string `yaml:"border"`
|
||||
}
|
||||
|
||||
type ThemeConfig struct {
|
||||
@@ -68,8 +68,6 @@ type ThemeConfig struct {
|
||||
SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"`
|
||||
CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"`
|
||||
CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"`
|
||||
MarkedBaseCommitBgColor []string `yaml:"markedBaseCommitBgColor"`
|
||||
MarkedBaseCommitFgColor []string `yaml:"markedBaseCommitFgColor"`
|
||||
UnstagedChangesColor []string `yaml:"unstagedChangesColor"`
|
||||
DefaultFgColor []string `yaml:"defaultFgColor"`
|
||||
}
|
||||
@@ -270,7 +268,6 @@ type KeybindingCommitsConfig struct {
|
||||
CherryPickCopy string `yaml:"cherryPickCopy"`
|
||||
CherryPickCopyRange string `yaml:"cherryPickCopyRange"`
|
||||
PasteCommits string `yaml:"pasteCommits"`
|
||||
MarkCommitAsBaseForRebase string `yaml:"markCommitAsBaseForRebase"`
|
||||
CreateTag string `yaml:"tagCommit"`
|
||||
CheckoutCommit string `yaml:"checkoutCommit"`
|
||||
ResetCherryPick string `yaml:"resetCherryPick"`
|
||||
@@ -357,9 +354,6 @@ type OSConfig struct {
|
||||
// OpenLinkCommand is the command for opening a link
|
||||
// Deprecated: use OpenLink instead.
|
||||
OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
|
||||
|
||||
// CopyToClipboardCmd is the command for copying to clipboard
|
||||
CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"`
|
||||
}
|
||||
|
||||
type CustomCommandAfterHook struct {
|
||||
@@ -436,26 +430,24 @@ func GetDefaultConfig() *UserConfig {
|
||||
SelectedRangeBgColor: []string{"blue"},
|
||||
CherryPickedCommitBgColor: []string{"cyan"},
|
||||
CherryPickedCommitFgColor: []string{"blue"},
|
||||
MarkedBaseCommitBgColor: []string{"yellow"},
|
||||
MarkedBaseCommitFgColor: []string{"blue"},
|
||||
UnstagedChangesColor: []string{"red"},
|
||||
DefaultFgColor: []string{"default"},
|
||||
},
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ShowBranchCommitHash: false,
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "single",
|
||||
AnimateExplosion: true,
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ExperimentalShowBranchHeads: false,
|
||||
ShowBranchCommitHash: false,
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "single",
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
@@ -620,7 +612,6 @@ func GetDefaultConfig() *UserConfig {
|
||||
CherryPickCopy: "c",
|
||||
CherryPickCopyRange: "C",
|
||||
PasteCommits: "v",
|
||||
MarkCommitAsBaseForRebase: "B",
|
||||
CreateTag: "T",
|
||||
CheckoutCommit: "<space>",
|
||||
ResetCherryPick: "<c-R>",
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@@ -134,7 +134,7 @@ func (self *ContextMgr) pushToContextStack(c types.Context) ([]types.Context, ty
|
||||
(topContext.GetKind() == types.MAIN_CONTEXT && c.GetKind() == types.MAIN_CONTEXT) {
|
||||
|
||||
contextsToDeactivate = append(contextsToDeactivate, topContext)
|
||||
_, self.ContextStack = utils.Pop(self.ContextStack)
|
||||
_, self.ContextStack = slices.Pop(self.ContextStack)
|
||||
}
|
||||
|
||||
self.ContextStack = append(self.ContextStack, c)
|
||||
@@ -154,7 +154,7 @@ func (self *ContextMgr) Pop() error {
|
||||
}
|
||||
|
||||
var currentContext types.Context
|
||||
currentContext, self.ContextStack = utils.Pop(self.ContextStack)
|
||||
currentContext, self.ContextStack = slices.Pop(self.ContextStack)
|
||||
|
||||
newContext := self.ContextStack[len(self.ContextStack)-1]
|
||||
|
||||
|
||||
@@ -83,7 +83,3 @@ func (self *BranchesContext) GetDiffTerminals() []string {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BranchesContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type CommitFilesContext struct {
|
||||
@@ -34,7 +34,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
}
|
||||
|
||||
lines := presentation.RenderCommitFileTree(viewModel, c.Modes().Diffing.Ref, c.Git().Patch.PatchBuilder)
|
||||
return lo.Map(lines, func(line string, _ int) []string {
|
||||
return slices.Map(lines, func(line string) []string {
|
||||
return []string{line}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,18 +38,13 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
}
|
||||
|
||||
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING
|
||||
showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
|
||||
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
c.Common,
|
||||
c.Model().Commits,
|
||||
c.Model().Branches,
|
||||
c.Model().CheckedOutBranch,
|
||||
showBranchMarkerForHeadCommit,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
c.Modes().MarkedBaseCommit.GetSha(),
|
||||
c.UserConfig.Gui.TimeFormat,
|
||||
c.UserConfig.Gui.ShortTimeFormat,
|
||||
time.Now(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
@@ -81,11 +82,11 @@ func (self *MenuViewModel) SetMenuItems(items []*types.MenuItem, columnAlignment
|
||||
// TODO: move into presentation package
|
||||
func (self *MenuViewModel) GetDisplayStrings(_startIdx int, _length int) [][]string {
|
||||
menuItems := self.FilteredListViewModel.GetItems()
|
||||
showKeys := lo.SomeBy(menuItems, func(item *types.MenuItem) bool {
|
||||
showKeys := slices.Some(menuItems, func(item *types.MenuItem) bool {
|
||||
return item.Key != nil
|
||||
})
|
||||
|
||||
return lo.Map(menuItems, func(item *types.MenuItem, _ int) []string {
|
||||
return slices.Map(menuItems, func(item *types.MenuItem) []string {
|
||||
displayStrings := item.LabelColumns
|
||||
|
||||
if !showKeys {
|
||||
@@ -106,18 +107,18 @@ func (self *MenuViewModel) GetDisplayStrings(_startIdx int, _length int) [][]str
|
||||
keyStyle = style.FgDefault.SetStrikethrough()
|
||||
}
|
||||
|
||||
displayStrings = utils.Prepend(displayStrings, keyStyle.Sprint(keyLabel))
|
||||
displayStrings = slices.Prepend(displayStrings, keyStyle.Sprint(keyLabel))
|
||||
return displayStrings
|
||||
})
|
||||
}
|
||||
|
||||
func (self *MenuContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
basicBindings := self.ListContextTrait.GetKeybindings(opts)
|
||||
menuItemsWithKeys := lo.Filter(self.menuItems, func(item *types.MenuItem, _ int) bool {
|
||||
menuItemsWithKeys := slices.Filter(self.menuItems, func(item *types.MenuItem) bool {
|
||||
return item.Key != nil
|
||||
})
|
||||
|
||||
menuItemBindings := lo.Map(menuItemsWithKeys, func(item *types.MenuItem, _ int) *types.Binding {
|
||||
menuItemBindings := slices.Map(menuItemsWithKeys, func(item *types.MenuItem) *types.Binding {
|
||||
return &types.Binding{
|
||||
Key: item.Key,
|
||||
Handler: func() error { return self.OnMenuPress(item) },
|
||||
|
||||
@@ -86,7 +86,3 @@ func (self *ReflogCommitsContext) GetDiffTerminals() []string {
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *ReflogCommitsContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -72,7 +72,3 @@ func (self *RemoteBranchesContext) GetDiffTerminals() []string {
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *RemoteBranchesContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -37,13 +37,6 @@ func NewSubCommitsContext(
|
||||
}
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
// This can happen if a sub-commits view is asked to be rerendered while
|
||||
// it is invisble; for example when switching screen modes, which
|
||||
// rerenders all views.
|
||||
if viewModel.GetRef() == nil {
|
||||
return [][]string{}
|
||||
}
|
||||
|
||||
selectedCommitSha := ""
|
||||
if c.CurrentContext().GetKey() == SUB_COMMITS_CONTEXT_KEY {
|
||||
selectedCommit := viewModel.GetSelected()
|
||||
@@ -51,21 +44,12 @@ func NewSubCommitsContext(
|
||||
selectedCommitSha = selectedCommit.Sha
|
||||
}
|
||||
}
|
||||
branches := []*models.Branch{}
|
||||
if viewModel.GetShowBranchHeads() {
|
||||
branches = c.Model().Branches
|
||||
}
|
||||
showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
c.Common,
|
||||
c.Model().SubCommits,
|
||||
branches,
|
||||
viewModel.GetRef().RefName(),
|
||||
showBranchMarkerForHeadCommit,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
"",
|
||||
c.UserConfig.Gui.TimeFormat,
|
||||
c.UserConfig.Gui.ShortTimeFormat,
|
||||
time.Now(),
|
||||
@@ -113,8 +97,7 @@ type SubCommitsViewModel struct {
|
||||
ref types.Ref
|
||||
*ListViewModel[*models.Commit]
|
||||
|
||||
limitCommits bool
|
||||
showBranchHeads bool
|
||||
limitCommits bool
|
||||
}
|
||||
|
||||
func (self *SubCommitsViewModel) SetRef(ref types.Ref) {
|
||||
@@ -125,14 +108,6 @@ func (self *SubCommitsViewModel) GetRef() types.Ref {
|
||||
return self.ref
|
||||
}
|
||||
|
||||
func (self *SubCommitsViewModel) SetShowBranchHeads(value bool) {
|
||||
self.showBranchHeads = value
|
||||
}
|
||||
|
||||
func (self *SubCommitsViewModel) GetShowBranchHeads() bool {
|
||||
return self.showBranchHeads
|
||||
}
|
||||
|
||||
func (self *SubCommitsContext) GetSelectedItemId() string {
|
||||
item := self.GetSelected()
|
||||
if item == nil {
|
||||
|
||||
@@ -69,7 +69,3 @@ func (self *TagsContext) GetDiffTerminals() []string {
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *TagsContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type WorkingTreeContext struct {
|
||||
@@ -25,7 +25,7 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
lines := presentation.RenderFileTree(viewModel, c.Modes().Diffing.Ref, c.Model().Submodules)
|
||||
return lo.Map(lines, func(line string, _ int) []string {
|
||||
return slices.Map(lines, func(line string) []string {
|
||||
return []string{line}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
stagingHelper,
|
||||
mergeConflictsHelper,
|
||||
worktreeHelper,
|
||||
gui.fileWatcher,
|
||||
)
|
||||
diffHelper := helpers.NewDiffHelper(helperCommon)
|
||||
cherryPickHelper := helpers.NewCherryPickHelper(
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type BisectController struct {
|
||||
@@ -67,18 +65,12 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
// ref, because we'll be reloading our commits in that case.
|
||||
waitToReselect := selectCurrentAfter && !self.c.Git().Bisect.ReachableFromStart(info)
|
||||
|
||||
// If we have a current sha already, then we always want to use that one. If
|
||||
// not, we're still picking the initial commits before we really start, so
|
||||
// use the selected commit in that case.
|
||||
shaToMark := lo.Ternary(info.GetCurrentSha() != "", info.GetCurrentSha(), commit.Sha)
|
||||
shortShaToMark := utils.ShortSha(shaToMark)
|
||||
|
||||
menuItems := []*types.MenuItem{
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, shortShaToMark, info.NewTerm()),
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
||||
if err := self.c.Git().Bisect.Mark(shaToMark, info.NewTerm()); err != nil {
|
||||
if err := self.c.Git().Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -87,10 +79,10 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
Key: 'b',
|
||||
},
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, shortShaToMark, info.OldTerm()),
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
||||
if err := self.c.Git().Bisect.Mark(shaToMark, info.OldTerm()); err != nil {
|
||||
if err := self.c.Git().Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -99,21 +91,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
Key: 'g',
|
||||
},
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.SkipCurrent, shortShaToMark),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BisectSkip)
|
||||
if err := self.c.Git().Bisect.Skip(shaToMark); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.afterMark(selectCurrentAfter, waitToReselect)
|
||||
},
|
||||
Key: 's',
|
||||
},
|
||||
}
|
||||
if info.GetCurrentSha() != "" && info.GetCurrentSha() != commit.Sha {
|
||||
menuItems = append(menuItems, lo.ToPtr(types.MenuItem{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.SkipSelected, commit.ShortSha()),
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Skip, commit.ShortSha()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BisectSkip)
|
||||
if err := self.c.Git().Bisect.Skip(commit.Sha); err != nil {
|
||||
@@ -122,16 +100,16 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
|
||||
return self.afterMark(selectCurrentAfter, waitToReselect)
|
||||
},
|
||||
Key: 'S',
|
||||
}))
|
||||
}
|
||||
menuItems = append(menuItems, lo.ToPtr(types.MenuItem{
|
||||
Label: self.c.Tr.Bisect.ResetOption,
|
||||
OnPress: func() error {
|
||||
return self.c.Helpers().Bisect.Reset()
|
||||
Key: 's',
|
||||
},
|
||||
Key: 'r',
|
||||
}))
|
||||
{
|
||||
Label: self.c.Tr.Bisect.ResetOption,
|
||||
OnPress: func() error {
|
||||
return self.c.Helpers().Bisect.Reset()
|
||||
},
|
||||
Key: 'r',
|
||||
},
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.Bisect.BisectMenuTitle,
|
||||
@@ -175,28 +153,6 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo,
|
||||
},
|
||||
Key: 'g',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.Bisect.ChooseTerms,
|
||||
OnPress: func() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: self.c.Tr.Bisect.OldTermPrompt,
|
||||
HandleConfirm: func(oldTerm string) error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: self.c.Tr.Bisect.NewTermPrompt,
|
||||
HandleConfirm: func(newTerm string) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
||||
if err := self.c.Git().Bisect.StartWithTerms(oldTerm, newTerm); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
Key: 't',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func (self *CommitFilesController) GetOnRenderToMain() func() error {
|
||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
|
||||
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(
|
||||
from, to, reverse, node.GetPath(), false, self.c.GetAppState().IgnoreWhitespaceInDiffView,
|
||||
from, to, reverse, node.GetPath(), false, self.c.State().GetIgnoreWhitespaceInDiffView(),
|
||||
)
|
||||
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package controllers
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -40,7 +41,7 @@ func (self *CustomCommandAction) Call() error {
|
||||
|
||||
func (self *CustomCommandAction) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion {
|
||||
// reversing so that we display the latest command first
|
||||
history := lo.Reverse(self.c.GetAppState().CustomCommandsHistory)
|
||||
history := slices.Reverse(self.c.GetAppState().CustomCommandsHistory)
|
||||
|
||||
return helpers.FuzzySearchFunc(history)
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ func (self *FilesController) GetOnRenderToMain() func() error {
|
||||
split := self.c.UserConfig.Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||
mainShowsStaged := !split && node.GetHasStagedChanges()
|
||||
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged, self.c.GetAppState().IgnoreWhitespaceInDiffView)
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
title := self.c.Tr.UnstagedChanges
|
||||
if mainShowsStaged {
|
||||
title = self.c.Tr.StagedChanges
|
||||
@@ -216,7 +216,7 @@ func (self *FilesController) GetOnRenderToMain() func() error {
|
||||
}
|
||||
|
||||
if split {
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true, self.c.GetAppState().IgnoreWhitespaceInDiffView)
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
|
||||
title := self.c.Tr.StagedChanges
|
||||
if mainShowsStaged {
|
||||
|
||||
@@ -61,45 +61,17 @@ func (self *ConfirmationHelper) DeactivateConfirmationPrompt() {
|
||||
self.clearConfirmationViewKeyBindings()
|
||||
}
|
||||
|
||||
// Temporary hack: we're just duplicating the logic in `gocui.lineWrap`
|
||||
func getMessageHeight(wrap bool, message string, width int) int {
|
||||
if !wrap {
|
||||
return len(strings.Split(message, "\n"))
|
||||
}
|
||||
|
||||
lineCount := 0
|
||||
lines := strings.Split(message, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
n := 0
|
||||
lastWhitespaceIndex := -1
|
||||
for i, currChr := range line {
|
||||
rw := runewidth.RuneWidth(currChr)
|
||||
n += rw
|
||||
|
||||
if n > width {
|
||||
if currChr == ' ' {
|
||||
n = 0
|
||||
} else if currChr == '-' {
|
||||
n = rw
|
||||
} else if lastWhitespaceIndex != -1 && lastWhitespaceIndex+1 != i {
|
||||
if line[lastWhitespaceIndex] == '-' {
|
||||
n = i - lastWhitespaceIndex
|
||||
} else {
|
||||
n = i - lastWhitespaceIndex + 1
|
||||
}
|
||||
} else {
|
||||
n = rw
|
||||
}
|
||||
lineCount++
|
||||
lastWhitespaceIndex = -1
|
||||
} else if currChr == ' ' || currChr == '-' {
|
||||
lastWhitespaceIndex = i
|
||||
}
|
||||
lineCount := 0
|
||||
// if we need to wrap, calculate height to fit content within view's width
|
||||
if wrap {
|
||||
for _, line := range lines {
|
||||
lineCount += runewidth.StringWidth(line)/width + 1
|
||||
}
|
||||
lineCount++
|
||||
} else {
|
||||
lineCount = len(lines)
|
||||
}
|
||||
|
||||
return lineCount
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ func (self *DiffHelper) DiffArgs() []string {
|
||||
output = append(output, "-R")
|
||||
}
|
||||
|
||||
if self.c.GetAppState().IgnoreWhitespaceInDiffView {
|
||||
if self.c.State().GetIgnoreWhitespaceInDiffView() {
|
||||
output = append(output, "--ignore-all-space")
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (self *DiffHelper) WithDiffModeCheck(f func() error) error {
|
||||
}
|
||||
|
||||
func (self *DiffHelper) IgnoringWhitespaceSubTitle() string {
|
||||
if self.c.GetAppState().IgnoreWhitespaceInDiffView {
|
||||
if self.c.State().GetIgnoreWhitespaceInDiffView() {
|
||||
return self.c.Tr.IgnoreWhitespaceDiffViewSubTitle
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type MergeAndRebaseHelper struct {
|
||||
@@ -52,7 +51,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
|
||||
})
|
||||
}
|
||||
|
||||
menuItems := lo.Map(options, func(row optionAndKey, _ int) *types.MenuItem {
|
||||
menuItems := slices.Map(options, func(row optionAndKey) *types.MenuItem {
|
||||
return &types.MenuItem{
|
||||
Label: row.option,
|
||||
OnPress: func() error {
|
||||
@@ -225,20 +224,8 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
||||
Key: 's',
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(task gocui.Task) error {
|
||||
baseCommit := self.c.Modes().MarkedBaseCommit.GetSha()
|
||||
var err error
|
||||
if baseCommit != "" {
|
||||
err = self.c.Git().Rebase.RebaseBranchFromBaseCommit(ref, baseCommit)
|
||||
} else {
|
||||
err = self.c.Git().Rebase.RebaseBranch(ref)
|
||||
}
|
||||
err = self.CheckMergeOrRebase(err)
|
||||
if err == nil {
|
||||
self.c.Modes().MarkedBaseCommit.Reset()
|
||||
}
|
||||
return err
|
||||
})
|
||||
err := self.c.Git().Rebase.RebaseBranch(ref)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -247,26 +234,17 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
||||
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
||||
baseCommit := self.c.Modes().MarkedBaseCommit.GetSha()
|
||||
var err error
|
||||
if baseCommit != "" {
|
||||
err = self.c.Git().Rebase.EditRebaseFromBaseCommit(ref, baseCommit)
|
||||
} else {
|
||||
err = self.c.Git().Rebase.EditRebase(ref)
|
||||
}
|
||||
err := self.c.Git().Rebase.EditRebase(ref)
|
||||
if err = self.CheckMergeOrRebase(err); err != nil {
|
||||
return err
|
||||
}
|
||||
self.c.Modes().MarkedBaseCommit.Reset()
|
||||
return self.c.PushContext(self.c.Contexts().LocalCommits)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
title := utils.ResolvePlaceholderString(
|
||||
lo.Ternary(self.c.Modes().MarkedBaseCommit.GetSha() != "",
|
||||
self.c.Tr.RebasingFromBaseCommitTitle,
|
||||
self.c.Tr.RebasingTitle),
|
||||
self.c.Tr.RebasingTitle,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranch,
|
||||
"ref": ref,
|
||||
@@ -305,8 +283,3 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *MergeAndRebaseHelper) ResetMarkedBaseCommit() error {
|
||||
self.c.Modes().MarkedBaseCommit.Reset()
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type ModeHelper struct {
|
||||
@@ -82,16 +82,6 @@ func (self *ModeHelper) Statuses() []ModeStatus {
|
||||
},
|
||||
Reset: self.ExitFilterMode,
|
||||
},
|
||||
{
|
||||
IsActive: self.c.Modes().MarkedBaseCommit.Active,
|
||||
Description: func() string {
|
||||
return self.withResetButton(
|
||||
self.c.Tr.MarkedBaseCommitStatus,
|
||||
style.FgCyan,
|
||||
)
|
||||
},
|
||||
Reset: self.mergeAndRebaseHelper.ResetMarkedBaseCommit,
|
||||
},
|
||||
{
|
||||
IsActive: self.c.Modes().CherryPicking.Active,
|
||||
Description: func() string {
|
||||
@@ -145,13 +135,13 @@ func (self *ModeHelper) withResetButton(content string, textStyle style.TextStyl
|
||||
}
|
||||
|
||||
func (self *ModeHelper) GetActiveMode() (ModeStatus, bool) {
|
||||
return lo.Find(self.Statuses(), func(mode ModeStatus) bool {
|
||||
return slices.Find(self.Statuses(), func(mode ModeStatus) bool {
|
||||
return mode.IsActive()
|
||||
})
|
||||
}
|
||||
|
||||
func (self *ModeHelper) IsAnyModeActive() bool {
|
||||
return lo.SomeBy(self.Statuses(), func(mode ModeStatus) bool {
|
||||
return slices.Some(self.Statuses(), func(mode ModeStatus) bool {
|
||||
return mode.IsActive()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
@@ -17,7 +18,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type RefreshHelper struct {
|
||||
@@ -28,6 +28,7 @@ type RefreshHelper struct {
|
||||
stagingHelper *StagingHelper
|
||||
mergeConflictsHelper *MergeConflictsHelper
|
||||
worktreeHelper *WorktreeHelper
|
||||
fileWatcher types.IFileWatcher
|
||||
}
|
||||
|
||||
func NewRefreshHelper(
|
||||
@@ -38,6 +39,7 @@ func NewRefreshHelper(
|
||||
stagingHelper *StagingHelper,
|
||||
mergeConflictsHelper *MergeConflictsHelper,
|
||||
worktreeHelper *WorktreeHelper,
|
||||
fileWatcher types.IFileWatcher,
|
||||
) *RefreshHelper {
|
||||
return &RefreshHelper{
|
||||
c: c,
|
||||
@@ -47,6 +49,7 @@ func NewRefreshHelper(
|
||||
stagingHelper: stagingHelper,
|
||||
mergeConflictsHelper: mergeConflictsHelper,
|
||||
worktreeHelper: worktreeHelper,
|
||||
fileWatcher: fileWatcher,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,10 +96,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
refresh := func(name string, f func()) {
|
||||
// if we're in a demo we don't want any async refreshes because
|
||||
// everything happens fast and it's better to have everything update
|
||||
// in the one frame
|
||||
if !self.c.InDemo() && options.Mode == types.ASYNC {
|
||||
if options.Mode == types.ASYNC {
|
||||
self.c.OnWorker(func(t gocui.Task) {
|
||||
f()
|
||||
})
|
||||
@@ -211,7 +211,7 @@ func getScopeNames(scopes []types.RefreshableView) []string {
|
||||
types.MERGE_CONFLICTS: "mergeConflicts",
|
||||
}
|
||||
|
||||
return lo.Map(scopes, func(scope types.RefreshableView, _ int) string {
|
||||
return slices.Map(scopes, func(scope types.RefreshableView) string {
|
||||
return scopeNameMap[scope]
|
||||
})
|
||||
}
|
||||
@@ -272,32 +272,6 @@ func (self *RefreshHelper) refreshCommitsAndCommitFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) determineCheckedOutBranchName() string {
|
||||
if rebasedBranch := self.c.Git().Status.BranchBeingRebased(); rebasedBranch != "" {
|
||||
// During a rebase we're on a detached head, so cannot determine the
|
||||
// branch name in the usual way. We need to read it from the
|
||||
// ".git/rebase-merge/head-name" file instead.
|
||||
return strings.TrimPrefix(rebasedBranch, "refs/heads/")
|
||||
}
|
||||
|
||||
if bisectInfo := self.c.Git().Bisect.GetInfo(); bisectInfo.Bisecting() && bisectInfo.GetStartSha() != "" {
|
||||
// Likewise, when we're bisecting we're on a detached head as well. In
|
||||
// this case we read the branch name from the ".git/BISECT_START" file.
|
||||
return bisectInfo.GetStartSha()
|
||||
}
|
||||
|
||||
// In all other cases, get the branch name by asking git what branch is
|
||||
// checked out. Note that if we're on a detached head (for reasons other
|
||||
// than rebasing or bisecting, i.e. it was explicitly checked out), then
|
||||
// this will return its sha.
|
||||
if branchName, err := self.c.Git().Branch.CurrentBranchName(); err == nil {
|
||||
return branchName
|
||||
}
|
||||
|
||||
// Should never get here unless the working copy is corrupt
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||
self.c.Mutexes().LocalCommitsMutex.Lock()
|
||||
defer self.c.Mutexes().LocalCommitsMutex.Unlock()
|
||||
@@ -317,7 +291,6 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||
self.c.Model().Commits = commits
|
||||
self.RefreshAuthors(commits)
|
||||
self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
|
||||
self.c.Model().CheckedOutBranch = self.determineCheckedOutBranchName()
|
||||
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
}
|
||||
@@ -439,12 +412,6 @@ func (self *RefreshHelper) refreshBranches() {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
// Need to re-render the commits view because the visualization of local
|
||||
// branch heads might have changed
|
||||
if err := self.c.Contexts().LocalCommits.HandleRender(); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
self.refreshStatus()
|
||||
}
|
||||
|
||||
@@ -545,6 +512,10 @@ func (self *RefreshHelper) refreshStateFiles() error {
|
||||
fileTreeViewModel.SetTree()
|
||||
fileTreeViewModel.RWMutex.Unlock()
|
||||
|
||||
if err := self.fileWatcher.AddFilesToFileWatcher(files); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type IRefsHelper interface {
|
||||
@@ -132,7 +132,7 @@ func (self *RefsHelper) CreateGitResetMenu(ref string) error {
|
||||
{strength: "hard", label: "Hard reset", key: 'h'},
|
||||
}
|
||||
|
||||
menuItems := lo.Map(strengths, func(row strengthWithKey, _ int) *types.MenuItem {
|
||||
menuItems := slices.Map(strengths, func(row strengthWithKey) *types.MenuItem {
|
||||
return &types.MenuItem{
|
||||
LabelColumns: []string{
|
||||
row.label,
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
@@ -17,7 +18,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type onNewRepoFn func(startArgs appTypes.StartArgs, contextKey types.ContextKey) error
|
||||
@@ -115,7 +115,7 @@ func (self *ReposHelper) CreateRecentReposMenu() error {
|
||||
|
||||
wg.Wait()
|
||||
|
||||
menuItems := lo.Map(recentRepoPaths, func(path string, _ int) *types.MenuItem {
|
||||
menuItems := slices.Map(recentRepoPaths, func(path string) *types.MenuItem {
|
||||
branchName, _ := currentBranches.Load(path)
|
||||
if icons.IsIconEnabled() {
|
||||
branchName = icons.BRANCH_ICON + " " + fmt.Sprintf("%v", branchName)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/jesseduffield/minimal/gitignore"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
"gopkg.in/ozeidan/fuzzy-patricia.v3/patricia"
|
||||
)
|
||||
|
||||
@@ -48,13 +48,13 @@ func NewSuggestionsHelper(
|
||||
}
|
||||
|
||||
func (self *SuggestionsHelper) getRemoteNames() []string {
|
||||
return lo.Map(self.c.Model().Remotes, func(remote *models.Remote, _ int) string {
|
||||
return slices.Map(self.c.Model().Remotes, func(remote *models.Remote) string {
|
||||
return remote.Name
|
||||
})
|
||||
}
|
||||
|
||||
func matchesToSuggestions(matches []string) []*types.Suggestion {
|
||||
return lo.Map(matches, func(match string, _ int) *types.Suggestion {
|
||||
return slices.Map(matches, func(match string) *types.Suggestion {
|
||||
return &types.Suggestion{
|
||||
Value: match,
|
||||
Label: match,
|
||||
@@ -69,7 +69,7 @@ func (self *SuggestionsHelper) GetRemoteSuggestionsFunc() func(string) []*types.
|
||||
}
|
||||
|
||||
func (self *SuggestionsHelper) getBranchNames() []string {
|
||||
return lo.Map(self.c.Model().Branches, func(branch *models.Branch, _ int) string {
|
||||
return slices.Map(self.c.Model().Branches, func(branch *models.Branch) string {
|
||||
return branch.Name
|
||||
})
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func (self *SuggestionsHelper) GetBranchNameSuggestionsFunc() func(string) []*ty
|
||||
matchingBranchNames = utils.FuzzySearch(input, branchNames)
|
||||
}
|
||||
|
||||
return lo.Map(matchingBranchNames, func(branchName string, _ int) *types.Suggestion {
|
||||
return slices.Map(matchingBranchNames, func(branchName string) *types.Suggestion {
|
||||
return &types.Suggestion{
|
||||
Value: branchName,
|
||||
Label: presentation.GetBranchTextStyle(branchName).Sprint(branchName),
|
||||
@@ -141,8 +141,8 @@ func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*type
|
||||
}
|
||||
|
||||
func (self *SuggestionsHelper) getRemoteBranchNames(separator string) []string {
|
||||
return lo.FlatMap(self.c.Model().Remotes, func(remote *models.Remote, _ int) []string {
|
||||
return lo.Map(remote.Branches, func(branch *models.RemoteBranch, _ int) string {
|
||||
return slices.FlatMap(self.c.Model().Remotes, func(remote *models.Remote) []string {
|
||||
return slices.Map(remote.Branches, func(branch *models.RemoteBranch) string {
|
||||
return fmt.Sprintf("%s%s%s", remote.Name, separator, branch.Name)
|
||||
})
|
||||
})
|
||||
@@ -153,7 +153,7 @@ func (self *SuggestionsHelper) GetRemoteBranchesSuggestionsFunc(separator string
|
||||
}
|
||||
|
||||
func (self *SuggestionsHelper) getTagNames() []string {
|
||||
return lo.Map(self.c.Model().Tags, func(tag *models.Tag, _ int) string {
|
||||
return slices.Map(self.c.Model().Tags, func(tag *models.Tag) string {
|
||||
return tag.Name
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type TagsHelper struct {
|
||||
@@ -20,16 +19,16 @@ func NewTagsHelper(c *HelperCommon, commitsHelper *CommitsHelper) *TagsHelper {
|
||||
}
|
||||
|
||||
func (self *TagsHelper) OpenCreateTagPrompt(ref string, onCreate func()) error {
|
||||
doCreateTag := func(tagName string, description string, force bool) error {
|
||||
onConfirm := func(tagName string, description string) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.CreatingTag, func(gocui.Task) error {
|
||||
if description != "" {
|
||||
self.c.LogAction(self.c.Tr.Actions.CreateAnnotatedTag)
|
||||
if err := self.c.Git().Tag.CreateAnnotated(tagName, ref, description, force); err != nil {
|
||||
if err := self.c.Git().Tag.CreateAnnotated(tagName, ref, description); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
} else {
|
||||
self.c.LogAction(self.c.Tr.Actions.CreateLightweightTag)
|
||||
if err := self.c.Git().Tag.CreateLightweight(tagName, ref, force); err != nil {
|
||||
if err := self.c.Git().Tag.CreateLightweight(tagName, ref); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -42,28 +41,6 @@ func (self *TagsHelper) OpenCreateTagPrompt(ref string, onCreate func()) error {
|
||||
})
|
||||
}
|
||||
|
||||
onConfirm := func(tagName string, description string) error {
|
||||
if self.c.Git().Tag.HasTag(tagName) {
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.ForceTagPrompt,
|
||||
map[string]string{
|
||||
"tagName": tagName,
|
||||
"cancelKey": self.c.UserConfig.Keybinding.Universal.Return,
|
||||
"confirmKey": self.c.UserConfig.Keybinding.Universal.Confirm,
|
||||
},
|
||||
)
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.ForceTag,
|
||||
Prompt: prompt,
|
||||
HandleConfirm: func() error {
|
||||
return doCreateTag(tagName, description, true)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
return doCreateTag(tagName, description, false)
|
||||
}
|
||||
}
|
||||
|
||||
return self.commitsHelper.OpenCommitMessagePanel(
|
||||
&OpenCommitMessagePanelOpts{
|
||||
CommitIndex: context.NoCommitIndex,
|
||||
|
||||
@@ -3,8 +3,8 @@ package helpers
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestGetSuggestedRemote(t *testing.T) {
|
||||
}
|
||||
|
||||
func mkRemoteList(names ...string) []*models.Remote {
|
||||
return lo.Map(names, func(name string, _ int) *models.Remote {
|
||||
return slices.Map(names, func(name string) *models.Remote {
|
||||
return &models.Remote{Name: name}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -201,18 +201,12 @@ func (self *WindowArrangementHelper) infoSectionChildren(informationStr string,
|
||||
appStatusBox.Weight = 1
|
||||
} else {
|
||||
optionsBox.Weight = 1
|
||||
if self.c.InDemo() {
|
||||
// app status appears very briefly in demos and dislodges the caption,
|
||||
// so better not to show it at all
|
||||
appStatusBox.Size = 0
|
||||
} else {
|
||||
appStatusBox.Size = runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(appStatus)
|
||||
}
|
||||
appStatusBox.Size = runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(appStatus)
|
||||
}
|
||||
|
||||
result := []*boxlayout.Box{appStatusBox, optionsBox}
|
||||
|
||||
if (!self.c.InDemo() && self.c.UserConfig.Gui.ShowBottomLine) || self.modeHelper.IsAnyModeActive() {
|
||||
if self.c.UserConfig.Gui.ShowBottomLine || self.modeHelper.IsAnyModeActive() {
|
||||
result = append(result, &boxlayout.Box{
|
||||
Window: "information",
|
||||
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
|
||||
|
||||
@@ -100,15 +100,9 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.PasteCommits),
|
||||
Handler: self.paste,
|
||||
Handler: opts.Guards.OutsideFilterMode(self.paste),
|
||||
Description: self.c.Tr.PasteCommits,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.MarkCommitAsBaseForRebase),
|
||||
Handler: self.checkSelected(self.markAsBaseCommit),
|
||||
Description: self.c.Tr.MarkAsBaseCommit,
|
||||
Tooltip: self.c.Tr.MarkAsBaseCommitTooltip,
|
||||
},
|
||||
// overriding these navigation keybindings because we might need to load
|
||||
// more commits on demand
|
||||
{
|
||||
@@ -177,7 +171,7 @@ func (self *LocalCommitsController) GetOnRenderToMain() func() error {
|
||||
"ref": commit.Name,
|
||||
}))
|
||||
} else {
|
||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Sha, self.c.Modes().Filtering.GetPath(), self.c.GetAppState().IgnoreWhitespaceInDiffView)
|
||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Sha, self.c.Modes().Filtering.GetPath(), self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
}
|
||||
|
||||
@@ -421,15 +415,10 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoComma
|
||||
}
|
||||
|
||||
self.c.LogAction("Update rebase TODO")
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.Log.HandleMidRebaseCommand,
|
||||
map[string]string{
|
||||
"shortSha": commit.ShortSha(),
|
||||
"action": action.String(),
|
||||
},
|
||||
self.c.LogCommand(
|
||||
fmt.Sprintf("Updating rebase action of commit %s to '%s'", commit.ShortSha(), action.String()),
|
||||
false,
|
||||
)
|
||||
self.c.LogCommand(msg, false)
|
||||
|
||||
if err := self.c.Git().Rebase.EditRebaseTodo(commit, action); err != nil {
|
||||
return false, self.c.Error(err)
|
||||
@@ -457,14 +446,7 @@ func (self *LocalCommitsController) moveDown(commit *models.Commit) error {
|
||||
// logging directly here because MoveTodoDown doesn't have enough information
|
||||
// to provide a useful log
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.Log.MovingCommitDown,
|
||||
map[string]string{
|
||||
"shortSha": commit.ShortSha(),
|
||||
},
|
||||
)
|
||||
self.c.LogCommand(msg, false)
|
||||
self.c.LogCommand(fmt.Sprintf("Moving commit %s down", commit.ShortSha()), false)
|
||||
|
||||
if err := self.c.Git().Rebase.MoveTodoDown(commit); err != nil {
|
||||
return self.c.Error(err)
|
||||
@@ -499,13 +481,10 @@ func (self *LocalCommitsController) moveUp(commit *models.Commit) error {
|
||||
// logging directly here because MoveTodoDown doesn't have enough information
|
||||
// to provide a useful log
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.Log.MovingCommitUp,
|
||||
map[string]string{
|
||||
"shortSha": commit.ShortSha(),
|
||||
},
|
||||
self.c.LogCommand(
|
||||
fmt.Sprintf("Moving commit %s up", commit.ShortSha()),
|
||||
false,
|
||||
)
|
||||
self.c.LogCommand(msg, false)
|
||||
|
||||
if err := self.c.Git().Rebase.MoveTodoUp(self.c.Model().Commits[index]); err != nil {
|
||||
return self.c.Error(err)
|
||||
@@ -861,16 +840,6 @@ func (self *LocalCommitsController) paste() error {
|
||||
return self.c.Helpers().CherryPick.Paste()
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) markAsBaseCommit(commit *models.Commit) error {
|
||||
if commit.Sha == self.c.Modes().MarkedBaseCommit.GetSha() {
|
||||
// Reset when invoking it again on the marked commit
|
||||
self.c.Modes().MarkedBaseCommit.SetSha("")
|
||||
} else {
|
||||
self.c.Modes().MarkedBaseCommit.SetSha(commit.Sha)
|
||||
}
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) isHeadCommit() bool {
|
||||
return models.IsHeadCommit(self.c.Model().Commits, self.context().GetSelectedLineIdx())
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ func (self *MergeConflictsController) HandleUndo() error {
|
||||
}
|
||||
|
||||
self.c.LogAction("Restoring file to previous state")
|
||||
self.c.LogCommand(self.c.Tr.Log.HandleUndo, false)
|
||||
self.c.LogCommand("Undoing last conflict resolution", false)
|
||||
if err := os.WriteFile(state.GetPath(), []byte(state.GetContent()), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -20,7 +21,7 @@ func (self *OptionsMenuAction) Call() error {
|
||||
|
||||
bindings := self.getBindings(ctx)
|
||||
|
||||
menuItems := lo.Map(bindings, func(binding *types.Binding, _ int) *types.MenuItem {
|
||||
menuItems := slices.Map(bindings, func(binding *types.Binding) *types.MenuItem {
|
||||
return &types.MenuItem{
|
||||
OpensMenu: binding.OpensMenu,
|
||||
Label: binding.Description,
|
||||
|
||||
@@ -37,7 +37,7 @@ func (self *ReflogCommitsController) GetOnRenderToMain() func() error {
|
||||
if commit == nil {
|
||||
task = types.NewRenderStringTask("No reflog history")
|
||||
} else {
|
||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Sha, self.c.Modes().Filtering.GetPath(), self.c.GetAppState().IgnoreWhitespaceInDiffView)
|
||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Sha, self.c.Modes().Filtering.GetPath(), self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
|
||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func (self *StashController) GetOnRenderToMain() func() error {
|
||||
task = types.NewRunPtyTask(
|
||||
self.c.Git().Stash.ShowStashEntryCmdObj(
|
||||
stashEntry.Index,
|
||||
self.c.GetAppState().IgnoreWhitespaceInDiffView,
|
||||
self.c.State().GetIgnoreWhitespaceInDiffView(),
|
||||
).GetCmd(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type StatusController struct {
|
||||
@@ -155,7 +155,7 @@ func (self *StatusController) askForConfigFile(action func(file string) error) e
|
||||
case 1:
|
||||
return action(confPaths[0])
|
||||
default:
|
||||
menuItems := lo.Map(confPaths, func(path string, _ int) *types.MenuItem {
|
||||
menuItems := slices.Map(confPaths, func(path string) *types.MenuItem {
|
||||
return &types.MenuItem{
|
||||
Label: path,
|
||||
OnPress: func() error {
|
||||
|
||||
@@ -38,7 +38,7 @@ func (self *SubCommitsController) GetOnRenderToMain() func() error {
|
||||
if commit == nil {
|
||||
task = types.NewRenderStringTask("No commits")
|
||||
} else {
|
||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Sha, self.c.Modes().Filtering.GetPath(), self.c.GetAppState().IgnoreWhitespaceInDiffView)
|
||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Sha, self.c.Modes().Filtering.GetPath(), self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
|
||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func (self *SubmodulesController) GetOnRenderToMain() func() error {
|
||||
if file == nil {
|
||||
task = types.NewRenderStringTask(prefix)
|
||||
} else {
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, self.c.GetAppState().IgnoreWhitespaceInDiffView)
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
task = types.NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ var _ types.IController = &SwitchToSubCommitsController{}
|
||||
type CanSwitchToSubCommits interface {
|
||||
types.Context
|
||||
GetSelectedRef() types.Ref
|
||||
ShowBranchHeadsInSubCommits() bool
|
||||
}
|
||||
|
||||
type SwitchToSubCommitsController struct {
|
||||
@@ -80,7 +79,6 @@ func (self *SwitchToSubCommitsController) viewCommits() error {
|
||||
subCommitsContext.SetTitleRef(ref.Description())
|
||||
subCommitsContext.SetRef(ref)
|
||||
subCommitsContext.SetLimitCommits(true)
|
||||
subCommitsContext.SetShowBranchHeads(self.context.ShowBranchHeadsInSubCommits())
|
||||
subCommitsContext.ClearSearchString()
|
||||
subCommitsContext.GetView().ClearSearch()
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type SyncController struct {
|
||||
@@ -187,7 +186,7 @@ func (self *SyncController) pushAux(opts pushOpts) error {
|
||||
}
|
||||
_ = self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.ForcePush,
|
||||
Prompt: self.forcePushPrompt(),
|
||||
Prompt: self.c.Tr.ForcePushPrompt,
|
||||
HandleConfirm: func() error {
|
||||
newOpts := opts
|
||||
newOpts.force = true
|
||||
@@ -211,20 +210,10 @@ func (self *SyncController) requestToForcePush(opts pushOpts) error {
|
||||
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.ForcePush,
|
||||
Prompt: self.forcePushPrompt(),
|
||||
Prompt: self.c.Tr.ForcePushPrompt,
|
||||
HandleConfirm: func() error {
|
||||
opts.force = true
|
||||
return self.pushAux(opts)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SyncController) forcePushPrompt() string {
|
||||
return utils.ResolvePlaceholderString(
|
||||
self.c.Tr.ForcePushPrompt,
|
||||
map[string]string{
|
||||
"cancelKey": self.c.UserConfig.Keybinding.Universal.Return,
|
||||
"confirmKey": self.c.UserConfig.Keybinding.Universal.Confirm,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,10 +23,7 @@ func (self *ToggleWhitespaceAction) Call() error {
|
||||
return self.c.ErrorMsg(self.c.Tr.IgnoreWhitespaceNotSupportedHere)
|
||||
}
|
||||
|
||||
self.c.GetAppState().IgnoreWhitespaceInDiffView = !self.c.GetAppState().IgnoreWhitespaceInDiffView
|
||||
if err := self.c.SaveAppState(); err != nil {
|
||||
self.c.Log.Errorf("error when saving app state: %v", err)
|
||||
}
|
||||
self.c.State().SetIgnoreWhitespaceInDiffView(!self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
|
||||
return self.c.CurrentSideContext().HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
@@ -34,10 +29,6 @@ func (self *FilesController) createResetMenu() error {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
if self.c.UserConfig.Gui.AnimateExplosion {
|
||||
self.animateExplosion()
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
Key: 'x',
|
||||
@@ -144,102 +135,3 @@ func (self *FilesController) createResetMenu() error {
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{Title: "", Items: menuItems})
|
||||
}
|
||||
|
||||
func (self *FilesController) animateExplosion() {
|
||||
self.Explode(self.c.Views().Files, func() {
|
||||
err := self.c.PostRefreshUpdate(self.c.Contexts().Files)
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Animates an explosion within the view by drawing a bunch of flamey characters
|
||||
func (self *FilesController) Explode(v *gocui.View, onDone func()) {
|
||||
width := v.InnerWidth()
|
||||
height := v.InnerHeight() + 1
|
||||
styles := []style.TextStyle{
|
||||
style.FgLightWhite.SetBold(),
|
||||
style.FgYellow.SetBold(),
|
||||
style.FgRed.SetBold(),
|
||||
style.FgBlue.SetBold(),
|
||||
style.FgBlack.SetBold(),
|
||||
}
|
||||
|
||||
self.c.OnWorker(func(_ gocui.Task) {
|
||||
max := 25
|
||||
for i := 0; i < max; i++ {
|
||||
image := getExplodeImage(width, height, i, max)
|
||||
style := styles[(i*len(styles)/max)%len(styles)]
|
||||
coloredImage := style.Sprint(image)
|
||||
self.c.OnUIThread(func() error {
|
||||
v.SetContent(coloredImage)
|
||||
return nil
|
||||
})
|
||||
time.Sleep(time.Millisecond * 20)
|
||||
}
|
||||
self.c.OnUIThread(func() error {
|
||||
v.Clear()
|
||||
onDone()
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Render an explosion in the given bounds.
|
||||
func getExplodeImage(width int, height int, frame int, max int) string {
|
||||
// Predefine the explosion symbols
|
||||
explosionChars := []rune{'*', '.', '@', '#', '&', '+', '%'}
|
||||
|
||||
// Initialize a buffer to build our string
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Initialize RNG seed
|
||||
random := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// calculate the center of explosion
|
||||
centerX, centerY := width/2, height/2
|
||||
|
||||
// calculate the max radius (hypotenuse of the view)
|
||||
maxRadius := math.Hypot(float64(centerX), float64(centerY))
|
||||
|
||||
// calculate frame as a proportion of max, apply square root to create the non-linear effect
|
||||
progress := math.Sqrt(float64(frame) / float64(max))
|
||||
|
||||
// calculate radius of explosion according to frame and max
|
||||
radius := progress * maxRadius * 2
|
||||
|
||||
// introduce a new radius for the inner boundary of the explosion (the shockwave effect)
|
||||
var innerRadius float64
|
||||
if progress > 0.5 {
|
||||
innerRadius = (progress - 0.5) * 2 * maxRadius
|
||||
}
|
||||
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
// calculate distance from center, scale x by 2 to compensate for character aspect ratio
|
||||
distance := math.Hypot(float64(x-centerX), float64(y-centerY)*2)
|
||||
|
||||
// if distance is less than radius and greater than innerRadius, draw explosion char
|
||||
if distance <= radius && distance >= innerRadius {
|
||||
// Make placement random and less likely as explosion progresses
|
||||
if random.Float64() > progress {
|
||||
// Pick a random explosion char
|
||||
char := explosionChars[random.Intn(len(explosionChars))]
|
||||
buf.WriteRune(char)
|
||||
} else {
|
||||
buf.WriteRune(' ')
|
||||
}
|
||||
} else {
|
||||
// If not explosion, then it's empty space
|
||||
buf.WriteRune(' ')
|
||||
}
|
||||
}
|
||||
// End of line
|
||||
if y < height-1 {
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -24,9 +24,7 @@ func (gui *Gui) handleCreateExtrasMenuPanel() error {
|
||||
show := !gui.c.State().GetShowExtrasWindow()
|
||||
gui.c.State().SetShowExtrasWindow(show)
|
||||
gui.c.GetAppState().HideCommandLog = !show
|
||||
if err := gui.c.SaveAppState(); err != nil {
|
||||
gui.c.Log.Errorf("error when saving app state: %v", err)
|
||||
}
|
||||
_ = gui.c.SaveAppState()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
137
pkg/gui/file_watching.go
Normal file
137
pkg/gui/file_watching.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// macs for some bizarre reason cap the number of watchable files to 256.
|
||||
// there's no obvious platform agnostic way to check the situation of the user's
|
||||
// computer so we're just arbitrarily capping at 200. This isn't so bad because
|
||||
// file watching is only really an added bonus for faster refreshing.
|
||||
const MAX_WATCHED_FILES = 50
|
||||
|
||||
var _ types.IFileWatcher = new(fileWatcher)
|
||||
|
||||
type fileWatcher struct {
|
||||
Watcher *fsnotify.Watcher
|
||||
WatchedFilenames []string
|
||||
Log *logrus.Entry
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
func NewFileWatcher(log *logrus.Entry) *fileWatcher {
|
||||
// TODO: get this going again, and ensure we don't see any crashes from it
|
||||
return &fileWatcher{
|
||||
Disabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *fileWatcher) watchingFilename(filename string) bool {
|
||||
for _, watchedFilename := range w.WatchedFilenames {
|
||||
if watchedFilename == filename {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *fileWatcher) popOldestFilename() {
|
||||
// shift the last off the array to make way for this one
|
||||
oldestFilename := w.WatchedFilenames[0]
|
||||
w.WatchedFilenames = w.WatchedFilenames[1:]
|
||||
if err := w.Watcher.Remove(oldestFilename); err != nil {
|
||||
// swallowing errors here because it doesn't really matter if we can't unwatch a file
|
||||
w.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *fileWatcher) watchFilename(filename string) {
|
||||
if err := w.Watcher.Add(filename); err != nil {
|
||||
// swallowing errors here because it doesn't really matter if we can't watch a file
|
||||
w.Log.Error(err)
|
||||
}
|
||||
|
||||
// assume we're watching it now to be safe
|
||||
w.WatchedFilenames = append(w.WatchedFilenames, filename)
|
||||
}
|
||||
|
||||
func (w *fileWatcher) AddFilesToFileWatcher(files []*models.File) error {
|
||||
if w.Disabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// watch the files for changes
|
||||
dirName, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range files[0:min(MAX_WATCHED_FILES, len(files))] {
|
||||
if file.Deleted {
|
||||
continue
|
||||
}
|
||||
filename := filepath.Join(dirName, file.Name)
|
||||
if w.watchingFilename(filename) {
|
||||
continue
|
||||
}
|
||||
if len(w.WatchedFilenames) > MAX_WATCHED_FILES {
|
||||
w.popOldestFilename()
|
||||
}
|
||||
|
||||
w.watchFilename(filename)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func min(a int, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// NOTE: given that we often edit files ourselves, this may make us end up refreshing files too often
|
||||
// TODO: consider watching the whole directory recursively (could be more expensive)
|
||||
func (gui *Gui) WatchFilesForChanges() {
|
||||
gui.fileWatcher = NewFileWatcher(gui.Log)
|
||||
if gui.fileWatcher.Disabled {
|
||||
return
|
||||
}
|
||||
go utils.Safe(func() {
|
||||
for {
|
||||
select {
|
||||
// watch for events
|
||||
case event := <-gui.fileWatcher.Watcher.Events:
|
||||
if event.Op == fsnotify.Chmod {
|
||||
// for some reason we pick up chmod events when they don't actually happen
|
||||
continue
|
||||
}
|
||||
// only refresh if we're not already
|
||||
if !gui.IsRefreshingFiles {
|
||||
gui.c.OnUIThread(func() error {
|
||||
// TODO: find out if refresh needs to be run on the UI thread
|
||||
return gui.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
})
|
||||
}
|
||||
|
||||
// watch for errors
|
||||
case err := <-gui.fileWatcher.Watcher.Errors:
|
||||
if err != nil {
|
||||
gui.c.Log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package filetree
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -60,7 +60,7 @@ func (self *CommitFileTree) GetAllItems() []*CommitFileNode {
|
||||
}
|
||||
|
||||
// ignoring root
|
||||
return lo.Map(self.tree.Flatten(self.collapsedPaths)[1:], func(node *Node[models.CommitFile], _ int) *CommitFileNode {
|
||||
return slices.Map(self.tree.Flatten(self.collapsedPaths)[1:], func(node *Node[models.CommitFile]) *CommitFileNode {
|
||||
return NewCommitFileNode(node)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package filetree
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -88,7 +88,7 @@ func (self *FileTree) getFilesForDisplay() []*models.File {
|
||||
}
|
||||
|
||||
func (self *FileTree) FilterFiles(test func(*models.File) bool) []*models.File {
|
||||
return lo.Filter(self.getFiles(), func(file *models.File, _ int) bool { return test(file) })
|
||||
return slices.Filter(self.getFiles(), test)
|
||||
}
|
||||
|
||||
func (self *FileTree) SetStatusFilter(filter FileTreeDisplayFilter) {
|
||||
@@ -130,7 +130,7 @@ func (self *FileTree) GetAllItems() []*FileNode {
|
||||
}
|
||||
|
||||
// ignoring root
|
||||
return lo.Map(self.tree.Flatten(self.collapsedPaths)[1:], func(node *Node[models.File], _ int) *FileNode {
|
||||
return slices.Map(self.tree.Flatten(self.collapsedPaths)[1:], func(node *Node[models.File]) *FileNode {
|
||||
return NewFileNode(node)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package filetree
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Represents a file or directory in a file tree.
|
||||
@@ -153,7 +152,7 @@ func (self *Node[T]) Flatten(collapsedPaths *CollapsedPaths) []*Node[T] {
|
||||
result := []*Node[T]{self}
|
||||
|
||||
if len(self.Children) > 0 && !collapsedPaths.IsCollapsed(self.GetPath()) {
|
||||
result = append(result, lo.FlatMap(self.Children, func(child *Node[T], _ int) []*Node[T] {
|
||||
result = append(result, slices.FlatMap(self.Children, func(child *Node[T]) []*Node[T] {
|
||||
return child.Flatten(collapsedPaths)
|
||||
})...)
|
||||
}
|
||||
@@ -274,11 +273,11 @@ func (self *Node[T]) GetPathsMatching(test func(*Node[T]) bool) []string {
|
||||
}
|
||||
|
||||
func (self *Node[T]) GetFilePathsMatching(test func(*T) bool) []string {
|
||||
matchingFileNodes := lo.Filter(self.GetLeaves(), func(node *Node[T], _ int) bool {
|
||||
matchingFileNodes := slices.Filter(self.GetLeaves(), func(node *Node[T]) bool {
|
||||
return test(node.File)
|
||||
})
|
||||
|
||||
return lo.Map(matchingFileNodes, func(node *Node[T], _ int) string {
|
||||
return slices.Map(matchingFileNodes, func(node *Node[T]) string {
|
||||
return node.GetPath()
|
||||
})
|
||||
}
|
||||
@@ -288,7 +287,7 @@ func (self *Node[T]) GetLeaves() []*Node[T] {
|
||||
return []*Node[T]{self}
|
||||
}
|
||||
|
||||
return lo.FlatMap(self.Children, func(child *Node[T], _ int) []*Node[T] {
|
||||
return slices.FlatMap(self.Children, func(child *Node[T]) []*Node[T] {
|
||||
return child.GetLeaves()
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user