Compare commits

..
1 Commits
Author SHA1 Message Date
Jesse Duffield 91de559f15 add half and fullscreen modes 2020-02-25 08:41:53 +11:00
76 changed files with 1505 additions and 2167 deletions
+62
View File
@@ -0,0 +1,62 @@
version: 2
jobs:
build:
docker:
- image: circleci/golang:1.13
environment:
GO111MODULE: "on"
working_directory: /go/src/github.com/jesseduffield/lazygit
steps:
- checkout
- run:
name: Run gofmt -s
command: |
if [ $(find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;|wc -l) -gt 0 ]; then
find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;
exit 1;
fi
- restore_cache:
keys:
- pkg-cache-{{ checksum "go.sum" }}-v5
- run:
name: Run tests
command: |
./test.sh
- run:
name: Push on codecov result
command: |
bash <(curl -s https://codecov.io/bash)
- run:
name: Compile project on every platform
command: |
go get github.com/mitchellh/gox
GOFLAGS=-mod=vendor gox -parallel 10 -os "linux freebsd netbsd windows" -osarch "darwin/i386 darwin/amd64"
- save_cache:
key: pkg-cache-{{ checksum "go.sum" }}-v5
paths:
- ~/.cache/go-build
release:
docker:
- image: circleci/golang:1.13
working_directory: /go/src/github.com/jesseduffield/lazygit
steps:
- checkout
- run:
name: Run gorelease
command: |
curl -sL https://git.io/goreleaser | bash
workflows:
version: 2
build:
jobs:
- build
release:
jobs:
- release:
filters:
tags:
only: /v[0-9]+(\.[0-9]+)*/
branches:
ignore: /.*/
-34
View File
@@ -1,34 +0,0 @@
name: Continuous Delivery
on:
push:
tags:
- 'v*'
jobs:
cd:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Unshallow repo
run: git fetch --prune --unshallow
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.14.x
- name: Run goreleaser
uses: goreleaser/goreleaser-action@v1
with:
args: release --rm-dist
env:
GITHUB_TOKEN: ${{secrets.GITHUB_API_TOKEN}}
- name: Get tag
id: tag
uses: dawidd6/action-get-tag@v1
- name: Bump Homebrew
uses: dawidd6/action-homebrew-bump-formula@v1
with:
token: ${{secrets.GITHUB_API_TOKEN}}
formula: lazygit
url: "https://github.com/${{github.repository}}/archive/${{steps.tag.outputs.tag}}.tar.gz"
-37
View File
@@ -1,37 +0,0 @@
name: Continuous Integration
on: push
jobs:
ci:
runs-on: ubuntu-latest
env:
GOFLAGS: -mod=vendor
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.14.x
- name: Cache build
uses: actions/cache@v1
with:
path: ~/.cache/go-build
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}
restore-keys: |
${{runner.os}}-go-
- name: Format code
run: |
if [ $(find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;|wc -l) -gt 0 ]; then
find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;
exit 1
fi
- name: Test code
run: |
./test.sh
- name: Build binaries
run: |
go get github.com/mitchellh/gox
export PATH="$(go env GOPATH)/bin:$PATH"
gox -parallel 4 -os "linux freebsd netbsd windows" -osarch "darwin/i386 darwin/amd64"
-3
View File
@@ -13,9 +13,6 @@ builds:
- arm
- arm64
- 386
ignore:
- goos: freebsd
goarch: arm64
# Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}`.
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.buildSource=binaryRelease
+2 -2
View File
@@ -2,12 +2,12 @@
# docker build -t lazygit .
# docker run -it lazygit:latest /bin/sh -l
FROM golang:1.14-alpine3.11
FROM golang:1.13-alpine3.10
WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY ./ .
RUN CGO_ENABLED=0 GOOS=linux go build
FROM alpine:3.11
FROM alpine:3.10
RUN apk add -U git xdg-utils
WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY --from=0 /go/src/github.com/jesseduffield/lazygit /go/src/github.com/jesseduffield/lazygit
+7 -32
View File
@@ -1,8 +1,8 @@
# lazygit ![CI](https://github.com/jesseduffield/lazygit/workflows/Continuous%20Integration/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/jesseduffield/lazygit)](https://goreportcard.com/report/github.com/jesseduffield/lazygit) [![GolangCI](https://golangci.com/badges/github.com/jesseduffield/lazygit.svg)](https://golangci.com) [![GoDoc](https://godoc.org/github.com/jesseduffield/lazygit?status.svg)](http://godoc.org/github.com/jesseduffield/lazygit) [![GitHub tag](https://img.shields.io/github/tag/jesseduffield/lazygit.svg)]() [![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/jesseduffield/lazygit)](https://www.tickgit.com/browse?repo=github.com/jesseduffield/lazygit)
# lazygit [![CircleCI](https://circleci.com/gh/jesseduffield/lazygit.svg?style=svg)](https://circleci.com/gh/jesseduffield/lazygit) [![codecov](https://codecov.io/gh/jesseduffield/lazygit/branch/master/graph/badge.svg)](https://codecov.io/gh/jesseduffield/lazygit) [![Go Report Card](https://goreportcard.com/badge/github.com/jesseduffield/lazygit)](https://goreportcard.com/report/github.com/jesseduffield/lazygit) [![GolangCI](https://golangci.com/badges/github.com/jesseduffield/lazygit.svg)](https://golangci.com) [![GoDoc](https://godoc.org/github.com/jesseduffield/lazygit?status.svg)](http://godoc.org/github.com/jesseduffield/lazygit) [![GitHub tag](https://img.shields.io/github/tag/jesseduffield/lazygit.svg)]()
A simple terminal UI for git commands, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
Rant time: You've heard it before, git is _powerful_, but what good is that power when everything is so damn hard to do? Interactive rebasing requires you to edit a goddamn TODO file in your editor? *Are you kidding me?* To stage part of a file you need to use a command line program stepping through each hunk and if a hunk can't be split down any further but contains code you don't want to stage, bad luck? *Are you KIDDING me?!* Sometimes you get asked to stash your changes when switching branches only to realise that after you switch and unstash that there weren't even any conflicts and it would have been fine to just checkout the branch directly? *YOU HAVE GOT TO BE KIDDING ME!*
Rant time: You've heard it before, git is _powerful_, but what good is that power when everything is so damn hard to do? Interactive rebasing requires you to edit a goddamn TODO file in your editor? *Are you kidding me?* To stage part of a file you need to use a command line program stepping through each hunk and if a hunk can't be split down any further but contains code you don't want to stage, bad luck? *Are you KIDDING me?!* Sometimes you get asked to stash your changes when switching branches only to realise that after you switch and unstash that there weren't even any conflicts and it would have been fine to just checkout the branch directly? *YOU HAVE GOT TO BE KIDDING ME!*
If you're a mere mortal like me and you're tired of hearing how powerful git is when in your daily life it's a powerful pain in your ass, lazygit might be for you.
@@ -24,11 +24,9 @@ Github Sponsors is matching all donations dollar-for-dollar for 12 months so if
## Installation
### Homebrew
Normally the lazygit formula can be found in the Homebrew core but we suggest you tap our formula to get the frequently updated one. It works with Linux, too.
Tap:
```
brew install jesseduffield/lazygit/lazygit
```
@@ -40,10 +38,8 @@ brew install lazygit
```
### MacPorts
Latest version built from github releases.
Tap:
```
sudo port install lazygit
```
@@ -67,17 +63,6 @@ They follow upstream latest releases
```sh
sudo xbps-install -S lazygit
```
### Scoop (Windows)
You can install `lazygit` using [scoop](https://scoop.sh/). It's in the `extras` bucket:
```sh
# Add the extras bucket
scoop bucket add extras
# Install lazygit
scoop install lazygit
```
### Arch Linux
@@ -86,11 +71,11 @@ Packages for Arch Linux are available via AUR (Arch User Repository).
There are two packages. The stable one which is built with the latest release
and the git version which builds from the most recent commit.
- Stable: <https://aur.archlinux.org/packages/lazygit/>
- Development: <https://aur.archlinux.org/packages/lazygit-git/>
- Stable: https://aur.archlinux.org/packages/lazygit/
- Development: https://aur.archlinux.org/packages/lazygit-git/
Instruction of how to install AUR content can be found here:
<https://wiki.archlinux.org/index.php/Arch_User_Repository>
https://wiki.archlinux.org/index.php/Arch_User_Repository
### Fedora and CentOS 7
@@ -103,7 +88,7 @@ sudo dnf install lazygit
### Conda
Released versions are available for different platforms, see <https://anaconda.org/conda-forge/lazygit>
Released versions are available for different platforms, see https://anaconda.org/conda-forge/lazygit
```sh
conda install -c conda-forge lazygit
@@ -135,11 +120,10 @@ whichever rc file you're using).
- Rebase Magic tutorial [here](https://youtu.be/4XaToVut_hs)
- List of keybindings
[here](/docs/keybindings).
## Changing Directory On Exit
If you change repos in lazygit and want your shell to change directory into that repo on exiting lazygit, add this to your `~/.zshrc` (or other rc file):
```
lg()
{
@@ -153,17 +137,8 @@ lg()
fi
}
```
Then `source ~/.zshrc` and from now on when you call `lg` and exit you'll switch directories to whatever you were in inside lazyigt. To override this behaviour you can exit using `shift+Q` rather than just `q`.
## Configuration
Check the [configuration docs](docs/Config.md).
## Custom Pagers
See the [docs](docs/Custom_Pagers.md)
## Cool features
- Adding files easily
+13 -39
View File
@@ -1,25 +1,24 @@
# User Config
# User Config:
Default path for the config file:
* Linux: `~/.config/jesseduffield/lazygit/config.yml`
* MacOS: `~/Library/Application Support/jesseduffield/lazygit/config.yml`
## Default
## Default:
```yaml
gui:
# stuff relating to the UI
scrollHeight: 2 # how many lines you scroll by
scrollPastBottom: true # enable scrolling past the bottom
sidePanelWidth: 0.3333 # number from 0 to 1
theme:
lightTheme: false # For terminals with a light background
activeBorderColor:
- white
- bold
inactiveBorderColor:
- green
- white
optionsTextColor:
- blue
selectedLineBgColor:
@@ -29,9 +28,6 @@ Default path for the config file:
mouseEvents: true
skipUnstageLineWarning: false
git:
paging:
colorArg: always
useConfig: false
merging:
# only applicable to unix users
manualCommit: false
@@ -57,8 +53,6 @@ Default path for the config file:
nextBlock: '<right>' # goto the next block / panel
prevBlock-alt: 'h' # goto the previous block / panel
nextBlock-alt: 'l' # goto the next block / panel
nextMatch: 'n'
prevMatch: 'N'
optionMenu: 'x' # show help menu
optionMenu-alt1: '?' # show help menu
select: '<space>'
@@ -128,7 +122,6 @@ Default path for the config file:
tagCommit: 'T'
toggleDiffCommit: 'i'
checkoutCommit: '<space>'
resetCherryPick: '<c-R>'
stash:
popStash: 'g'
commitFiles:
@@ -141,30 +134,30 @@ Default path for the config file:
undo: 'z'
```
## Platform Defaults
## Platform Defaults:
### Windows
### Windows:
```yaml
os:
openCommand: 'cmd /c "start "" {{filename}}"'
```
### Linux
### Linux:
```yaml
os:
openCommand: 'sh -c "xdg-open {{filename}} >/dev/null"'
```
### OSX
### OSX:
```yaml
os:
openCommand: 'open {{filename}}'
```
### Recommended Config Values
### Recommended Config Values:
for users of VSCode
@@ -173,7 +166,7 @@ for users of VSCode
openCommand: 'code -r {{filename}}'
```
## Color Attributes
## Color Attributes:
For color attributes you can choose an array of attributes (with max one color attribute)
The available attributes are:
@@ -191,7 +184,7 @@ The available attributes are:
- reverse # useful for high-contrast
- underline
## Light terminal theme
## Light terminal theme:
If you have issues with a light terminal theme where you can't read / see the text add these settings
@@ -208,16 +201,15 @@ If you have issues with a light terminal theme where you can't read / see the te
- blue
```
## Example Coloring
## Example Coloring:
![border example](/docs/resources/colored-border-example.png)
## Keybindings
## Keybindings:
For all possible keybinding options, check [Custom_Keybinding.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybinding.md)
### Example Keybindings For Colemak Users
#### Example Keybindings For Colemak Users:
```yaml
keybinding:
universal:
@@ -225,8 +217,6 @@ For all possible keybinding options, check [Custom_Keybinding.md](https://github
nextItem-alt: 'e'
prevBlock-alt: 'n'
nextBlock-alt: 'i'
nextMatch: '='
prevMatch: '-'
new: 'k'
edit: 'o'
openFile: 'O'
@@ -244,19 +234,3 @@ For all possible keybinding options, check [Custom_Keybinding.md](https://github
viewGitFlowOptions: 'I'
```
## Custom pull request URLs
Some git provider setups (e.g. on-premises GitLab) can have distinct URLs for git-related calls and
the web interface/API itself. To work with those, Lazygit needs to know where it needs to create
the pull request. You can do so on your `config.yml` file using the following syntax:
```yaml
services:
"<gitDomain>": "<provider>:<webDomain>"
```
Where:
- `gitDomain` stands for the domain used by git itself (i.e. the one present on clone URLs), e.g. `git.work.com`
- `provider` is one of `github`, `bitbucket` or `gitlab`
- `webDomain` is the URL where your git service exposes a web interface and APIs, e.g. `gitservice.work.com`
-64
View File
@@ -1,64 +0,0 @@
# Custom Pagers
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing 'o' in the Status panel).
Support does not extend to windows users, because we're making use of a package which doesn't have windows support.
## Default:
```yaml
git:
paging:
colorArg: always
useConfig: false
```
the `colorArg` key is for whether you want the `--color=always` arg in your `git diff` command. Some pagers want it set to always, others want it set to 'never'.
## Delta:
```yaml
git:
paging:
colorArg: always
pager: delta --dark --paging=never --24-bit-color=never
```
![](https://i.imgur.com/QJpQkF3.png)
## Diff-so-fancy
```yaml
git:
paging:
colorArg: always
pager: diff-so-fancy
```
![](https://i.imgur.com/rjH1TpT.png)
## ydiff
```yaml
gui:
sidePanelWidth: 0.2 # gives you more space to show things side-by-side
git:
paging:
colorArg: never
pager: ydiff -p cat -s --wrap --width={{columnWidth}}
```
![](https://i.imgur.com/vaa8z0H.png)
Be careful with this one, I think the homebrew and pip versions are behind master. I needed to directly download the ydiff script to get the no-pager functionality working.
## Using git config
```yaml
git:
paging:
colorArg: always
useConfig: true
```
If you set `useConfig: true`, lazygit will use whatever pager is specified in $GIT_PAGER, $PAGER, or your git config. If the pager ends with something like ' | less' we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
+61 -166
View File
@@ -1,29 +1,47 @@
# Lazygit Keybindings
# Lazygit menu
## Global Keybindings
## Global
<pre>
<kbd>pgup</kbd>: scroll up main panel (fn+up)
<kbd>pgdown</kbd>: scroll down main panel (fn+down)
<kbd>m</kbd>: view merge/rebase options
<kbd>ctrl+p</kbd>: view custom patch options
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>R</kbd>: refresh
<kbd>x</kbd>: open menu
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: prev screen mode
<kbd>:</kbd>: execute custom command
</pre>
## Branches Panel
## Status
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>e</kbd>: edit config file
<kbd>o</kbd>: open config file
<kbd>u</kbd>: check for update
<kbd>s</kbd>: switch to a recent repo
</pre>
## Branches Panel (Branches Tab)
## Files
<pre>
<kbd>c</kbd>: commit changes
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>A</kbd>: amend last commit
<kbd>C</kbd>: commit changes using git editor
<kbd>space</kbd>: toggle staged
<kbd>d</kbd>: view 'discard changes' options
<kbd>e</kbd>: edit file
<kbd>o</kbd>: open file
<kbd>i</kbd>: add to .gitignore
<kbd>r</kbd>: refresh files
<kbd>S</kbd>: stash files
<kbd>a</kbd>: stage/unstage all
<kbd>t</kbd>: add patch
<kbd>D</kbd>: view reset options
<kbd>enter</kbd>: stage individual hunks/lines
<kbd>f</kbd>: fetch
<kbd>X</kbd>: execute custom command
</pre>
## Branches
<pre>
<kbd>space</kbd>: checkout
@@ -32,70 +50,12 @@
<kbd>F</kbd>: force checkout
<kbd>n</kbd>: new branch
<kbd>d</kbd>: delete branch
<kbd>r</kbd>: rebase checked-out branch onto this branch
<kbd>r</kbd>: rebase branch
<kbd>M</kbd>: merge into currently checked out branch
<kbd>i</kbd>: show git-flow options
<kbd>f</kbd>: fast-forward this branch from its upstream
<kbd>g</kbd>: view reset options
<kbd>R</kbd>: rename branch
<kbd>/</kbd>: start search
</pre>
## Branches Panel (Remote Branches (in Remotes tab))
<pre>
<kbd>esc</kbd>: return to remotes list
<kbd>g</kbd>: view reset options
<kbd>space</kbd>: checkout
<kbd>M</kbd>: merge into currently checked out branch
<kbd>d</kbd>: delete branch
<kbd>r</kbd>: rebase checked-out branch onto this branch
<kbd>u</kbd>: set as upstream of checked-out branch
<kbd>/</kbd>: start search
</pre>
## Branches Panel (Remotes Tab)
<pre>
<kbd>f</kbd>: fetch remote
<kbd>n</kbd>: add new remote
<kbd>d</kbd>: remove remote
<kbd>e</kbd>: edit remote
<kbd>/</kbd>: start search
</pre>
## Branches Panel (Tags Tab)
<pre>
<kbd>space</kbd>: checkout
<kbd>d</kbd>: delete tag
<kbd>P</kbd>: push tag
<kbd>n</kbd>: create tag
<kbd>g</kbd>: view reset options
<kbd>/</kbd>: start search
</pre>
## Commit Files Panel
<pre>
<kbd>esc</kbd>: go back
<kbd>c</kbd>: checkout file
<kbd>d</kbd>: discard this commit's changes to this file
<kbd>o</kbd>: open file
<kbd>space</kbd>: toggle file included in patch
<kbd>enter</kbd>: enter file to add selected lines to the patch
<kbd>/</kbd>: start search
</pre>
## Commits Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>/</kbd>: start search
</pre>
## Commits Panel (Commits Tab)
## Commits
<pre>
<kbd>s</kbd>: squash down
@@ -116,43 +76,46 @@
<kbd>C</kbd>: copy commit range (cherry-pick)
<kbd>v</kbd>: paste commits (cherry-pick)
<kbd>enter</kbd>: view commit's files
<kbd>space</kbd>: checkout commit
<kbd>i</kbd>: select commit to diff with another commit
<kbd>T</kbd>: tag commit
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
<kbd>space</kbd>: select commit to diff with another commit
</pre>
## Commits Panel (Reflog Tab)
## Stash
<pre>
<kbd>space</kbd>: checkout commit
<kbd>g</kbd>: view reset options
<kbd>space</kbd>: apply
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
</pre>
## Files Panel
## Commit files
<pre>
<kbd>c</kbd>: commit changes
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>A</kbd>: amend last commit
<kbd>C</kbd>: commit changes using git editor
<kbd>space</kbd>: toggle staged
<kbd>d</kbd>: view 'discard changes' options
<kbd>e</kbd>: edit file
<kbd>esc</kbd>: go back
<kbd>c</kbd>: checkout file
<kbd>d</kbd>: discard this commit's changes to this file
<kbd>o</kbd>: open file
<kbd>i</kbd>: add to .gitignore
<kbd>r</kbd>: refresh files
<kbd>s</kbd>: stash changes
<kbd>S</kbd>: view stash options
<kbd>a</kbd>: stage/unstage all
<kbd>D</kbd>: view reset options
<kbd>enter</kbd>: stage individual hunks/lines
<kbd>f</kbd>: fetch
<kbd>g</kbd>: view upstream reset options
<kbd>/</kbd>: start search
</pre>
## Main Panel (Merging)
## Main (Normal)
<pre>
<kbd>PgDn</kbd>: scroll down (fn+up)
<kbd>PgUp</kbd>: scroll up (fn+down)
</pre>
## Main (Staging)
<pre>
<kbd>esc</kbd>: return to files panel
<kbd>▲</kbd>: select previous line
<kbd>▼</kbd>: select next line
<kbd>◄</kbd>: select previous hunk
<kbd>►</kbd>: select next hunk
<kbd>space</kbd>: stage line
<kbd>a</kbd>: stage hunk
</pre>
## Main (Merging)
<pre>
<kbd>esc</kbd>: return to files panel
@@ -164,71 +127,3 @@
<kbd>▼</kbd>: select bottom hunk
<kbd>z</kbd>: undo
</pre>
## Main Panel (Normal)
<pre>
<kbd> ̄</kbd>: scroll down (fn+up)
<kbd>¦</kbd>: scroll up (fn+down)
</pre>
## Main Panel (Patch Building)
<pre>
<kbd>esc</kbd>: exit line-by-line mode
<kbd>▲</kbd>: select previous line
<kbd>▼</kbd>: select next line
<kbd>◄</kbd>: select previous hunk
<kbd>►</kbd>: select next hunk
<kbd>space</kbd>: add/remove line(s) to patch
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
</pre>
## Main Panel (Staging)
<pre>
<kbd>esc</kbd>: return to files panel
<kbd>space</kbd>: toggle line staged / unstaged
<kbd>d</kbd>: delete change (git reset)
<kbd>tab</kbd>: switch to other panel
<kbd>▲</kbd>: select previous line
<kbd>▼</kbd>: select next line
<kbd>◄</kbd>: select previous hunk
<kbd>►</kbd>: select next hunk
<kbd>e</kbd>: edit file
<kbd>o</kbd>: open file
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
<kbd>c</kbd>: commit changes
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>C</kbd>: commit changes using git editor
</pre>
## Menu Panel
<pre>
<kbd>esc</kbd>: close menu
<kbd>q</kbd>: close menu
<kbd>/</kbd>: start search
</pre>
## Stash Panel
<pre>
<kbd>space</kbd>: apply
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
<kbd>/</kbd>: start search
</pre>
## Status Panel
<pre>
<kbd>e</kbd>: edit config file
<kbd>o</kbd>: open config file
<kbd>u</kbd>: check for update
<kbd>enter</kbd>: switch to a recent repo
</pre>
+55 -161
View File
@@ -1,29 +1,46 @@
# Lazygit Keybindings
# Lazygit menu
## Global
<pre>
<kbd>pgup</kbd>: scroll up main panel (fn+up)
<kbd>pgdown</kbd>: scroll down main panel (fn+down)
<kbd>m</kbd>: bekijk merge/rebase opties
<kbd>ctrl+p</kbd>: view custom patch options
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>R</kbd>: verversen
<kbd>x</kbd>: open menu
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: prev screen mode
<kbd>:</kbd>: voor aangepast commando uit
</pre>
## Branches Panel
## Status
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>e</kbd>: verander config file
<kbd>o</kbd>: open config file
<kbd>u</kbd>: check voor updates
<kbd>s</kbd>: wissel naar een recente repo
</pre>
## Branches Panel (Branches Tab)
## Bestanden
<pre>
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
<kbd>A</kbd>: wijzig laatste commit
<kbd>C</kbd>: commit veranderingen met de git editor
<kbd>space</kbd>: toggle staged
<kbd>d</kbd>: bekijk 'veranderingen ongedaan maken' opties
<kbd>e</kbd>: verander bestand
<kbd>o</kbd>: open bestand
<kbd>i</kbd>: voeg toe aan .gitignore
<kbd>r</kbd>: refresh bestanden
<kbd>S</kbd>: stash-bestanden
<kbd>a</kbd>: toggle staged alle
<kbd>t</kbd>: bewerkingen toevoegen
<kbd>D</kbd>: bekijk reset opties
<kbd>enter</kbd>: stage individuele hunks/lijnen
<kbd>f</kbd>: fetch
<kbd>X</kbd>: voor aangepast commando uit
</pre>
## Branches
<pre>
<kbd>space</kbd>: uitchecken
@@ -34,68 +51,10 @@
<kbd>d</kbd>: verwijder branch
<kbd>r</kbd>: rebase branch
<kbd>M</kbd>: merge in met huidige checked out branch
<kbd>i</kbd>: show git-flow options
<kbd>f</kbd>: fast-forward this branch from its upstream
<kbd>g</kbd>: bekijk reset opties
<kbd>R</kbd>: rename branch
<kbd>/</kbd>: start search
</pre>
## Branches Panel (Remote Branches (in Remotes tab))
<pre>
<kbd>esc</kbd>: return to remotes list
<kbd>g</kbd>: bekijk reset opties
<kbd>space</kbd>: uitchecken
<kbd>M</kbd>: merge in met huidige checked out branch
<kbd>d</kbd>: verwijder branch
<kbd>r</kbd>: rebase branch
<kbd>u</kbd>: set as upstream of checked-out branch
<kbd>/</kbd>: start search
</pre>
## Branches Panel (Remotes Tab)
<pre>
<kbd>f</kbd>: fetch remote
<kbd>n</kbd>: add new remote
<kbd>d</kbd>: remove remote
<kbd>e</kbd>: edit remote
<kbd>/</kbd>: start search
</pre>
## Branches Panel (Tags Tab)
<pre>
<kbd>space</kbd>: uitchecken
<kbd>d</kbd>: delete tag
<kbd>P</kbd>: push tag
<kbd>n</kbd>: create tag
<kbd>g</kbd>: bekijk reset opties
<kbd>/</kbd>: start search
</pre>
## Commit bestanden Panel
<pre>
<kbd>esc</kbd>: ga terug
<kbd>c</kbd>: bestand uitchecken
<kbd>d</kbd>: uitsluit deze commit zijn veranderingen aan dit bestand
<kbd>o</kbd>: open bestand
<kbd>space</kbd>: toggle file included in patch
<kbd>enter</kbd>: enter file to add selected lines to the patch
<kbd>/</kbd>: start search
</pre>
## Commits Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>/</kbd>: start search
</pre>
## Commits Panel (Commits Tab)
## Commits
<pre>
<kbd>s</kbd>: squash beneden
@@ -116,43 +75,39 @@
<kbd>C</kbd>: kopiëer commit reeks (cherry-pick)
<kbd>v</kbd>: plak commits (cherry-pick)
<kbd>enter</kbd>: bekijk gecommite bestanden
<kbd>space</kbd>: checkout commit
<kbd>i</kbd>: select commit to diff with another commit
<kbd>T</kbd>: tag commit
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
<kbd>space</kbd>: select commit to diff with another commit
</pre>
## Commits Panel (Reflog Tab)
## Stash
<pre>
<kbd>space</kbd>: checkout commit
<kbd>g</kbd>: bekijk reset opties
<kbd>space</kbd>: toepassen
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
</pre>
## Bestanden Panel
## Commit bestanden
<pre>
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
<kbd>A</kbd>: wijzig laatste commit
<kbd>C</kbd>: commit veranderingen met de git editor
<kbd>space</kbd>: toggle staged
<kbd>d</kbd>: bekijk 'veranderingen ongedaan maken' opties
<kbd>e</kbd>: verander bestand
<kbd>esc</kbd>: ga terug
<kbd>c</kbd>: bestand uitchecken
<kbd>d</kbd>: uitsluit deze commit zijn veranderingen aan dit bestand
<kbd>o</kbd>: open bestand
<kbd>i</kbd>: voeg toe aan .gitignore
<kbd>r</kbd>: refresh bestanden
<kbd>s</kbd>: stash-bestanden
<kbd>S</kbd>: view stash options
<kbd>a</kbd>: toggle staged alle
<kbd>D</kbd>: bekijk reset opties
<kbd>enter</kbd>: stage individuele hunks/lijnen
<kbd>f</kbd>: fetch
<kbd>g</kbd>: view upstream reset options
<kbd>/</kbd>: start search
</pre>
## Hoofd Panel (Merging)
## Hoofd (Stage Lines/Hunks)
<pre>
<kbd>esc</kbd>: ga terug naar het bestanden paneel
<kbd>▲</kbd>: selecteer de vorige lijn
<kbd>▼</kbd>: selecteer de volgende lijn
<kbd>◄</kbd>: selecteer de vorige hunk
<kbd>►</kbd>: selecteer de volgende hunk
<kbd>space</kbd>: stage lijn
<kbd>a</kbd>: stage hunk
</pre>
## Hoofd (Merging)
<pre>
<kbd>esc</kbd>: ga terug naar het bestanden paneel
@@ -165,70 +120,9 @@
<kbd>z</kbd>: ongedaan maken
</pre>
## Hoofd Panel (Normaal)
## Hoofd (Normaal)
<pre>
<kbd> ̄</kbd>: scroll omlaag (fn+up)
<kbd>¦</kbd>: scroll omhoog (fn+down)
</pre>
## Hoofd Panel (Patch Building)
<pre>
<kbd>esc</kbd>: exit line-by-line mode
<kbd>▲</kbd>: selecteer de vorige lijn
<kbd>▼</kbd>: selecteer de volgende lijn
<kbd>◄</kbd>: selecteer de vorige hunk
<kbd>►</kbd>: selecteer de volgende hunk
<kbd>space</kbd>: add/remove line(s) to patch
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
</pre>
## Hoofd Panel (Stage Lines/Hunks)
<pre>
<kbd>esc</kbd>: ga terug naar het bestanden paneel
<kbd>space</kbd>: toggle line staged / unstaged
<kbd>d</kbd>: delete change (git reset)
<kbd>tab</kbd>: switch to other panel
<kbd>▲</kbd>: selecteer de vorige lijn
<kbd>▼</kbd>: selecteer de volgende lijn
<kbd>◄</kbd>: selecteer de vorige hunk
<kbd>►</kbd>: selecteer de volgende hunk
<kbd>e</kbd>: verander bestand
<kbd>o</kbd>: open bestand
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
<kbd>C</kbd>: commit veranderingen met de git editor
</pre>
## Menu Panel
<pre>
<kbd>esc</kbd>: close menu
<kbd>q</kbd>: close menu
<kbd>/</kbd>: start search
</pre>
## Stash Panel
<pre>
<kbd>space</kbd>: toepassen
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
<kbd>/</kbd>: start search
</pre>
## Status Panel
<pre>
<kbd>e</kbd>: verander config file
<kbd>o</kbd>: open config file
<kbd>u</kbd>: check voor updates
<kbd>enter</kbd>: wissel naar een recente repo
<kbd>PgDn</kbd>: scroll omlaag (fn+up)
<kbd>PgUp</kbd>: scroll omhoog (fn+down)
</pre>
+59 -165
View File
@@ -1,29 +1,46 @@
# Lazygit Keybindings
# Lazygit menu
## Globalne
<pre>
<kbd>pgup</kbd>: scroll up main panel (fn+up)
<kbd>pgdown</kbd>: scroll down main panel (fn+down)
<kbd>m</kbd>: view merge/rebase options
<kbd>ctrl+p</kbd>: view custom patch options
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>R</kbd>: odśwież
<kbd>x</kbd>: open menu
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: prev screen mode
<kbd>:</kbd>: execute custom command
</pre>
## Gałęzie Panel
## Status
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>e</kbd>: edytuj plik konfiguracyjny
<kbd>o</kbd>: otwórz plik konfiguracyjny
<kbd>u</kbd>: sprawdź aktualizacje
<kbd>s</kbd>: switch to a recent repo
</pre>
## Gałęzie Panel (Branches Tab)
## Pliki
<pre>
<kbd>c</kbd>: commituj zmiany
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>A</kbd>: zmień ostatnie zatwierdzenie
<kbd>C</kbd>: commituj zmiany używając edytora z gita
<kbd>space</kbd>: przełącz zatwierdzenie
<kbd>d</kbd>: view 'discard changes' options
<kbd>e</kbd>: edytuj plik
<kbd>o</kbd>: otwórz plik
<kbd>i</kbd>: dodaj do .gitignore
<kbd>r</kbd>: odśwież pliki
<kbd>S</kbd>: przechowaj pliki
<kbd>a</kbd>: przełącz wszystkie zatwierdzenia
<kbd>t</kbd>: dodaj łatkę
<kbd>D</kbd>: view reset options
<kbd>enter</kbd>: zatwierdź pojedyncze linie
<kbd>f</kbd>: fetch
<kbd>X</kbd>: execute custom command
</pre>
## Gałęzie
<pre>
<kbd>space</kbd>: przełącz
@@ -34,68 +51,10 @@
<kbd>d</kbd>: usuń gałąź
<kbd>r</kbd>: rebase branch
<kbd>M</kbd>: scal do obecnej gałęzi
<kbd>i</kbd>: show git-flow options
<kbd>f</kbd>: fast-forward this branch from its upstream
<kbd>g</kbd>: view reset options
<kbd>R</kbd>: rename branch
<kbd>/</kbd>: start search
</pre>
## Gałęzie Panel (Remote Branches (in Remotes tab))
<pre>
<kbd>esc</kbd>: return to remotes list
<kbd>g</kbd>: view reset options
<kbd>space</kbd>: przełącz
<kbd>M</kbd>: scal do obecnej gałęzi
<kbd>d</kbd>: usuń gałąź
<kbd>r</kbd>: rebase branch
<kbd>u</kbd>: set as upstream of checked-out branch
<kbd>/</kbd>: start search
</pre>
## Gałęzie Panel (Remotes Tab)
<pre>
<kbd>f</kbd>: fetch remote
<kbd>n</kbd>: add new remote
<kbd>d</kbd>: remove remote
<kbd>e</kbd>: edit remote
<kbd>/</kbd>: start search
</pre>
## Gałęzie Panel (Tags Tab)
<pre>
<kbd>space</kbd>: przełącz
<kbd>d</kbd>: delete tag
<kbd>P</kbd>: push tag
<kbd>n</kbd>: create tag
<kbd>g</kbd>: view reset options
<kbd>/</kbd>: start search
</pre>
## Commit files Panel
<pre>
<kbd>esc</kbd>: go back
<kbd>c</kbd>: checkout file
<kbd>d</kbd>: discard this commit's changes to this file
<kbd>o</kbd>: otwórz plik
<kbd>space</kbd>: toggle file included in patch
<kbd>enter</kbd>: enter file to add selected lines to the patch
<kbd>/</kbd>: start search
</pre>
## Commity Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>/</kbd>: start search
</pre>
## Commity Panel (Commits Tab)
## Commity
<pre>
<kbd>s</kbd>: ściśnij w dół
@@ -116,43 +75,46 @@
<kbd>C</kbd>: copy commit range (cherry-pick)
<kbd>v</kbd>: paste commits (cherry-pick)
<kbd>enter</kbd>: view commit's files
<kbd>space</kbd>: checkout commit
<kbd>i</kbd>: select commit to diff with another commit
<kbd>T</kbd>: tag commit
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
<kbd>space</kbd>: select commit to diff with another commit
</pre>
## Commity Panel (Reflog Tab)
## Schowek
<pre>
<kbd>space</kbd>: checkout commit
<kbd>g</kbd>: view reset options
<kbd>space</kbd>: zastosuj
<kbd>g</kbd>: wyciągnij
<kbd>d</kbd>: porzuć
</pre>
## Pliki Panel
## Commit files
<pre>
<kbd>c</kbd>: commituj zmiany
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>A</kbd>: zmień ostatnie zatwierdzenie
<kbd>C</kbd>: commituj zmiany używając edytora z gita
<kbd>space</kbd>: przełącz zatwierdzenie
<kbd>d</kbd>: view 'discard changes' options
<kbd>e</kbd>: edytuj plik
<kbd>esc</kbd>: go back
<kbd>c</kbd>: checkout file
<kbd>d</kbd>: discard this commit's changes to this file
<kbd>o</kbd>: otwórz plik
<kbd>i</kbd>: dodaj do .gitignore
<kbd>r</kbd>: odśwież pliki
<kbd>s</kbd>: przechowaj pliki
<kbd>S</kbd>: view stash options
<kbd>a</kbd>: przełącz wszystkie zatwierdzenia
<kbd>D</kbd>: view reset options
<kbd>enter</kbd>: zatwierdź pojedyncze linie
<kbd>f</kbd>: fetch
<kbd>g</kbd>: view upstream reset options
<kbd>/</kbd>: start search
</pre>
## Main Panel (Merging)
## Main (Normal)
<pre>
<kbd>PgDn</kbd>: scroll down (fn+up)
<kbd>PgUp</kbd>: scroll up (fn+down)
</pre>
## Main (Zatwierdzanie)
<pre>
<kbd>esc</kbd>: wróć do panelu plików
<kbd>▲</kbd>: select previous line
<kbd>▼</kbd>: select next line
<kbd>◄</kbd>: select previous hunk
<kbd>►</kbd>: select next hunk
<kbd>space</kbd>: zatwierdź linię
<kbd>a</kbd>: zatwierdź kawałek
</pre>
## Main (Merging)
<pre>
<kbd>esc</kbd>: wróć do panelu plików
@@ -164,71 +126,3 @@
<kbd>▼</kbd>: select bottom hunk
<kbd>z</kbd>: undo
</pre>
## Main Panel (Normal)
<pre>
<kbd> ̄</kbd>: scroll down (fn+up)
<kbd>¦</kbd>: scroll up (fn+down)
</pre>
## Main Panel (Patch Building)
<pre>
<kbd>esc</kbd>: exit line-by-line mode
<kbd>▲</kbd>: select previous line
<kbd>▼</kbd>: select next line
<kbd>◄</kbd>: select previous hunk
<kbd>►</kbd>: select next hunk
<kbd>space</kbd>: add/remove line(s) to patch
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
</pre>
## Main Panel (Zatwierdzanie)
<pre>
<kbd>esc</kbd>: wróć do panelu plików
<kbd>space</kbd>: toggle line staged / unstaged
<kbd>d</kbd>: delete change (git reset)
<kbd>tab</kbd>: switch to other panel
<kbd>▲</kbd>: select previous line
<kbd>▼</kbd>: select next line
<kbd>◄</kbd>: select previous hunk
<kbd>►</kbd>: select next hunk
<kbd>e</kbd>: edytuj plik
<kbd>o</kbd>: otwórz plik
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
<kbd>c</kbd>: commituj zmiany
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>C</kbd>: commituj zmiany używając edytora z gita
</pre>
## Menu Panel
<pre>
<kbd>esc</kbd>: close menu
<kbd>q</kbd>: close menu
<kbd>/</kbd>: start search
</pre>
## Schowek Panel
<pre>
<kbd>space</kbd>: zastosuj
<kbd>g</kbd>: wyciągnij
<kbd>d</kbd>: porzuć
<kbd>/</kbd>: start search
</pre>
## Status Panel
<pre>
<kbd>e</kbd>: edytuj plik konfiguracyjny
<kbd>o</kbd>: otwórz plik konfiguracyjny
<kbd>u</kbd>: sprawdź aktualizacje
<kbd>enter</kbd>: switch to a recent repo
</pre>
+3 -3
View File
@@ -1,6 +1,6 @@
module github.com/jesseduffield/lazygit
go 1.14
go 1.13
require (
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
@@ -11,8 +11,9 @@ require (
github.com/golang/protobuf v1.3.2 // indirect
github.com/google/go-cmp v0.3.1 // indirect
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/gocui v0.3.1-0.20200309001002-7765949e1c8a
github.com/jesseduffield/gocui v0.3.1-0.20200224201655-5024a02682ed
github.com/jesseduffield/pty v1.2.1
github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7 // indirect
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
@@ -20,7 +21,6 @@ require (
github.com/mattn/go-isatty v0.0.11 // indirect
github.com/mattn/go-runewidth v0.0.8
github.com/mgutz/str v1.2.0
github.com/mitchellh/gox v1.0.1 // indirect
github.com/nicksnyder/go-i18n/v2 v2.0.3
github.com/onsi/ginkgo v1.10.3 // indirect
github.com/onsi/gomega v1.7.1 // indirect
+33 -10
View File
@@ -54,6 +54,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -68,8 +69,6 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
@@ -78,12 +77,28 @@ github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jesseduffield/gocui v0.3.1-0.20200301081700-d6e485450113 h1:jHZRVJUWsU8HaQ0crocz0i0BkpOqFLDJEO/AtBp+Ecs=
github.com/jesseduffield/gocui v0.3.1-0.20200301081700-d6e485450113/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20200309001002-7765949e1c8a h1:JSORQue6V4bMppr22dtUuYX+w79cgupo66PcGZ9ijlU=
github.com/jesseduffield/gocui v0.3.1-0.20200309001002-7765949e1c8a/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20191116013947-b13bda319532 h1:V1Lk2rm5/p27NjnlF2ezzkxDaisHNcveMNueSD7RYgs=
github.com/jesseduffield/gocui v0.3.1-0.20191116013947-b13bda319532/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20200112025325-6c933915c351 h1:+sSqd2YotacWt+1MNRN8ZmXnYoiJeblZeptzKiHIyv0=
github.com/jesseduffield/gocui v0.3.1-0.20200112025325-6c933915c351/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20200131125953-f679540a7039 h1:CVhilJ8ZdN7GmAI+fbH9829Cp/8hbK7Lijbd4VaNgo0=
github.com/jesseduffield/gocui v0.3.1-0.20200131125953-f679540a7039/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20200131131454-a319843434ac h1:vp7I0RpFq4L46nFA9QQokzhFgr68LRGtwDO9xfq4F+A=
github.com/jesseduffield/gocui v0.3.1-0.20200131131454-a319843434ac/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5 h1:tE0w3tuL/bj1o5VMhjjE0ep6i7Fva+RYjKcMFcniJEY=
github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20200223105115-3e1f0f7c3efe h1:UQyebauOcBzbGq32kTXwEyuJaqp3BkI8JoCrGs2jijU=
github.com/jesseduffield/gocui v0.3.1-0.20200223105115-3e1f0f7c3efe/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20200224201655-5024a02682ed h1:glGs+mzPZOl1iHiUsBW3918WeFwqsbQQ/jtLkkQXDi4=
github.com/jesseduffield/gocui v0.3.1-0.20200224201655-5024a02682ed/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/pty v1.2.1 h1:7xYBiwNH0PpWqC8JmvrPq1a/ksNqyCavzWu9pbBGYWI=
github.com/jesseduffield/pty v1.2.1/go.mod h1:7jlS40+UhOqkZJDIG1B/H21xnuET/+fvbbnHCa8wSIo=
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00 h1:+JaOkfBNYQYlGD7dgru8mCwYNEc5tRRI8mThlVANhSM=
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00/go.mod h1:cWNQljQAWYBp4wchyGfql4q2jRNZXxiE1KhVQgz+JaM=
github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7 h1:CRD7bVjlGIiV+M0jlsa+XWpneW0KY0e7Y4z3GWb5S4o=
github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7/go.mod h1:VspA3aTkEo0Q7TPCLmX1uHNP+Wb4iSDX09hmTRo1QYc=
github.com/jesseduffield/termbox-go v0.0.0-20190630083001-9dd53af7214e h1:tth7wr6+sfSbdpRWWrwvLYyS56HyIRVfq0Qcl2h28wM=
github.com/jesseduffield/termbox-go v0.0.0-20190630083001-9dd53af7214e/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4=
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9 h1:iBBk1lhFwjwJw//J2m1yyz9S368GeXQTpMVACTyQMh0=
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -97,6 +112,7 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -114,6 +130,8 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -121,10 +139,6 @@ 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=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -137,6 +151,7 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
@@ -160,6 +175,7 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w=
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
@@ -168,14 +184,17 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -210,6 +229,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -224,6 +244,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -243,6 +264,7 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
@@ -264,6 +286,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+6
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"github.com/go-errors/errors"
@@ -19,6 +20,11 @@ var (
buildSource = "unknown"
)
func projectPath(path string) string {
gopath := os.Getenv("GOPATH")
return filepath.FromSlash(gopath + "/src/github.com/jesseduffield/lazygit/" + path)
}
func main() {
flaggy.DefaultParser.ShowVersionWithVersionFlag = false
+41 -6
View File
@@ -1,12 +1,47 @@
package commands
import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// Branch : A git branch
// duplicating this for now
type Branch struct {
Name string
Recency string
Pushables string
Pullables string
UpstreamName string
Head bool
Name string
Recency string
Pushables string
Pullables string
Selected bool
}
// GetDisplayStrings returns the display string of branch
func (b *Branch) GetDisplayStrings(isFocused bool) []string {
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name))
if isFocused && b.Selected && b.Pushables != "" && b.Pullables != "" {
displayName = fmt.Sprintf("%s ↑%s↓%s", displayName, b.Pushables, b.Pullables)
}
return []string{b.Recency, displayName}
}
// GetBranchColor branch color
func GetBranchColor(name string) color.Attribute {
branchType := strings.Split(name, "/")[0]
switch branchType {
case "feature":
return color.FgGreen
case "bugfix":
return color.FgYellow
case "hotfix":
return color.FgRed
default:
return theme.DefaultTextColor
}
}
+84 -110
View File
@@ -7,6 +7,8 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus"
"gopkg.in/src-d/go-git.v4/plumbing"
)
// context:
@@ -34,114 +36,113 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand) (*BranchLis
}, nil
}
func (b *BranchListBuilder) obtainCurrentBranchName() string {
func (b *BranchListBuilder) obtainCurrentBranch() *Branch {
branchName, err := b.GitCommand.CurrentBranchName()
if err != nil {
panic(err.Error())
}
return branchName
return &Branch{Name: strings.TrimSpace(branchName)}
}
func (b *BranchListBuilder) obtainBranches() []*Branch {
cmdStr := `git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`
output, err := b.GitCommand.OSCommand.RunCommandWithOutput(cmdStr)
func (b *BranchListBuilder) obtainReflogBranches() []*Branch {
branches := make([]*Branch, 0)
// if we directly put this string in RunCommandWithOutput the compiler complains because it thinks it's a format string
unescaped := "git reflog --date=relative --pretty='%gd|%gs' --grep-reflog='checkout: moving' HEAD"
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput(unescaped)
if err != nil {
return branches
}
branchLines := utils.SplitLines(rawString)
for _, line := range branchLines {
recency, branchName := branchInfoFromLine(line)
if branchName == "" {
continue
}
branch := &Branch{Name: branchName, Recency: recency}
branches = append(branches, branch)
}
return uniqueByName(branches)
}
func (b *BranchListBuilder) obtainSafeBranches() []*Branch {
branches := make([]*Branch, 0)
bIter, err := b.GitCommand.Repo.Branches()
if err != nil {
panic(err)
}
trimmedOutput := strings.TrimSpace(output)
outputLines := strings.Split(trimmedOutput, "\n")
branches := make([]*Branch, 0, len(outputLines))
for _, line := range outputLines {
if line == "" {
continue
}
split := strings.Split(line, SEPARATION_CHAR)
name := split[1]
branch := &Branch{
Name: name,
Pullables: "?",
Pushables: "?",
Head: split[0] == "*",
}
upstreamName := split[2]
if upstreamName == "" {
branches = append(branches, branch)
continue
}
branch.UpstreamName = upstreamName
track := split[3]
re := regexp.MustCompile(`ahead (\d+)`)
match := re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pushables = match[1]
} else {
branch.Pushables = "0"
}
re = regexp.MustCompile(`behind (\d+)`)
match = re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pullables = match[1]
} else {
branch.Pullables = "0"
}
branches = append(branches, branch)
}
bIter.ForEach(func(b *plumbing.Reference) error {
name := b.Name().Short()
branches = append(branches, &Branch{Name: name})
return nil
})
return branches
}
func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []*Branch, included bool) []*Branch {
for _, newBranch := range newBranches {
if included == branchIncluded(newBranch.Name, existingBranches) {
finalBranches = append(finalBranches, newBranch)
}
}
return finalBranches
}
func sanitisedReflogName(reflogBranch *Branch, safeBranches []*Branch) string {
for _, safeBranch := range safeBranches {
if strings.ToLower(safeBranch.Name) == strings.ToLower(reflogBranch.Name) {
return safeBranch.Name
}
}
return reflogBranch.Name
}
// Build the list of branches for the current repo
func (b *BranchListBuilder) Build() []*Branch {
currentBranchName := b.obtainCurrentBranchName()
branches := b.obtainBranches()
branches := make([]*Branch, 0)
head := b.obtainCurrentBranch()
safeBranches := b.obtainSafeBranches()
reflogBranches := b.obtainReflogBranches()
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
branchesWithRecency := make([]*Branch, 0)
outer:
for _, reflogBranch := range reflogBranches {
for j, branch := range branches {
if branch.Head {
continue
}
if strings.EqualFold(reflogBranch.Name, branch.Name) {
branch.Recency = reflogBranch.Recency
branchesWithRecency = append(branchesWithRecency, branch)
branches = append(branches[0:j], branches[j+1:]...)
continue outer
}
}
for i, reflogBranch := range reflogBranches {
reflogBranches[i].Name = sanitisedReflogName(reflogBranch, safeBranches)
}
branches = append(branchesWithRecency, branches...)
branches = b.appendNewBranches(branches, reflogBranches, safeBranches, true)
branches = b.appendNewBranches(branches, safeBranches, branches, false)
foundHead := false
for i, branch := range branches {
if branch.Head {
foundHead = true
branch.Name = currentBranchName
branch.Recency = " *"
branches = append(branches[0:i], branches[i+1:]...)
branches = append([]*Branch{branch}, branches...)
break
}
}
if !foundHead {
branches = append([]*Branch{{Name: currentBranchName, Head: true, Recency: " *"}}, branches...)
if len(branches) == 0 || branches[0].Name != head.Name {
branches = append([]*Branch{head}, branches...)
}
branches[0].Recency = " *"
return branches
}
func branchIncluded(branchName string, branches []*Branch) bool {
for _, existingBranch := range branches {
if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) {
return true
}
}
return false
}
func uniqueByName(branches []*Branch) []*Branch {
finalBranches := make([]*Branch, 0)
for _, branch := range branches {
if branchIncluded(branch.Name, finalBranches) {
continue
}
finalBranches = append(finalBranches, branch)
}
return finalBranches
}
// A line will have the form '10 days ago master' so we need to strip out the
// useful information from that into timeNumber, timeUnit, and branchName
func branchInfoFromLine(line string) (string, string) {
@@ -171,30 +172,3 @@ func abbreviatedTimeUnit(timeUnit string) string {
}
return timeUnitMap[timeUnit]
}
func (b *BranchListBuilder) obtainReflogBranches() []*Branch {
branches := make([]*Branch, 0)
// if we directly put this string in RunCommandWithOutput the compiler complains because it thinks it's a format string
unescaped := "git reflog --date=relative --pretty='%gd|%gs' --grep-reflog='checkout: moving' HEAD"
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput(unescaped)
if err != nil {
return branches
}
branchNameMap := map[string]bool{}
branchLines := utils.SplitLines(rawString)
for _, line := range branchLines {
recency, branchName := branchInfoFromLine(line)
if branchName == "" {
continue
}
if _, ok := branchNameMap[branchName]; ok {
continue
}
branchNameMap[branchName] = true
branch := &Branch{Name: branchName, Recency: recency}
branches = append(branches, branch)
}
return branches
}
+57 -3
View File
@@ -1,5 +1,13 @@
package commands
import (
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// Commit : A git commit
type Commit struct {
Sha string
@@ -9,7 +17,53 @@ type Commit struct {
Action string // one of "", "pick", "edit", "squash", "reword", "drop", "fixup"
Copied bool // to know if this commit is ready to be cherry-picked somewhere
Tags []string
ExtraInfo string // something like 'HEAD -> master, tag: v0.15.2'
Author string
Date string
}
// GetDisplayStrings is a function.
func (c *Commit) GetDisplayStrings(isFocused bool) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor)
magenta := color.New(color.FgMagenta)
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)
var shaColor *color.Color
switch c.Status {
case "unpushed":
shaColor = red
case "pushed":
shaColor = yellow
case "merged":
shaColor = green
case "rebasing":
shaColor = blue
case "reflog":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = defaultColor
}
if c.Copied {
shaColor = copied
}
actionString := ""
tagString := ""
if c.Action != "" {
actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
} else if len(c.Tags) > 0 {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(strings.Join(c.Tags, " "), tagColor) + " "
}
return []string{shaColor.Sprint(c.Sha[:8]), actionString + tagString + defaultColor.Sprint(c.Name)}
}
+23
View File
@@ -1,5 +1,10 @@
package commands
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/theme"
)
// CommitFile : A git commit file
type CommitFile struct {
Sha string
@@ -17,3 +22,21 @@ const (
// PART is for when you're only talking about specific lines that have been modified
PART
)
// GetDisplayStrings is a function.
func (f *CommitFile) GetDisplayStrings(isFocused bool) []string {
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
defaultColor := color.New(theme.DefaultTextColor)
var colour *color.Color
switch f.Status {
case UNSELECTED:
colour = defaultColor
case WHOLE:
colour = green
case PART:
colour = yellow
}
return []string{colour.Sprint(f.DisplayString)}
}
+43 -35
View File
@@ -23,8 +23,6 @@ import (
// if we find out we need to use one of these functions in the git.go file, we
// can just pull them out of here and put them there and then call them from in here
const SEPARATION_CHAR = "|"
// CommitListBuilder returns a list of Branch objects for the current repo
type CommitListBuilder struct {
Log *logrus.Entry
@@ -47,37 +45,42 @@ func NewCommitListBuilder(log *logrus.Entry, gitCommand *GitCommand, osCommand *
}, nil
}
// extractCommitFromLine takes a line from a git log and extracts the sha, message, date, and tag if present
// then puts them into a commit object
// example input:
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
func (c *CommitListBuilder) extractCommitFromLine(line string) *Commit {
split := strings.Split(line, SEPARATION_CHAR)
// nameAndTag takes a line from a git log and extracts the sha, message and tag (if present)
// example inputs:
// 66e6369c284e96ed5af5 (tag: v0.14.4) allow fastforwarding the current branch
// 32e650e0bb3f4327749f (HEAD -> show-tags) this is my commit
// 32e650e0bb3f4327749e this is my other commit
func (c *CommitListBuilder) commitLineParts(line string) (string, string, []string) {
re := regexp.MustCompile(`(\w+) (.*)`)
shaMatch := re.FindStringSubmatch(line)
sha := split[0]
date := split[1]
author := split[2]
extraInfo := strings.TrimSpace(split[3])
message := strings.Join(split[4:], SEPARATION_CHAR)
tags := []string{}
if len(shaMatch) <= 1 {
return line, "", nil
}
sha := shaMatch[1]
rest := shaMatch[2]
if extraInfo != "" {
re := regexp.MustCompile(`tag: ([^,\)]+)`)
tagMatch := re.FindStringSubmatch(extraInfo)
if len(tagMatch) > 1 {
tags = append(tags, tagMatch[1])
}
if !strings.HasPrefix(rest, "(") {
return sha, rest, nil
}
return &Commit{
Sha: sha,
Name: message,
DisplayString: line,
Tags: tags,
ExtraInfo: extraInfo,
Date: date,
Author: author,
re = regexp.MustCompile(`\((.*)\) (.*)`)
parensMatch := re.FindStringSubmatch(rest)
if len(parensMatch) <= 1 {
return sha, rest, nil
}
notes := parensMatch[1]
message := parensMatch[2]
re = regexp.MustCompile(`tag: ([^,]+)`)
tagMatch := re.FindStringSubmatch(notes)
if len(tagMatch) <= 1 {
return sha, message, nil
}
tag := tagMatch[1]
return sha, message, []string{tag}
}
// GetCommits obtains the commits of the current branch
@@ -104,10 +107,16 @@ func (c *CommitListBuilder) GetCommits(limit bool) ([]*Commit, error) {
// now we can split it up and turn it into commits
for _, line := range utils.SplitLines(log) {
commit := c.extractCommitFromLine(line)
_, unpushed := unpushedCommits[commit.Sha[:8]]
commit.Status = map[bool]string{true: "unpushed", false: "pushed"}[unpushed]
commits = append(commits, commit)
sha, name, tags := c.commitLineParts(line)
_, unpushed := unpushedCommits[sha]
status := map[bool]string{true: "unpushed", false: "pushed"}[unpushed]
commits = append(commits, &Commit{
Sha: sha,
Name: name,
Status: status,
DisplayString: line,
Tags: tags,
})
}
if rebaseMode != "" {
currentCommit := commits[len(rebasingCommits)]
@@ -298,7 +307,7 @@ func (c *CommitListBuilder) getMergeBase() (string, error) {
// to the remote branch of the current branch, a map is returned to ease look up
func (c *CommitListBuilder) getUnpushedCommits() map[string]bool {
pushables := map[string]bool{}
o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --abbrev-commit --abbrev=8")
o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --abbrev-commit")
if err != nil {
return pushables
}
@@ -316,8 +325,7 @@ func (c *CommitListBuilder) getLog(limit bool) string {
limitFlag = "-30"
}
result, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --oneline --pretty=format:\"%%H%s%%ar%s%%aN%s%%d%s%%s\" %s --abbrev=%d", SEPARATION_CHAR, SEPARATION_CHAR, SEPARATION_CHAR, SEPARATION_CHAR, limitFlag, 20))
result, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --decorate --oneline %s --abbrev=%d", limitFlag, 20))
if err != nil {
// assume if there is an error there are no commits yet for this branch
return ""
+167
View File
@@ -149,3 +149,170 @@ func TestCommitListBuilderGetMergeBase(t *testing.T) {
})
}
}
// TestCommitListBuilderGetLog is a function.
func TestCommitListBuilderGetLog(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
test func(string)
}
scenarios := []scenario{
{
"Retrieves logs",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"log", "--decorate", "--oneline", "-30", "--abbrev=20"}, args)
return exec.Command("echo", "6f0b32f commands/git : add GetCommits tests refactor\n9d9d775 circle : remove new line")
},
func(output string) {
assert.EqualValues(t, "6f0b32f commands/git : add GetCommits tests refactor\n9d9d775 circle : remove new line\n", output)
},
},
{
"An error occurred when retrieving logs",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"log", "--decorate", "--oneline", "-30", "--abbrev=20"}, args)
return exec.Command("test")
},
func(output string) {
assert.Empty(t, output)
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
c := NewDummyCommitListBuilder()
c.OSCommand.SetCommand(s.command)
s.test(c.getLog(true))
})
}
}
// TestCommitListBuilderGetCommits is a function.
func TestCommitListBuilderGetCommits(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
test func([]*Commit, error)
}
scenarios := []scenario{
{
"No data found",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
switch args[0] {
case "rev-list":
assert.EqualValues(t, []string{"rev-list", "@{u}..HEAD", "--abbrev-commit"}, args)
return exec.Command("echo")
case "log":
assert.EqualValues(t, []string{"log", "--decorate", "--oneline", "-30", "--abbrev=20"}, args)
return exec.Command("echo")
case "merge-base":
assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
return exec.Command("test")
case "symbolic-ref":
assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
return exec.Command("echo", "master")
}
return nil
},
func(commits []*Commit, err error) {
assert.NoError(t, err)
assert.Len(t, commits, 0)
},
},
{
"GetCommits returns 2 commits, 1 unpushed, the other merged",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
switch args[0] {
case "rev-list":
assert.EqualValues(t, []string{"rev-list", "@{u}..HEAD", "--abbrev-commit"}, args)
return exec.Command("echo", "8a2bb0e")
case "log":
assert.EqualValues(t, []string{"log", "--decorate", "--oneline", "-30", "--abbrev=20"}, args)
return exec.Command("echo", "8a2bb0e commit 1\n78976bc commit 2")
case "merge-base":
assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
return exec.Command("echo", "78976bc")
case "symbolic-ref":
assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
return exec.Command("echo", "master")
}
return nil
},
func(commits []*Commit, err error) {
assert.NoError(t, err)
assert.Len(t, commits, 2)
assert.EqualValues(t, []*Commit{
{
Sha: "8a2bb0e",
Name: "commit 1",
Status: "unpushed",
DisplayString: "8a2bb0e commit 1",
},
{
Sha: "78976bc",
Name: "commit 2",
Status: "merged",
DisplayString: "78976bc commit 2",
},
}, commits)
},
},
{
"GetCommits bubbles up an error from setCommitMergedStatuses",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
switch args[0] {
case "rev-list":
assert.EqualValues(t, []string{"rev-list", "@{u}..HEAD", "--abbrev-commit"}, args)
return exec.Command("echo", "8a2bb0e")
case "log":
assert.EqualValues(t, []string{"log", "--decorate", "--oneline", "-30", "--abbrev=20"}, args)
return exec.Command("echo", "8a2bb0e commit 1\n78976bc commit 2")
case "merge-base":
assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
return exec.Command("echo", "78976bc")
case "symbolic-ref":
assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
// here's where we are returning the error
return exec.Command("test")
case "branch":
assert.EqualValues(t, []string{"branch", "--contains"}, args)
// here too
return exec.Command("test")
case "rev-parse":
assert.EqualValues(t, []string{"rev-parse", "--short", "HEAD"}, args)
// here too
return exec.Command("test")
}
return nil
},
func(commits []*Commit, err error) {
assert.Error(t, err)
assert.Len(t, commits, 0)
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
c := NewDummyCommitListBuilder()
c.OSCommand.SetCommand(s.command)
s.test(c.GetCommits(true))
})
}
}
+22
View File
@@ -1,5 +1,7 @@
package commands
import "github.com/fatih/color"
// File : A file from git status
// duplicating this for now
type File struct {
@@ -14,3 +16,23 @@ type File struct {
Type string // one of 'file', 'directory', and 'other'
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
}
// GetDisplayStrings returns the display string of a file
func (f *File) GetDisplayStrings(isFocused bool) []string {
// potentially inefficient to be instantiating these color
// objects with each render
red := color.New(color.FgRed)
green := color.New(color.FgGreen)
if !f.Tracked && !f.HasStagedChanges {
return []string{red.Sprint(f.DisplayString)}
}
output := green.Sprint(f.DisplayString[0:1])
output += red.Sprint(f.DisplayString[1:3])
if f.HasUnstagedChanges {
output += red.Sprint(f.Name)
} else {
output += green.Sprint(f.Name)
}
return []string{output}
}
+19 -61
View File
@@ -7,7 +7,6 @@ import (
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
@@ -178,7 +177,7 @@ func stashEntryFromLine(line string, index int) *StashEntry {
// GetStashEntryDiff stash diff
func (c *GitCommand) ShowStashEntryCmdStr(index int) string {
return fmt.Sprintf("git stash show -p --color=%s stash@{%d}", c.colorArg(), index)
return fmt.Sprintf("git stash show -p --color stash@{%d}", index)
}
// GetStatusFiles git status files
@@ -327,8 +326,8 @@ func (c *GitCommand) ResetToCommit(sha string, strength string) error {
}
// NewBranch create new branch
func (c *GitCommand) NewBranch(name string, baseBranch string) error {
return c.OSCommand.RunCommand("git checkout -b %s %s", name, baseBranch)
func (c *GitCommand) NewBranch(name string) error {
return c.OSCommand.RunCommand("git checkout -b %s", name)
}
// CurrentBranchName is a function.
@@ -343,7 +342,7 @@ func (c *GitCommand) CurrentBranchName() (string, error) {
match := re.FindStringSubmatch(output)
branchName = match[1]
}
return strings.TrimSpace(branchName), nil
return utils.TrimTrailingNewline(branchName), nil
}
// DeleteBranch delete branch
@@ -559,11 +558,11 @@ func (c *GitCommand) Ignore(filename string) error {
}
func (c *GitCommand) ShowCmdStr(sha string) string {
return fmt.Sprintf("git show --color=%s --no-renames --stat -p %s", c.colorArg(), sha)
return fmt.Sprintf("git show --color --no-renames --stat -p %s", sha)
}
func (c *GitCommand) GetBranchGraphCmdStr(branchName string) string {
return fmt.Sprintf("git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium %s --", branchName)
return fmt.Sprintf("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium %s", branchName)
}
// GetRemoteURL returns current repo remote url
@@ -592,7 +591,7 @@ func (c *GitCommand) Diff(file *File, plain bool, cached bool) string {
func (c *GitCommand) DiffCmdStr(file *File, plain bool, cached bool) string {
cachedArg := ""
trackedArg := "--"
colorArg := c.colorArg()
colorArg := "--color"
split := strings.Split(file.Name, " -> ") // in case of a renamed file we get the new filename
fileName := c.OSCommand.Quote(split[len(split)-1])
if cached {
@@ -602,10 +601,10 @@ func (c *GitCommand) DiffCmdStr(file *File, plain bool, cached bool) string {
trackedArg = "--no-index /dev/null"
}
if plain {
colorArg = "never"
colorArg = ""
}
return fmt.Sprintf("git diff --color=%s %s %s %s", colorArg, cachedArg, trackedArg, fileName)
return fmt.Sprintf("git diff --stat -p %s %s %s %s", colorArg, cachedArg, trackedArg, fileName)
}
func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
@@ -866,7 +865,7 @@ func (c *GitCommand) CherryPickCommits(commits []*Commit) error {
// GetCommitFiles get the specified commit files
func (c *GitCommand) GetCommitFiles(commitSha string, patchManager *PatchManager) ([]*CommitFile, error) {
files, err := c.OSCommand.RunCommandWithOutput("git diff-tree --no-commit-id --name-only -r --no-renames %s", commitSha)
files, err := c.OSCommand.RunCommandWithOutput("git show --pretty= --name-only --no-renames %s", commitSha)
if err != nil {
return nil, err
}
@@ -897,12 +896,12 @@ func (c *GitCommand) ShowCommitFile(commitSha, fileName string, plain bool) (str
}
func (c *GitCommand) ShowCommitFileCmdStr(commitSha, fileName string, plain bool) string {
colorArg := c.colorArg()
colorArg := "--color"
if plain {
colorArg = "never"
colorArg = ""
}
return fmt.Sprintf("git show --no-renames --color=%s %s -- %s", colorArg, commitSha, fileName)
return fmt.Sprintf("git show --no-renames %s %s -- %s", colorArg, commitSha, fileName)
}
// CheckoutFile checks out the file for the given commit
@@ -968,7 +967,7 @@ func (c *GitCommand) ResetSoft(ref string) error {
// DiffCommits show diff between commits
func (c *GitCommand) DiffCommits(sha1, sha2 string) (string, error) {
return c.OSCommand.RunCommandWithOutput("git diff --color=%s --stat -p %s %s", c.colorArg(), sha1, sha2)
return c.OSCommand.RunCommandWithOutput("git diff --color %s %s", sha1, sha2)
}
// CreateFixupCommit creates a commit that fixes up a previous commit
@@ -1108,65 +1107,24 @@ func (c *GitCommand) FetchRemote(remoteName string) error {
func (c *GitCommand) GetReflogCommits() ([]*Commit, error) {
output, err := c.OSCommand.RunCommandWithOutput("git reflog --abbrev=20")
if err != nil {
// assume error means we have no reflog
return []*Commit{}, nil
return nil, err
}
lines := strings.Split(strings.TrimSpace(output), "\n")
commits := make([]*Commit, 0)
commits := make([]*Commit, len(lines))
re := regexp.MustCompile(`(\w+).*HEAD@\{\d+\}: (.*)`)
for _, line := range lines {
for i, line := range lines {
match := re.FindStringSubmatch(line)
if len(match) <= 1 {
if len(match) == 1 {
continue
}
commit := &Commit{
commits[i] = &Commit{
Sha: match[1],
Name: match[2],
Status: "reflog",
}
commits = append(commits, commit)
}
return commits, nil
}
func (c *GitCommand) ConfiguredPager() string {
if os.Getenv("GIT_PAGER") != "" {
return os.Getenv("GIT_PAGER")
}
if os.Getenv("PAGER") != "" {
return os.Getenv("PAGER")
}
output, err := c.OSCommand.RunCommandWithOutput("git config --get-all core.pager")
if err != nil {
return ""
}
trimmedOutput := strings.TrimSpace(output)
return strings.Split(trimmedOutput, "\n")[0]
}
func (c *GitCommand) GetPager(width int) string {
useConfig := c.Config.GetUserConfig().GetBool("git.paging.useConfig")
if useConfig {
pager := c.ConfiguredPager()
return strings.Split(pager, "| less")[0]
}
templateValues := map[string]string{
"columnWidth": strconv.Itoa(width/2 - 6),
}
pagerTemplate := c.Config.GetUserConfig().GetString("git.paging.pager")
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
}
func (c *GitCommand) colorArg() string {
return c.Config.GetUserConfig().GetString("git.paging.colorArg")
}
func (c *GitCommand) RenameBranch(oldName string, newName string) error {
return c.OSCommand.RunCommand("git branch --move %s %s", oldName, newName)
}
+10 -10
View File
@@ -636,12 +636,12 @@ func TestGitCommandNewBranch(t *testing.T) {
gitCmd := NewDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"checkout", "-b", "test", "master"}, args)
assert.EqualValues(t, []string{"checkout", "-b", "test"}, args)
return exec.Command("echo")
}
assert.NoError(t, gitCmd.NewBranch("test", "master"))
assert.NoError(t, gitCmd.NewBranch("test"))
}
// TestGitCommandDeleteBranch is a function.
@@ -1449,7 +1449,7 @@ func TestGitCommandGetBranchGraph(t *testing.T) {
gitCmd := NewDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"log", "--graph", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "test", "--"}, args)
assert.EqualValues(t, []string{"log", "--graph", "--color", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "test"}, args)
return exec.Command("echo")
}
@@ -1473,7 +1473,7 @@ func TestGitCommandDiff(t *testing.T) {
"Default case",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--color=", "--", "test.txt"}, args)
assert.EqualValues(t, []string{"diff", "--stat", "-p", "--color", "--", "test.txt"}, args)
return exec.Command("echo")
},
@@ -1489,7 +1489,7 @@ func TestGitCommandDiff(t *testing.T) {
"cached",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--color=", "--cached", "--", "test.txt"}, args)
assert.EqualValues(t, []string{"diff", "--stat", "-p", "--color", "--cached", "--", "test.txt"}, args)
return exec.Command("echo")
},
@@ -1505,7 +1505,7 @@ func TestGitCommandDiff(t *testing.T) {
"plain",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--color=never", "--", "test.txt"}, args)
assert.EqualValues(t, []string{"diff", "--stat", "-p", "--", "test.txt"}, args)
return exec.Command("echo")
},
@@ -1521,7 +1521,7 @@ func TestGitCommandDiff(t *testing.T) {
"File not tracked and file has no staged changes",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--color=", "--no-index", "/dev/null", "test.txt"}, args)
assert.EqualValues(t, []string{"diff", "--stat", "-p", "--color", "--no-index", "/dev/null", "test.txt"}, args)
return exec.Command("echo")
},
@@ -1871,7 +1871,7 @@ func TestGitCommandShowCommitFile(t *testing.T) {
"hello.txt",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git show --no-renames --color=never 123456 -- hello.txt",
Expect: "git show --no-renames 123456 -- hello.txt",
Replace: "echo -n hello",
},
}),
@@ -1907,7 +1907,7 @@ func TestGitCommandGetCommitFiles(t *testing.T) {
"123456",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git diff-tree --no-commit-id --name-only -r --no-renames 123456",
Expect: "git show --pretty= --name-only --no-renames 123456",
Replace: "echo 'hello\nworld'",
},
}),
@@ -2130,7 +2130,7 @@ func TestGitCommandSkipEditorCommand(t *testing.T) {
)
})
_ = cmd.RunSkipEditorCommand("true")
cmd.RunSkipEditorCommand("true")
}
func TestFindDotGitDir(t *testing.T) {
-8
View File
@@ -401,11 +401,3 @@ func (c *OSCommand) PipeCommands(commandStrings ...string) error {
}
return nil
}
func Kill(cmd *exec.Cmd) error {
if cmd.Process == nil {
// somebody got to it before we were able to, poor bastard
return nil
}
return cmd.Process.Kill()
}
+18 -55
View File
@@ -5,7 +5,6 @@ import (
"strings"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/config"
)
// Service is a service that repository is on (Github, Bitbucket, ...)
@@ -27,63 +26,27 @@ type RepoInformation struct {
Repository string
}
// NewService builds a Service based on the host type
func NewService(typeName string, repositoryDomain string, siteDomain string) *Service {
var service *Service
switch typeName {
case "github":
service = &Service{
Name: repositoryDomain,
PullRequestURL: fmt.Sprintf("https://%s%s", siteDomain, "/%s/%s/compare/%s?expand=1"),
}
case "bitbucket":
service = &Service{
Name: repositoryDomain,
PullRequestURL: fmt.Sprintf("https://%s%s", siteDomain, "/%s/%s/pull-requests/new?source=%s&t=1"),
}
case "gitlab":
service = &Service{
Name: repositoryDomain,
PullRequestURL: fmt.Sprintf("https://%s%s", siteDomain, "/%s/%s/merge_requests/new?merge_request[source_branch]=%s"),
}
func getServices() []*Service {
return []*Service{
{
Name: "github.com",
PullRequestURL: "https://github.com/%s/%s/compare/%s?expand=1",
},
{
Name: "bitbucket.org",
PullRequestURL: "https://bitbucket.org/%s/%s/pull-requests/new?source=%s&t=1",
},
{
Name: "gitlab.com",
PullRequestURL: "https://gitlab.com/%s/%s/merge_requests/new?merge_request[source_branch]=%s",
},
}
return service
}
func getServices(config config.AppConfigurer) []*Service {
services := []*Service{
NewService("github", "github.com", "github.com"),
NewService("bitbucket", "bitbucket.org", "bitbucket.org"),
NewService("gitlab", "gitlab.com", "gitlab.com"),
}
configServices := config.GetUserConfig().GetStringMapString("services")
for repoDomain, typeAndDomain := range configServices {
splitData := strings.Split(typeAndDomain, ":")
if len(splitData) != 2 {
// TODO log this misconfiguration
continue
}
service := NewService(splitData[0], repoDomain, splitData[1])
if service == nil {
// TODO log this unsupported service
continue
}
services = append(services, service)
}
return services
}
// NewPullRequest creates new instance of PullRequest
func NewPullRequest(gitCommand *GitCommand) *PullRequest {
return &PullRequest{
GitServices: getServices(gitCommand.Config),
GitServices: getServices(),
GitCommand: gitCommand,
}
}
@@ -122,7 +85,7 @@ func getRepoInfoFromURL(url string) *RepoInformation {
if isHTTP {
splits := strings.Split(url, "/")
owner := strings.Join(splits[3:len(splits)-1], "/")
owner := splits[len(splits)-2]
repo := strings.TrimSuffix(splits[len(splits)-1], ".git")
return &RepoInformation{
@@ -133,8 +96,8 @@ func getRepoInfoFromURL(url string) *RepoInformation {
tmpSplit := strings.Split(url, ":")
splits := strings.Split(tmpSplit[1], "/")
owner := strings.Join(splits[0:len(splits)-1], "/")
repo := strings.TrimSuffix(splits[len(splits)-1], ".git")
owner := splits[0]
repo := strings.TrimSuffix(splits[1], ".git")
return &RepoInformation{
Owner: owner,
-7
View File
@@ -147,13 +147,6 @@ func TestCreatePullRequest(t *testing.T) {
gitCommand := NewDummyGitCommand()
gitCommand.OSCommand.command = s.command
gitCommand.OSCommand.Config.GetUserConfig().Set("os.openLinkCommand", "open {{link}}")
gitCommand.Config.GetUserConfig().Set("services", map[string]string{
// valid configuration for a custom service URL
"git.work.com": "gitlab:code.work.com",
// invalid configurations for a custom service URL
"invalid.work.com": "noservice:invalid.work.com",
"noservice.work.com": "noservice.work.com",
})
dummyPullRequest := NewPullRequest(gitCommand)
s.test(dummyPullRequest.Create(s.branch))
})
+12
View File
@@ -1,7 +1,19 @@
package commands
import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
// Remote Branch : A git remote branch
type RemoteBranch struct {
Name string
Selected bool
RemoteName string
}
// GetDisplayStrings returns the display string of branch
func (b *RemoteBranch) GetDisplayStrings(isFocused bool) []string {
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name))
return []string{displayName}
}
+5
View File
@@ -4,3 +4,8 @@ package commands
type Tag struct {
Name string
}
// GetDisplayStrings returns the display string of a remote
func (r *Tag) GetDisplayStrings(isFocused bool) []string {
return []string{r.Name}
}
+1 -7
View File
@@ -244,11 +244,10 @@ func GetDefaultConfig() []byte {
scrollPastBottom: true
mouseEvents: true
skipUnstageLineWarning: false
sidePanelWidth: 0.3333
theme:
lightTheme: false
activeBorderColor:
- green
- white
- bold
inactiveBorderColor:
- white
@@ -259,9 +258,6 @@ func GetDefaultConfig() []byte {
commitLength:
show: true
git:
paging:
colorArg: always
useConfig: false
merging:
manualCommit: false
skipHookPrefix: 'WIP'
@@ -334,7 +330,6 @@ keybinding:
checkoutBranchByName: 'c'
forceCheckoutBranch: 'F'
rebaseBranch: 'r'
renameBranch: 'R'
mergeIntoCurrentBranch: 'M'
viewGitFlowOptions: 'i'
fastForward: 'f'
@@ -360,7 +355,6 @@ keybinding:
tagCommit: 'T'
toggleDiffCommit: 'i'
checkoutCommit: '<space>'
resetCherryPick: '<c-R>'
stash:
popStash: 'g'
commitFiles:
+3 -1
View File
@@ -66,7 +66,9 @@ func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
if appStatus == "" {
return
}
gui.renderString(gui.g, "appStatus", appStatus)
if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
gui.Log.Warn(err)
}
}
}()
+46 -64
View File
@@ -6,7 +6,6 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -40,7 +39,12 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
return gui.newStringTask("main", gui.Tr.SLocalize("NoBranchesThisRepo"))
}
branch := gui.getSelectedBranch()
v.FocusPoint(0, gui.State.Panels.Branches.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches), v); err != nil {
return err
}
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
return err
}
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
@@ -51,6 +55,28 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
return nil
}
func (gui *Gui) RenderSelectedBranchUpstreamDifferences() error {
return gui.newTask("branches", func(stop chan struct{}) error {
// here we tell the selected branch that it is selected.
// this is necessary for showing stats on a branch that is selected, because
// the displaystring function doesn't have access to gui state to tell if it's selected
for i, branch := range gui.State.Branches {
branch.Selected = i == gui.State.Panels.Branches.SelectedLine
}
branch := gui.getSelectedBranch()
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
select {
case <-stop:
return nil
default:
}
return gui.renderListPanel(gui.getBranchesView(), gui.State.Branches)
})
}
// gui.refreshStatus is called at the end of this because that's when we can
// be sure there is a state.Branches array to pick the current branch from
func (gui *Gui) refreshBranches(g *gocui.Gui) error {
@@ -85,8 +111,9 @@ func (gui *Gui) renderLocalBranchesWithSelection() error {
branchesView := gui.getBranchesView()
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
displayStrings := presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.State.ScreenMode != SCREEN_NORMAL)
gui.renderDisplayStrings(branchesView, displayStrings)
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
return err
}
if gui.g.CurrentView() == branchesView {
if err := gui.handleBranchSelect(gui.g, branchesView); err != nil {
return err
@@ -137,7 +164,7 @@ func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
title := gui.Tr.SLocalize("ForceCheckoutBranch")
return gui.createConfirmationPanel(g, v, true, title, message, func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.Checkout(branch.Name, true); err != nil {
_ = gui.createErrorPanel(g, err.Error())
gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(g)
}, nil)
@@ -181,9 +208,10 @@ func (gui *Gui) handleCheckoutRef(ref string) error {
}
func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error {
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", "", func(g *gocui.Gui, v *gocui.View) error {
gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", "", func(g *gocui.Gui, v *gocui.View) error {
return gui.handleCheckoutRef(gui.trimmedContent(v))
})
return nil
}
func (gui *Gui) getCheckedOutBranch() *commands.Branch {
@@ -195,31 +223,31 @@ func (gui *Gui) getCheckedOutBranch() *commands.Branch {
}
func (gui *Gui) handleNewBranch(g *gocui.Gui, v *gocui.View) error {
branch := gui.getSelectedBranch()
if branch == nil {
return nil
}
branch := gui.getCheckedOutBranch()
message := gui.Tr.TemplateLocalize(
"NewBranchNameBranchOff",
Teml{
"branchName": branch.Name,
},
)
return gui.createPromptPanel(g, v, message, "", func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.NewBranch(gui.trimmedContent(v), branch.Name); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshSidePanels(g); err != nil {
gui.createPromptPanel(g, v, message, "", func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.NewBranch(gui.trimmedContent(v)); err != nil {
return gui.createErrorPanel(g, err.Error())
}
gui.refreshSidePanels(g)
return gui.handleBranchSelect(g, v)
})
return nil
}
func (gui *Gui) handleDeleteBranch(g *gocui.Gui, v *gocui.View) error {
return gui.deleteBranch(g, v, false)
}
func (gui *Gui) handleForceDeleteBranch(g *gocui.Gui, v *gocui.View) error {
return gui.deleteBranch(g, v, true)
}
func (gui *Gui) deleteBranch(g *gocui.Gui, v *gocui.View, force bool) error {
selectedBranch := gui.getSelectedBranch()
if selectedBranch == nil {
@@ -353,7 +381,7 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.FastForward(branch.Name, remoteName, remoteBranchName); err != nil {
_ = gui.createErrorPanel(gui.g, err.Error())
}
_ = gui.refreshBranches(gui.g)
_ = gui.RenderSelectedBranchUpstreamDifferences()
}
_ = gui.closeConfirmationPrompt(gui.g, true)
@@ -372,9 +400,7 @@ func (gui *Gui) onBranchesTabClick(tabIndex int) error {
func (gui *Gui) switchBranchesPanelContext(context string) error {
branchesView := gui.getBranchesView()
branchesView.Context = context
if err := gui.onSearchEscape(); err != nil {
return err
}
gui.onSearchEscape()
contextTabIndexMap := map[string]int{
"local-branches": 0,
@@ -385,13 +411,7 @@ func (gui *Gui) switchBranchesPanelContext(context string) error {
branchesView.TabIndex = contextTabIndexMap[context]
return gui.refreshBranchesViewWithSelection()
}
func (gui *Gui) refreshBranchesViewWithSelection() error {
branchesView := gui.getBranchesView()
switch branchesView.Context {
switch context {
case "local-branches":
return gui.renderLocalBranchesWithSelection()
case "remotes":
@@ -441,41 +461,3 @@ func (gui *Gui) onBranchesPanelSearchSelect(selectedLine int) error {
}
return nil
}
func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error {
branch := gui.getSelectedBranch()
if branch == nil {
return nil
}
promptForNewName := func() error {
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("NewBranchNamePrompt")+" "+branch.Name+":", "", func(g *gocui.Gui, v *gocui.View) error {
newName := gui.trimmedContent(v)
if err := gui.GitCommand.RenameBranch(branch.Name, newName); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
// need to checkout so that the branch shows up in our reflog and therefore
// doesn't get lost among all the other branches when we switch to something else
if err := gui.GitCommand.Checkout(newName, false); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
return gui.refreshBranches(gui.g)
})
}
// I could do an explicit check here for whether the branch is tracking a remote branch
// but if we've selected it we'll already know that via Pullables and Pullables.
// Bit of a hack but I'm lazy.
notTrackingRemote := branch.Pullables == "?"
if notTrackingRemote {
return promptForNewName()
}
return gui.createConfirmationPanel(gui.g, v, true, gui.Tr.SLocalize("renameBranch"), gui.Tr.SLocalize("RenameBranchWarning"), func(_g *gocui.Gui, _v *gocui.View) error {
return promptForNewName()
}, nil)
}
func (gui *Gui) currentBranch() *commands.Branch {
return gui.State.Branches[0]
}
+11 -13
View File
@@ -4,7 +4,6 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
)
func (gui *Gui) getSelectedCommitFile(g *gocui.Gui) *commands.CommitFile {
@@ -36,20 +35,21 @@ func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error {
commitFile := gui.getSelectedCommitFile(g)
if commitFile == nil {
gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil
return gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
}
if err := gui.refreshSecondaryPatchPanel(); err != nil {
return err
}
v.FocusPoint(0, gui.State.Panels.CommitFiles.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.CommitFiles.SelectedLine, len(gui.State.CommitFiles), v); err != nil {
return err
}
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCommitFileCmdStr(commitFile.Sha, commitFile.Name, false),
)
if err := gui.newPtyTask("main", cmd); err != nil {
if err := gui.newCmdTask("main", cmd); err != nil {
gui.Log.Error(err)
}
@@ -112,11 +112,11 @@ func (gui *Gui) refreshCommitFilesView() error {
gui.refreshSelectedLine(&gui.State.Panels.CommitFiles.SelectedLine, len(gui.State.CommitFiles))
commitsFileView := gui.getCommitFilesView()
displayStrings := presentation.GetCommitFileListDisplayStrings(gui.State.CommitFiles)
gui.renderDisplayStrings(commitsFileView, displayStrings)
if err := gui.renderListPanel(gui.getCommitFilesView(), gui.State.CommitFiles); err != nil {
return err
}
return gui.handleCommitFileSelect(gui.g, commitsFileView)
return gui.handleCommitFileSelect(gui.g, gui.getCommitFilesView())
}
func (gui *Gui) handleOpenOldCommitFile(g *gocui.Gui, v *gocui.View) error {
@@ -131,8 +131,7 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error {
commitFile := gui.getSelectedCommitFile(g)
if commitFile == nil {
gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil
return gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
}
toggleTheFile := func() error {
@@ -187,8 +186,7 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
commitFile := gui.getSelectedCommitFile(gui.g)
if commitFile == nil {
gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil
return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
}
enterTheFile := func(selectedLineIdx int) error {
+2 -3
View File
@@ -52,7 +52,7 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
_, _ = g.SetViewOnBottom("commitMessage")
g.SetViewOnBottom("commitMessage")
return gui.switchFocus(g, v, gui.getFilesView())
}
@@ -68,8 +68,7 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error {
"keyBindConfirm": "enter",
},
)
gui.renderString(g, "options", message)
return nil
return gui.renderString(g, "options", message)
}
func (gui *Gui) getBufferLength(view *gocui.View) string {
+64 -24
View File
@@ -3,9 +3,10 @@ package gui
import (
"strconv"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -53,7 +54,9 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
return gui.newStringTask("main", gui.Tr.SLocalize("NoCommitsThisBranch"))
}
v.FocusPoint(0, gui.State.Panels.Commits.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits), v); err != nil {
return err
}
// if specific diff mode is on, don't show diff
if gui.State.Panels.Commits.SpecificDiffMode {
@@ -63,7 +66,7 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha),
)
if err := gui.newPtyTask("main", cmd); err != nil {
if err := gui.newCmdTask("main", cmd); err != nil {
gui.Log.Error(err)
}
@@ -73,7 +76,7 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
g.Update(func(*gocui.Gui) error {
// I think this is here for the sake of some kind of rebasing thing
_ = gui.refreshStatus(g)
gui.refreshStatus(g)
if err := gui.refreshCommitsWithLimit(); err != nil {
return err
@@ -117,6 +120,28 @@ func (gui *Gui) refreshCommitsWithLimit() error {
// specific functions
func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error {
return gui.createConfirmationPanel(g, commitView, true, gui.Tr.SLocalize("ResetToCommit"), gui.Tr.SLocalize("SureResetThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
commit := gui.getSelectedCommit(g)
if commit == nil {
panic(errors.New(gui.Tr.SLocalize("NoCommitsThisBranch")))
}
if err := gui.GitCommand.ResetToCommit(commit.Sha, "mixed"); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshCommits(g); err != nil {
panic(err)
}
if err := gui.refreshFiles(); err != nil {
panic(err)
}
gui.resetOrigin(commitView)
gui.State.Panels.Commits.SelectedLine = 0
return gui.handleCommitSelect(g, commitView)
}, nil)
}
func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
if len(gui.State.Commits) <= 1 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("YouNoCommitsToSquash"))
@@ -130,12 +155,23 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
return nil
}
return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Squash"), gui.Tr.SLocalize("SureSquashThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Squash"), gui.Tr.SLocalize("SureSquashThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error {
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "squash")
return gui.handleGenericMergeCommandResult(err)
})
}, nil)
return nil
}
// TODO: move to files panel
func (gui *Gui) anyUnStagedChanges(files []*commands.File) bool {
for _, file := range files {
if file.Tracked && file.HasUnstagedChanges {
return true
}
}
return false
}
func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
@@ -151,12 +187,13 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
return nil
}
return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Fixup"), gui.Tr.SLocalize("SureFixupThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Fixup"), gui.Tr.SLocalize("SureFixupThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.SLocalize("FixingStatus"), func() error {
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "fixup")
return gui.handleGenericMergeCommandResult(err)
})
}, nil)
return nil
}
func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
@@ -226,6 +263,21 @@ func (gui *Gui) handleMidRebaseCommand(action string) (bool, error) {
return true, gui.refreshCommits(gui.g)
}
// handleMoveTodoDown like handleMidRebaseCommand but for moving an item up in the todo list
func (gui *Gui) handleMoveTodoDown(index int) (bool, error) {
selectedCommit := gui.State.Commits[index]
if selectedCommit.Status != "rebasing" {
return false, nil
}
if gui.State.Commits[index+1].Status != "rebasing" {
return true, nil
}
if err := gui.GitCommand.MoveTodoDown(index); err != nil {
return true, gui.createErrorPanel(gui.g, err.Error())
}
return true, gui.refreshCommits(gui.g)
}
func (gui *Gui) handleCommitDelete(g *gocui.Gui, v *gocui.View) error {
applied, err := gui.handleMidRebaseCommand("drop")
if err != nil {
@@ -451,7 +503,7 @@ func (gui *Gui) setDiffMode() {
v.Title = gui.Tr.SLocalize("CommitsTitle")
}
_ = gui.refreshCommits(gui.g)
gui.refreshCommits(gui.g)
}
func (gui *Gui) hasCommit(commits []*commands.Commit, target string) (int, bool) {
@@ -548,8 +600,9 @@ func (gui *Gui) renderBranchCommitsWithSelection() error {
commitsView := gui.getCommitsView()
gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
displayStrings := presentation.GetCommitListDisplayStrings(gui.State.Commits, gui.State.ScreenMode != SCREEN_NORMAL)
gui.renderDisplayStrings(commitsView, displayStrings)
if err := gui.renderListPanel(commitsView, gui.State.Commits); err != nil {
return err
}
if gui.g.CurrentView() == commitsView && commitsView.Context == "branch-commits" {
if err := gui.handleCommitSelect(gui.g, commitsView); err != nil {
return err
@@ -570,9 +623,7 @@ func (gui *Gui) onCommitsTabClick(tabIndex int) error {
func (gui *Gui) switchCommitsPanelContext(context string) error {
commitsView := gui.getCommitsView()
commitsView.Context = context
if err := gui.onSearchEscape(); err != nil {
return err
}
gui.onSearchEscape()
contextTabIndexMap := map[string]int{
"branch-commits": 0,
@@ -581,13 +632,7 @@ func (gui *Gui) switchCommitsPanelContext(context string) error {
commitsView.TabIndex = contextTabIndexMap[context]
return gui.refreshCommitsViewWithSelection()
}
func (gui *Gui) refreshCommitsViewWithSelection() error {
commitsView := gui.getCommitsView()
switch commitsView.Context {
switch context {
case "branch-commits":
return gui.renderBranchCommitsWithSelection()
case "reflog-commits":
@@ -642,8 +687,3 @@ func (gui *Gui) handleOpenSearchForCommitsPanel(g *gocui.Gui, v *gocui.View) err
return gui.handleOpenSearch(gui.g, v)
}
func (gui *Gui) handleResetCherryPick(g *gocui.Gui, v *gocui.View) error {
gui.State.CherryPickedCommits = []*commands.Commit{}
return gui.refreshCommits(gui.g)
}
+10 -2
View File
@@ -122,7 +122,9 @@ func (gui *Gui) createPopupPanel(g *gocui.Gui, currentView *gocui.View, title, p
}()
}
gui.renderString(g, "confirmation", prompt)
if err := gui.renderString(g, "confirmation", prompt); err != nil {
return err
}
return gui.setKeyBindings(g, handleConfirm, handleClose, returnFocusOnClose)
})
return nil
@@ -149,13 +151,19 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go
"keyBindConfirm": "enter",
},
)
gui.renderString(g, "options", actions)
if err := gui.renderString(g, "options", actions); err != nil {
return err
}
if err := g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(handleConfirm, returnFocusOnClose)); err != nil {
return err
}
return g.SetKeybinding("confirmation", nil, gocui.KeyEsc, gocui.ModNone, gui.wrappedConfirmationFunction(handleClose, returnFocusOnClose))
}
func (gui *Gui) createMessagePanel(g *gocui.Gui, currentView *gocui.View, title, prompt string) error {
return gui.createPopupPanel(g, currentView, title, prompt, false, true, false, nil, nil)
}
// createSpecificErrorPanel allows you to create an error popup, specifying the
// view to be focused when the user closes the popup, and a boolean specifying
// whether we will log the error. If the message may include a user password,
+1
View File
@@ -16,4 +16,5 @@ func (gui *Gui) changeMainViewsContext(context string) {
}
gui.State.MainContext = context
return
}
+1 -2
View File
@@ -82,8 +82,7 @@ func (gui *Gui) handleCredentialsViewFocused(g *gocui.Gui, v *gocui.View) error
"keyBindConfirm": "enter",
},
)
gui.renderString(g, "options", message)
return nil
return gui.renderString(g, "options", message)
}
// HandleCredentialsPopup handles the views after executing a command that might ask for credentials
+2 -4
View File
@@ -23,13 +23,11 @@ type fileWatcher struct {
}
func NewFileWatcher(log *logrus.Entry) *fileWatcher {
// TODO: get this going again, and ensure we don't see any crashes from it
watcher, err := fsnotify.NewWatcher()
log.Error(err)
return &fileWatcher{
Disabled: true,
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Error(err)
return &fileWatcher{
+47 -29
View File
@@ -12,7 +12,7 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// list panel functions
@@ -37,7 +37,9 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
return gui.newStringTask("main", gui.Tr.SLocalize("NoChangedFiles"))
}
gui.getFilesView().FocusPoint(0, gui.State.Panels.Files.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, len(gui.State.Files), gui.getFilesView()); err != nil {
return err
}
if file.HasInlineMergeConflicts {
gui.getMainView().Title = gui.Tr.SLocalize("MergeConflictsTitle")
@@ -60,7 +62,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
gui.getSecondaryView().Title = gui.Tr.SLocalize("StagedChanges")
cmdStr := gui.GitCommand.DiffCmdStr(file, false, true)
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
if err := gui.newPtyTask("secondary", cmd); err != nil {
if err := gui.newCmdTask("secondary", cmd); err != nil {
return err
}
} else {
@@ -74,7 +76,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
cmdStr := gui.GitCommand.DiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges)
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
if err := gui.newPtyTask("main", cmd); err != nil {
if err := gui.newCmdTask("main", cmd); err != nil {
return err
}
@@ -101,8 +103,14 @@ func (gui *Gui) refreshFiles() error {
}
gui.g.Update(func(g *gocui.Gui) error {
displayStrings := presentation.GetFileListDisplayStrings(gui.State.Files)
gui.renderDisplayStrings(filesView, displayStrings)
filesView.Clear()
isFocused := gui.g.CurrentView().Name() == "files"
list, err := utils.RenderList(gui.State.Files, isFocused)
if err != nil {
return err
}
fmt.Fprint(filesView, list)
if g.CurrentView() == filesView || (g.CurrentView() == gui.getMainView() && g.CurrentView().Context == "merging") {
newSelectedFile, _ := gui.getSelectedFile(gui.g)
@@ -186,12 +194,9 @@ func (gui *Gui) handleFilePress(g *gocui.Gui, v *gocui.View) error {
}
if file.HasUnstagedChanges {
err = gui.GitCommand.StageFile(file.Name)
gui.GitCommand.StageFile(file.Name)
} else {
err = gui.GitCommand.UnStageFile(file.Name, file.Tracked)
}
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
gui.GitCommand.UnStageFile(file.Name, file.Tracked)
}
if err := gui.refreshFiles(); err != nil {
@@ -269,7 +274,9 @@ func (gui *Gui) handleWIPCommitPress(g *gocui.Gui, filesView *gocui.View) error
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("SkipHookPrefixNotConfigured"))
}
gui.renderString(g, "commitMessage", skipHookPreifx)
if err := gui.renderString(g, "commitMessage", skipHookPreifx); err != nil {
return err
}
if err := gui.getCommitMessageView().SetCursor(len(skipHookPreifx), 0); err != nil {
return err
}
@@ -283,14 +290,8 @@ func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
}
commitMessageView := gui.getCommitMessageView()
g.Update(func(g *gocui.Gui) error {
if _, err := g.SetViewOnTop("commitMessage"); err != nil {
return err
}
if err := gui.switchFocus(g, filesView, commitMessageView); err != nil {
return err
}
g.SetViewOnTop("commitMessage")
gui.switchFocus(g, filesView, commitMessageView)
gui.RenderCommitLength()
return nil
})
@@ -399,20 +400,24 @@ func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
// if we have no upstream branch we need to set that first
currentBranch := gui.currentBranch()
if currentBranch.Pullables == "?" {
_, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
currentBranchName, err := gui.GitCommand.CurrentBranchName()
if err != nil {
return err
}
if pullables == "?" {
// see if we have this branch in our config with an upstream
conf, err := gui.GitCommand.Repo.Config()
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
for branchName, branch := range conf.Branches {
if branchName == currentBranch.Name {
if branchName == currentBranchName {
return gui.pullFiles(v, fmt.Sprintf("%s %s", branch.Remote, branchName))
}
}
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranch.Name, func(g *gocui.Gui, v *gocui.View) error {
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranchName, func(g *gocui.Gui, v *gocui.View) error {
upstream := gui.trimmedContent(v)
if err := gui.GitCommand.SetUpstreamBranch(upstream); err != nil {
errorMessage := err.Error()
@@ -463,24 +468,28 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool, upstr
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
// if we have pullables we'll ask if the user wants to force push
currentBranch := gui.currentBranch()
_, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
currentBranchName, err := gui.GitCommand.CurrentBranchName()
if err != nil {
return err
}
if currentBranch.Pullables == "?" {
if pullables == "?" {
// see if we have this branch in our config with an upstream
conf, err := gui.GitCommand.Repo.Config()
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
for branchName, branch := range conf.Branches {
if branchName == currentBranch.Name {
if branchName == currentBranchName {
return gui.pushWithForceFlag(g, v, false, "", fmt.Sprintf("%s %s", branch.Remote, branchName))
}
}
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranch.Name, func(g *gocui.Gui, v *gocui.View) error {
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranchName, func(g *gocui.Gui, v *gocui.View) error {
return gui.pushWithForceFlag(g, v, false, gui.trimmedContent(v), "")
})
} else if currentBranch.Pullables == "0" {
} else if pullables == "0" {
return gui.pushWithForceFlag(g, v, false, "", "")
}
return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error {
@@ -506,6 +515,15 @@ func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
return gui.refreshMergePanel()
}
func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.AbortMerge(); err != nil {
return gui.createErrorPanel(g, err.Error())
}
gui.createMessagePanel(g, v, "", gui.Tr.SLocalize("MergeAborted"))
gui.refreshStatus(g)
return gui.refreshFiles()
}
func (gui *Gui) openFile(filename string) error {
if err := gui.OSCommand.OpenFile(filename); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
+28 -58
View File
@@ -80,7 +80,7 @@ type Gui struct {
GitCommand *commands.GitCommand
OSCommand *commands.OSCommand
SubProcess *exec.Cmd
State *guiState
State guiState
Config config.AppConfigurer
Tr *i18n.Localizer
Errors SentinelErrors
@@ -210,10 +210,6 @@ type guiState struct {
Searching searchingState
ScreenMode int
SideView *gocui.View
Ptmx *os.File
PrevMainWidth int
PrevMainHeight int
OldInformation string
}
// for now the split view will always be on
@@ -221,7 +217,7 @@ type guiState struct {
// NewGui builds a new gui handler
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) {
initialState := &guiState{
initialState := guiState{
Files: make([]*commands.File, 0),
PreviousView: "files",
Commits: make([]*commands.Commit, 0),
@@ -250,7 +246,6 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
},
ScreenMode: SCREEN_NORMAL,
SideView: nil,
Ptmx: nil,
}
gui := &Gui{
@@ -274,28 +269,12 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
func (gui *Gui) nextScreenMode(g *gocui.Gui, v *gocui.View) error {
gui.State.ScreenMode = utils.NextIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
// commits render differently depending on whether we're in fullscreen more or not
if err := gui.refreshCommitsViewWithSelection(); err != nil {
return err
}
// same with branches
if err := gui.refreshBranchesViewWithSelection(); err != nil {
return err
}
return nil
}
func (gui *Gui) prevScreenMode(g *gocui.Gui, v *gocui.View) error {
gui.State.ScreenMode = utils.PrevIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
// commits render differently depending on whether we're in fullscreen more or not
if err := gui.refreshCommitsViewWithSelection(); err != nil {
return err
}
// same with branches
if err := gui.refreshBranchesViewWithSelection(); err != nil {
return err
}
return nil
}
@@ -389,11 +368,17 @@ func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
return nil
}
if v.IsSearching() && newView.Name() != "search" {
if err := gui.onSearchEscape(); err != nil {
return err
}
gui.onSearchEscape()
}
switch v.Name() {
case "branches":
if v.Context == "local-branches" {
// This stops the branches panel from showing the upstream/downstream changes to the selected branch, when it loses focus
// inside renderListPanel it checks to see if the panel has focus
if err := gui.renderListPanel(gui.getBranchesView(), gui.State.Branches); err != nil {
return err
}
}
case "main":
// if we have lost focus to a first-class panel, we need to do some cleanup
gui.changeMainViewsContext("normal")
@@ -495,9 +480,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.SLocalize("Donate"))
information = donate + " " + information
}
if len(gui.State.CherryPickedCommits) > 0 {
information = utils.ColoredString(fmt.Sprintf("%d commits copied", len(gui.State.CherryPickedCommits)), color.FgCyan)
}
minimumHeight := 9
minimumWidth := 10
@@ -525,15 +507,13 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
_, _ = g.SetViewOnBottom("limit")
_ = g.DeleteView("limit")
sidePanelWidthRatio := gui.Config.GetUserConfig().GetFloat64("gui.sidePanelWidth")
g.DeleteView("limit")
textColor := theme.GocuiDefaultTextColor
var leftSideWidth int
switch gui.State.ScreenMode {
case SCREEN_NORMAL:
leftSideWidth = int(float64(width) * sidePanelWidthRatio)
leftSideWidth = width / 3
case SCREEN_HALF:
leftSideWidth = width / 2
case SCREEN_FULL:
@@ -545,6 +525,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
}
panelSplitX := width - 1
mainPanelLeft := leftSideWidth + 1
mainPanelRight := width - 1
secondaryPanelLeft := width - 1
@@ -553,7 +534,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
if gui.State.SplitMainPanel {
if gui.State.ScreenMode == SCREEN_FULL {
mainPanelLeft = 0
panelSplitX := width/2 - 4
panelSplitX = width/2 - 4
mainPanelRight = panelSplitX
secondaryPanelLeft = panelSplitX + 1
} else if width < 220 {
@@ -564,7 +545,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
units := 5
leftSideWidth = width / units
mainPanelLeft = leftSideWidth + 1
panelSplitX := (1 + ((units - 1) / 2)) * width / units
panelSplitX = (1 + ((units - 1) / 2)) * width / units
mainPanelRight = panelSplitX
secondaryPanelLeft = panelSplitX + 1
}
@@ -699,7 +680,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
if err.Error() != "unknown view" {
return err
}
_, _ = g.SetViewOnBottom("commitMessage")
g.SetViewOnBottom("commitMessage")
commitMessageView.Title = gui.Tr.SLocalize("CommitMessage")
commitMessageView.FgColor = textColor
commitMessageView.Editable = true
@@ -764,25 +745,22 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
}
informationView, err := g.SetView("information", optionsVersionBoundary-1, height-2, width, height, 0)
if err != nil {
if v, err := g.SetView("information", optionsVersionBoundary-1, height-2, width, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
informationView.BgColor = gocui.ColorDefault
informationView.FgColor = gocui.ColorGreen
informationView.Frame = false
gui.renderString(g, "information", information)
v.BgColor = gocui.ColorDefault
v.FgColor = gocui.ColorGreen
v.Frame = false
if err := gui.renderString(g, "information", information); err != nil {
return err
}
// doing this here because it'll only happen once
if err := gui.onInitialViewsCreation(); err != nil {
return err
}
}
if gui.State.OldInformation != information {
gui.setViewContent(g, informationView, information)
gui.State.OldInformation = information
}
if gui.g.CurrentView() == nil {
if _, err := gui.g.SetCurrentView(gui.getFilesView().Name()); err != nil {
@@ -821,14 +799,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
continue
}
// check if the selected line is now out of view and if so refocus it
listView.view.FocusPoint(0, listView.selectedLine)
}
mainViewWidth, mainViewHeight := gui.getMainView().Size()
if mainViewWidth != gui.State.PrevMainWidth || mainViewHeight != gui.State.PrevMainHeight {
gui.State.PrevMainWidth = mainViewWidth
gui.State.PrevMainHeight = mainViewHeight
if err := gui.onResize(); err != nil {
if err := gui.focusPoint(0, listView.selectedLine, listView.lineCount, listView.view); err != nil {
return err
}
}
@@ -916,8 +887,7 @@ func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canAskForCredentials bool) (u
_ = gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Error"), coloredMessage, close, close)
}
_ = gui.refreshStatus(g)
gui.refreshStatus(g)
return unamePassOpend, err
}
@@ -926,7 +896,7 @@ func (gui *Gui) renderGlobalOptions() error {
fmt.Sprintf("%s/%s", gui.getKeyDisplay("universal.scrollUpMain"), gui.getKeyDisplay("universal.scrollDownMain")): gui.Tr.SLocalize("scroll"),
fmt.Sprintf("%s %s %s %s", gui.getKeyDisplay("universal.prevBlock"), gui.getKeyDisplay("universal.nextBlock"), gui.getKeyDisplay("universal.prevItem"), gui.getKeyDisplay("universal.nextItem")): gui.Tr.SLocalize("navigate"),
fmt.Sprintf("%s/%s", gui.getKeyDisplay("universal.return"), gui.getKeyDisplay("universal.quit")): gui.Tr.SLocalize("close"),
gui.getKeyDisplay("universal.optionMenu"): gui.Tr.SLocalize("menu"),
fmt.Sprintf("%s", gui.getKeyDisplay("universal.optionMenu")): gui.Tr.SLocalize("menu"),
"1-5": gui.Tr.SLocalize("jump"),
})
}
@@ -965,7 +935,7 @@ func (gui *Gui) startBackgroundFetch() {
// Run setup the gui with keybindings and start the mainloop
func (gui *Gui) Run() error {
g, err := gocui.NewGui(gocui.Output256, OverlappingEdges)
g, err := gocui.NewGui(gocui.OutputNormal, OverlappingEdges)
if err != nil {
return err
}
+48 -73
View File
@@ -162,15 +162,15 @@ func (gui *Gui) getKeyDisplay(name string) string {
func GetKeyDisplay(key interface{}) string {
keyInt := 0
switch key := key.(type) {
switch key.(type) {
case rune:
keyInt = int(key)
keyInt = int(key.(rune))
case gocui.Key:
value, ok := keyMapReversed[key]
value, ok := keyMapReversed[key.(gocui.Key)]
if ok {
return value
}
keyInt = int(key)
keyInt = int(key.(gocui.Key))
}
return string(keyInt)
@@ -225,7 +225,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.scrollUpMain,
Alternative: "fn+up",
Description: gui.Tr.SLocalize("scrollUpMainPanel"),
},
{
ViewName: "",
@@ -233,7 +232,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.scrollDownMain,
Alternative: "fn+down",
Description: gui.Tr.SLocalize("scrollDownMainPanel"),
},
{
ViewName: "",
@@ -295,11 +293,10 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.SLocalize("refresh"),
},
{
ViewName: "",
Key: gui.getKey("universal.optionMenu"),
Modifier: gocui.ModNone,
Handler: gui.handleCreateOptionsMenu,
Description: gui.Tr.SLocalize("openMenu"),
ViewName: "",
Key: gui.getKey("universal.optionMenu"),
Modifier: gocui.ModNone,
Handler: gui.handleCreateOptionsMenu,
},
{
ViewName: "",
@@ -569,14 +566,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleCreateResetToBranchMenu,
Description: gui.Tr.SLocalize("viewResetOptions"),
},
{
ViewName: "branches",
Contexts: []string{"local-branches"},
Key: gui.getKey("branches.renameBranch"),
Modifier: gocui.ModNone,
Handler: gui.handleRenameBranch,
Description: gui.Tr.SLocalize("renameBranch"),
},
{
ViewName: "branches",
Contexts: []string{"tags"},
@@ -618,18 +607,16 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.SLocalize("viewResetOptions"),
},
{
ViewName: "branches",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextBranchesTab,
Description: gui.Tr.SLocalize("nextTab"),
ViewName: "branches",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextBranchesTab,
},
{
ViewName: "branches",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevBranchesTab,
Description: gui.Tr.SLocalize("prevTab"),
ViewName: "branches",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevBranchesTab,
},
{
ViewName: "branches",
@@ -656,25 +643,22 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.SLocalize("fetchRemote"),
},
{
ViewName: "commits",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextCommitsTab,
Description: gui.Tr.SLocalize("nextTab"),
ViewName: "commits",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextCommitsTab,
},
{
ViewName: "commits",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevCommitsTab,
Description: gui.Tr.SLocalize("prevTab"),
ViewName: "commits",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevCommitsTab,
},
{
ViewName: "commits",
Key: gui.getKey("universal.startSearch"),
Modifier: gocui.ModNone,
Handler: gui.handleOpenSearchForCommitsPanel,
Description: gui.Tr.SLocalize("startSearch"),
ViewName: "commits",
Key: gui.getKey("universal.startSearch"),
Modifier: gocui.ModNone,
Handler: gui.handleOpenSearchForCommitsPanel,
},
{
ViewName: "commits",
@@ -844,14 +828,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleTagCommit,
Description: gui.Tr.SLocalize("tagCommit"),
},
{
ViewName: "commits",
Contexts: []string{"branch-commits"},
Key: gui.getKey("commits.resetCherryPick"),
Modifier: gocui.ModNone,
Handler: gui.handleResetCherryPick,
Description: gui.Tr.SLocalize("resetCherryPick"),
},
{
ViewName: "commits",
Contexts: []string{"reflog-commits"},
@@ -914,18 +890,16 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleCloseCredentialsView,
},
{
ViewName: "menu",
Key: gui.getKey("universal.return"),
Modifier: gocui.ModNone,
Handler: gui.handleMenuClose,
Description: gui.Tr.SLocalize("closeMenu"),
ViewName: "menu",
Key: gui.getKey("universal.return"),
Modifier: gocui.ModNone,
Handler: gui.handleMenuClose,
},
{
ViewName: "menu",
Key: gui.getKey("universal.quit"),
Modifier: gocui.ModNone,
Handler: gui.handleMenuClose,
Description: gui.Tr.SLocalize("closeMenu"),
ViewName: "menu",
Key: gui.getKey("universal.quit"),
Modifier: gocui.ModNone,
Handler: gui.handleMenuClose,
},
{
ViewName: "information",
@@ -1039,7 +1013,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{"staging"},
Key: gui.getKey("universal.select"),
Modifier: gocui.ModNone,
Handler: gui.handleToggleStagedSelection,
Handler: gui.handleStageSelection,
Description: gui.Tr.SLocalize("StageSelection"),
},
{
@@ -1161,8 +1135,16 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{"patch-building"},
Key: gui.getKey("universal.select"),
Modifier: gocui.ModNone,
Handler: gui.handleToggleSelectionForPatch,
Description: gui.Tr.SLocalize("ToggleSelectionForPatch"),
Handler: gui.handleAddSelectionToPatch,
Description: gui.Tr.SLocalize("StageSelection"),
},
{
ViewName: "main",
Contexts: []string{"patch-building"},
Key: gui.getKey("universal.remove"),
Modifier: gocui.ModNone,
Handler: gui.handleRemoveSelectionFromPatch,
Description: gui.Tr.SLocalize("ResetSelection"),
},
{
ViewName: "main",
@@ -1478,14 +1460,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
// we need a specific keybinding for the commits panel beacuse it usually lazyloads commits
if listView.viewName != "commits" {
bindings = append(bindings, &Binding{
ViewName: listView.viewName,
Contexts: []string{listView.context},
Key: gui.getKey("universal.startSearch"),
Modifier: gocui.ModNone,
Handler: gui.handleOpenSearch,
Description: gui.Tr.SLocalize("startSearch"),
})
bindings = append(bindings, &Binding{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.startSearch"), Modifier: gocui.ModNone, Handler: gui.handleOpenSearch})
}
}
+3 -9
View File
@@ -88,8 +88,7 @@ func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, second
}
gui.g.Update(func(*gocui.Gui) error {
gui.setViewContent(gui.g, gui.getSecondaryView(), secondaryPatchParser.Render(-1, -1, nil))
return nil
return gui.setViewContent(gui.g, gui.getSecondaryView(), secondaryPatchParser.Render(-1, -1, nil))
})
return false, nil
@@ -221,10 +220,6 @@ func (gui *Gui) handleMouseScrollDown(g *gocui.Gui, v *gocui.View) error {
return gui.handleCycleLine(1)
}
func (gui *Gui) getSelectedCommitFileName() string {
return gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLine].Name
}
func (gui *Gui) refreshMainView() error {
state := gui.State.Panels.LineByLine
@@ -232,7 +227,7 @@ func (gui *Gui) refreshMainView() error {
// I'd prefer not to have knowledge of contexts using this file but I'm not sure
// how to get around this
if gui.State.MainContext == "patch-building" {
filename := gui.getSelectedCommitFileName()
filename := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLine].Name
includedLineIndices = gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
}
colorDiff := state.PatchParser.Render(state.FirstLineIdx, state.LastLineIdx, includedLineIndices)
@@ -242,8 +237,7 @@ func (gui *Gui) refreshMainView() error {
mainView.Wrap = false
gui.g.Update(func(*gocui.Gui) error {
gui.setViewContent(gui.g, gui.getMainView(), colorDiff)
return nil
return gui.setViewContent(gui.g, gui.getMainView(), colorDiff)
})
return nil
+1 -2
View File
@@ -17,8 +17,7 @@ type menuItem struct {
// list panel functions
func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error {
v.FocusPoint(0, gui.State.Panels.Menu.SelectedLine)
return nil
return gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, gui.State.MenuItemCount, v)
}
// specific functions
+6 -12
View File
@@ -151,19 +151,13 @@ func (gui *Gui) handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
if err != nil {
return err
}
if err := ioutil.WriteFile(gitFile.Name, []byte(prevContent), 0644); err != nil {
return err
}
ioutil.WriteFile(gitFile.Name, []byte(prevContent), 0644)
return gui.refreshMergePanel()
}
func (gui *Gui) handlePickHunk(g *gocui.Gui, v *gocui.View) error {
conflict := gui.State.Panels.Merging.Conflicts[gui.State.Panels.Merging.ConflictIndex]
if err := gui.pushFileSnapshot(g); err != nil {
return err
}
gui.pushFileSnapshot(g)
pick := "bottom"
if gui.State.Panels.Merging.ConflictTop {
pick = "top"
@@ -184,9 +178,7 @@ func (gui *Gui) handlePickHunk(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handlePickBothHunks(g *gocui.Gui, v *gocui.View) error {
conflict := gui.State.Panels.Merging.Conflicts[gui.State.Panels.Merging.ConflictIndex]
if err := gui.pushFileSnapshot(g); err != nil {
return err
}
gui.pushFileSnapshot(g)
err := gui.resolveConflict(g, conflict, "both")
if err != nil {
panic(err)
@@ -223,7 +215,9 @@ func (gui *Gui) refreshMergePanel() error {
mainView := gui.getMainView()
mainView.Wrap = false
gui.setViewContent(gui.g, mainView, content)
if err := gui.setViewContent(gui.g, mainView, content); err != nil {
return err
}
gui.Log.Warn("scrolling to conflict")
if err := gui.scrollToConflict(gui.g); err != nil {
return err
+27 -17
View File
@@ -2,7 +2,6 @@ package gui
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
@@ -18,8 +17,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
// get diff from commit file that's currently selected
commitFile := gui.getSelectedCommitFile(gui.g)
if commitFile == nil {
gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil
return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
}
diff, err := gui.GitCommand.ShowCommitFile(commitFile.Sha, commitFile.Name, true)
@@ -44,25 +42,38 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
return nil
}
func (gui *Gui) handleToggleSelectionForPatch(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handleAddSelectionToPatch(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.LineByLine
toggleFunc := gui.GitCommand.PatchManager.AddFileLineRange
filename := gui.getSelectedCommitFileName()
includedLineIndices := gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
currentLineIsStaged := utils.IncludesInt(includedLineIndices, state.SelectedLineIdx)
if currentLineIsStaged {
toggleFunc = gui.GitCommand.PatchManager.RemoveFileLineRange
}
// add range of lines to those set for the file
commitFile := gui.getSelectedCommitFile(gui.g)
if commitFile == nil {
gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil
return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
}
toggleFunc(commitFile.Name, state.FirstLineIdx, state.LastLineIdx)
gui.GitCommand.PatchManager.AddFileLineRange(commitFile.Name, state.FirstLineIdx, state.LastLineIdx)
if err := gui.refreshCommitFilesView(); err != nil {
return err
}
if err := gui.refreshPatchBuildingPanel(-1); err != nil {
return err
}
return nil
}
func (gui *Gui) handleRemoveSelectionFromPatch(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.LineByLine
// add range of lines to those set for the file
commitFile := gui.getSelectedCommitFile(gui.g)
if commitFile == nil {
return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
}
gui.GitCommand.PatchManager.RemoveFileLineRange(commitFile.Name, state.FirstLineIdx, state.LastLineIdx)
if err := gui.refreshCommitFilesView(); err != nil {
return err
@@ -94,8 +105,7 @@ func (gui *Gui) refreshSecondaryPatchPanel() error {
secondaryView.Wrap = false
gui.g.Update(func(*gocui.Gui) error {
gui.setViewContent(gui.g, gui.getSecondaryView(), gui.GitCommand.PatchManager.RenderAggregatedPatchColored(false))
return nil
return gui.setViewContent(gui.g, gui.getSecondaryView(), gui.GitCommand.PatchManager.RenderAggregatedPatchColored(false))
})
} else {
gui.State.SplitMainPanel = false
-61
View File
@@ -1,61 +0,0 @@
package presentation
import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetBranchListDisplayStrings(branches []*commands.Branch, fullDescription bool) [][]string {
lines := make([][]string, len(branches))
for i := range branches {
lines[i] = getBranchDisplayStrings(branches[i], fullDescription)
}
return lines
}
// getBranchDisplayStrings returns the display string of branch
func getBranchDisplayStrings(b *commands.Branch, fullDescription bool) []string {
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name))
if b.Pushables != "" && b.Pullables != "" && b.Pushables != "?" && b.Pullables != "?" {
trackColor := color.FgYellow
if b.Pushables == "0" && b.Pullables == "0" {
trackColor = color.FgGreen
}
track := utils.ColoredString(fmt.Sprintf("↑%s↓%s", b.Pushables, b.Pullables), trackColor)
displayName = fmt.Sprintf("%s %s", displayName, track)
}
recencyColor := color.FgCyan
if b.Recency == " *" {
recencyColor = color.FgGreen
}
if fullDescription {
return []string{utils.ColoredString(b.Recency, recencyColor), displayName, utils.ColoredString(b.UpstreamName, color.FgYellow)}
}
return []string{utils.ColoredString(b.Recency, recencyColor), displayName}
}
// GetBranchColor branch color
func GetBranchColor(name string) color.Attribute {
branchType := strings.Split(name, "/")[0]
switch branchType {
case "feature":
return color.FgGreen
case "bugfix":
return color.FgYellow
case "hotfix":
return color.FgRed
default:
return theme.DefaultTextColor
}
}
-35
View File
@@ -1,35 +0,0 @@
package presentation
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
)
func GetCommitFileListDisplayStrings(branches []*commands.CommitFile) [][]string {
lines := make([][]string, len(branches))
for i := range branches {
lines[i] = getCommitFileDisplayStrings(branches[i])
}
return lines
}
// getCommitFileDisplayStrings returns the display string of branch
func getCommitFileDisplayStrings(f *commands.CommitFile) []string {
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
defaultColor := color.New(theme.DefaultTextColor)
var colour *color.Color
switch f.Status {
case commands.UNSELECTED:
colour = defaultColor
case commands.WHOLE:
colour = green
case commands.PART:
colour = yellow
}
return []string{colour.Sprint(f.DisplayString)}
}
-126
View File
@@ -1,126 +0,0 @@
package presentation
import (
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool) [][]string {
lines := make([][]string, len(commits))
var displayFunc func(*commands.Commit) []string
if fullDescription {
displayFunc = getFullDescriptionDisplayStringsForCommit
} else {
displayFunc = getDisplayStringsForCommit
}
for i := range commits {
lines[i] = displayFunc(commits[i])
}
return lines
}
func getFullDescriptionDisplayStringsForCommit(c *commands.Commit) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor)
magenta := color.New(color.FgMagenta)
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)
var shaColor *color.Color
switch c.Status {
case "unpushed":
shaColor = red
case "pushed":
shaColor = yellow
case "merged":
shaColor = green
case "rebasing":
shaColor = blue
case "reflog":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = defaultColor
}
if c.Copied {
shaColor = copied
}
tagString := ""
truncatedDate := utils.TruncateWithEllipsis(c.Date, 15)
secondColumnString := blue.Sprint(truncatedDate)
if c.Action != "" {
secondColumnString = cyan.Sprint(c.Action)
} else if c.ExtraInfo != "" {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(c.ExtraInfo, tagColor) + " "
}
truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)
return []string{shaColor.Sprint(c.Sha[:8]), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(c.Name)}
}
func getDisplayStringsForCommit(c *commands.Commit) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor)
magenta := color.New(color.FgMagenta)
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)
var shaColor *color.Color
switch c.Status {
case "unpushed":
shaColor = red
case "pushed":
shaColor = yellow
case "merged":
shaColor = green
case "rebasing":
shaColor = blue
case "reflog":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = defaultColor
}
if c.Copied {
shaColor = copied
}
actionString := ""
tagString := ""
if c.Action != "" {
actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
} else if len(c.Tags) > 0 {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(strings.Join(c.Tags, " "), tagColor) + " "
}
return []string{shaColor.Sprint(c.Sha[:8]), actionString + tagString + defaultColor.Sprint(c.Name)}
}
-36
View File
@@ -1,36 +0,0 @@
package presentation
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
)
func GetFileListDisplayStrings(files []*commands.File) [][]string {
lines := make([][]string, len(files))
for i := range files {
lines[i] = getFileDisplayStrings(files[i])
}
return lines
}
// getFileDisplayStrings returns the display string of branch
func getFileDisplayStrings(f *commands.File) []string {
// potentially inefficient to be instantiating these color
// objects with each render
red := color.New(color.FgRed)
green := color.New(color.FgGreen)
if !f.Tracked && !f.HasStagedChanges {
return []string{red.Sprint(f.DisplayString)}
}
output := green.Sprint(f.DisplayString[0:1])
output += red.Sprint(f.DisplayString[1:3])
if f.HasUnstagedChanges {
output += red.Sprint(f.Name)
} else {
output += green.Sprint(f.Name)
}
return []string{output}
}
-23
View File
@@ -1,23 +0,0 @@
package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetRemoteBranchListDisplayStrings(branches []*commands.RemoteBranch) [][]string {
lines := make([][]string, len(branches))
for i := range branches {
lines[i] = getRemoteBranchDisplayStrings(branches[i])
}
return lines
}
// getRemoteBranchDisplayStrings returns the display string of branch
func getRemoteBranchDisplayStrings(b *commands.RemoteBranch) []string {
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name))
return []string{displayName}
}
-20
View File
@@ -1,20 +0,0 @@
package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands"
)
func GetRemoteListDisplayStrings(remotes []*commands.Remote) [][]string {
lines := make([][]string, len(remotes))
for i := range remotes {
lines[i] = getRemoteDisplayStrings(remotes[i])
}
return lines
}
// getRemoteDisplayStrings returns the display string of branch
func getRemoteDisplayStrings(r *commands.Remote) []string {
return []string{r.Name}
}
-20
View File
@@ -1,20 +0,0 @@
package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands"
)
func GetStashEntryListDisplayStrings(stashEntries []*commands.StashEntry) [][]string {
lines := make([][]string, len(stashEntries))
for i := range stashEntries {
lines[i] = getStashEntryDisplayStrings(stashEntries[i])
}
return lines
}
// getStashEntryDisplayStrings returns the display string of branch
func getStashEntryDisplayStrings(s *commands.StashEntry) []string {
return []string{s.DisplayString}
}
-20
View File
@@ -1,20 +0,0 @@
package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands"
)
func GetTagListDisplayStrings(tags []*commands.Tag) [][]string {
lines := make([][]string, len(tags))
for i := range tags {
lines[i] = getTagDisplayStrings(tags[i])
}
return lines
}
// getTagDisplayStrings returns the display string of branch
func getTagDisplayStrings(t *commands.Tag) []string {
return []string{t.Name}
}
-74
View File
@@ -1,74 +0,0 @@
// +build !windows
package gui
import (
"os/exec"
"github.com/jesseduffield/pty"
)
func (gui *Gui) onResize() error {
if gui.State.Ptmx == nil {
return nil
}
mainView := gui.getMainView()
width, height := mainView.Size()
if err := pty.Setsize(gui.State.Ptmx, &pty.Winsize{Cols: uint16(width), Rows: uint16(height)}); err != nil {
return err
}
// TODO: handle resizing properly
return nil
}
// Some commands need to output for a terminal to active certain behaviour.
// For example, git won't invoke the GIT_PAGER env var unless it thinks it's
// talking to a terminal. We typically write cmd outputs straight to a view,
// which is just an io.Reader. the pty package lets us wrap a command in a
// pseudo-terminal meaning we'll get the behaviour we want from the underlying
// command.
func (gui *Gui) newPtyTask(viewName string, cmd *exec.Cmd) error {
width, _ := gui.getMainView().Size()
pager := gui.GitCommand.GetPager(width)
if pager == "" {
// if we're not using a custom pager we don't need to use a pty
return gui.newCmdTask(viewName, cmd)
}
cmd.Env = append(cmd.Env, "GIT_PAGER="+pager)
view, err := gui.g.View(viewName)
if err != nil {
return nil // swallowing for now
}
_, height := view.Size()
_, oy := view.Origin()
manager := gui.getManager(view)
ptmx, err := pty.Start(cmd)
if err != nil {
return err
}
gui.State.Ptmx = ptmx
onClose := func() {
ptmx.Close()
gui.State.Ptmx = nil
}
if err := gui.onResize(); err != nil {
return err
}
if err := manager.NewTask(manager.NewCmdTask(ptmx, cmd, height+oy+10, onClose)); err != nil {
return err
}
return nil
}
-13
View File
@@ -1,13 +0,0 @@
// +build windows
package gui
import "os/exec"
func (gui *Gui) onResize() error {
return nil
}
func (gui *Gui) newPtyTask(viewName string, cmd *exec.Cmd) error {
return gui.newCmdTask(viewName, cmd)
}
-2
View File
@@ -16,8 +16,6 @@ func (gui *Gui) handleCreateRebaseOptionsMenu(g *gocui.Gui, v *gocui.View) error
menuItems := make([]*menuItem, len(options))
for i, option := range options {
// note to self. Never, EVER, close over loop variables in a function
option := option
menuItems[i] = &menuItem{
displayString: option,
onPress: func() error {
+7 -5
View File
@@ -3,7 +3,6 @@ package gui
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
)
// list panel functions
@@ -34,12 +33,14 @@ func (gui *Gui) handleReflogCommitSelect(g *gocui.Gui, v *gocui.View) error {
if commit == nil {
return gui.newStringTask("main", "No reflog history")
}
v.FocusPoint(0, gui.State.Panels.ReflogCommits.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.ReflogCommits.SelectedLine, len(gui.State.ReflogCommits), v); err != nil {
return err
}
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha),
)
if err := gui.newPtyTask("main", cmd); err != nil {
if err := gui.newCmdTask("main", cmd); err != nil {
gui.Log.Error(err)
}
@@ -65,8 +66,9 @@ func (gui *Gui) renderReflogCommitsWithSelection() error {
commitsView := gui.getCommitsView()
gui.refreshSelectedLine(&gui.State.Panels.ReflogCommits.SelectedLine, len(gui.State.ReflogCommits))
displayStrings := presentation.GetCommitListDisplayStrings(gui.State.ReflogCommits, gui.State.ScreenMode != SCREEN_NORMAL)
gui.renderDisplayStrings(commitsView, displayStrings)
if err := gui.renderListPanel(commitsView, gui.State.ReflogCommits); err != nil {
return err
}
if gui.g.CurrentView() == commitsView && commitsView.Context == "reflog-commits" {
if err := gui.handleReflogCommitSelect(gui.g, commitsView); err != nil {
return err
+7 -4
View File
@@ -5,7 +5,6 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
)
// list panel functions
@@ -38,7 +37,10 @@ func (gui *Gui) handleRemoteBranchSelect(g *gocui.Gui, v *gocui.View) error {
return gui.newStringTask("main", "No branches for this remote")
}
v.FocusPoint(0, gui.State.Panels.RemoteBranches.SelectedLine)
gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, gui.State.MenuItemCount, v)
if err := gui.focusPoint(0, gui.State.Panels.RemoteBranches.SelectedLine, len(gui.State.RemoteBranches), v); err != nil {
return err
}
branchName := fmt.Sprintf("%s/%s", remote.Name, remoteBranch.Name)
@@ -60,8 +62,9 @@ func (gui *Gui) renderRemoteBranchesWithSelection() error {
branchesView := gui.getBranchesView()
gui.refreshSelectedLine(&gui.State.Panels.RemoteBranches.SelectedLine, len(gui.State.RemoteBranches))
displayStrings := presentation.GetRemoteBranchListDisplayStrings(gui.State.RemoteBranches)
gui.renderDisplayStrings(branchesView, displayStrings)
if err := gui.renderListPanel(branchesView, gui.State.RemoteBranches); err != nil {
return err
}
if gui.g.CurrentView() == branchesView && branchesView.Context == "remote-branches" {
if err := gui.handleRemoteBranchSelect(gui.g, branchesView); err != nil {
return err
+6 -6
View File
@@ -7,7 +7,6 @@ import (
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -39,7 +38,9 @@ func (gui *Gui) handleRemoteSelect(g *gocui.Gui, v *gocui.View) error {
if remote == nil {
return gui.newStringTask("main", "No remotes")
}
v.FocusPoint(0, gui.State.Panels.Remotes.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.Remotes.SelectedLine, len(gui.State.Remotes), v); err != nil {
return err
}
return gui.newStringTask("main", fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
}
@@ -79,10 +80,9 @@ func (gui *Gui) renderRemotesWithSelection() error {
branchesView := gui.getBranchesView()
gui.refreshSelectedLine(&gui.State.Panels.Remotes.SelectedLine, len(gui.State.Remotes))
displayStrings := presentation.GetRemoteListDisplayStrings(gui.State.Remotes)
gui.renderDisplayStrings(branchesView, displayStrings)
if err := gui.renderListPanel(branchesView, gui.State.Remotes); err != nil {
return err
}
if gui.g.CurrentView() == branchesView && branchesView.Context == "remotes" {
if err := gui.handleRemoteSelect(gui.g, branchesView); err != nil {
return err
+1 -7
View File
@@ -23,10 +23,7 @@ func (gui *Gui) createResetMenu(ref string) error {
return gui.createErrorPanel(gui.g, err.Error())
}
if err := gui.switchCommitsPanelContext("branch-commits"); err != nil {
return err
}
gui.switchCommitsPanelContext("branch-commits")
gui.State.Panels.Commits.SelectedLine = 0
gui.State.Panels.ReflogCommits.SelectedLine = 0
@@ -36,9 +33,6 @@ func (gui *Gui) createResetMenu(ref string) error {
if err := gui.refreshFiles(); err != nil {
return err
}
if err := gui.refreshBranches(gui.g); err != nil {
return err
}
if err := gui.resetOrigin(gui.getCommitsView()); err != nil {
return err
}
+5 -8
View File
@@ -12,19 +12,14 @@ func (gui *Gui) handleOpenSearch(g *gocui.Gui, v *gocui.View) error {
gui.State.Searching.isSearching = true
gui.State.Searching.view = v
gui.renderString(gui.g, "search", "")
if err := gui.switchFocus(gui.g, v, gui.getSearchView()); err != nil {
return err
}
gui.switchFocus(gui.g, v, gui.getSearchView())
return nil
}
func (gui *Gui) handleSearch(g *gocui.Gui, v *gocui.View) error {
gui.State.Searching.searchString = gui.getSearchView().Buffer()
if err := gui.switchFocus(gui.g, nil, gui.State.Searching.view); err != nil {
return err
}
gui.switchFocus(gui.g, nil, gui.State.Searching.view)
if err := gui.State.Searching.view.Search(gui.State.Searching.searchString); err != nil {
return err
}
@@ -90,5 +85,7 @@ func (gui *Gui) handleSearchEscape(g *gocui.Gui, v *gocui.View) error {
return err
}
return gui.onSearchEscape()
gui.onSearchEscape()
return nil
}
+21 -14
View File
@@ -99,27 +99,26 @@ func (gui *Gui) handleStagingEscape(g *gocui.Gui, v *gocui.View) error {
return gui.switchFocus(gui.g, nil, gui.getFilesView())
}
func (gui *Gui) handleToggleStagedSelection(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.LineByLine
return gui.applySelection(state.SecondaryFocused)
func (gui *Gui) handleStageSelection(g *gocui.Gui, v *gocui.View) error {
return gui.applySelectionWithPrompt(false)
}
func (gui *Gui) handleResetSelection(g *gocui.Gui, v *gocui.View) error {
return gui.applySelectionWithPrompt(true)
}
func (gui *Gui) applySelectionWithPrompt(reverse bool) error {
state := gui.State.Panels.LineByLine
if state.SecondaryFocused {
// for backwards compatibility
return gui.applySelection(true)
if !reverse && state.SecondaryFocused {
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("CantStageStaged"))
} else if reverse && !state.SecondaryFocused && !gui.Config.GetUserConfig().GetBool("gui.skipUnstageLineWarning") {
return gui.createConfirmationPanel(gui.g, gui.getMainView(), false, "unstage lines", "Are you sure you want to unstage these lines? It is irreversible.\nTo disable this dialogue set the config key of 'gui.skipUnstageLineWarning' to true", func(*gocui.Gui, *gocui.View) error {
return gui.applySelection(reverse)
}, nil)
}
if gui.Config.GetUserConfig().GetBool("gui.skipUnstageLineWarning") {
return gui.createConfirmationPanel(gui.g, gui.getMainView(), false, "unstage lines", "Are you sure you want to delete the selected lines (git reset)? It is irreversible.\nTo disable this dialogue set the config key of 'gui.skipUnstageLineWarning' to true", func(*gocui.Gui, *gocui.View) error {
return gui.applySelection(true)
}, nil)
} else {
return gui.applySelection(true)
}
return gui.applySelection(reverse)
}
func (gui *Gui) applySelection(reverse bool) error {
@@ -159,3 +158,11 @@ func (gui *Gui) applySelection(reverse bool) error {
}
return nil
}
func (gui *Gui) handleMouseDownSecondaryWhileStaging(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.LineByLine
state.SecondaryFocused = !state.SecondaryFocused
return gui.refreshStagingPanel(false, -1)
}
+20 -15
View File
@@ -1,9 +1,11 @@
package gui
import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// list panel functions
@@ -34,12 +36,14 @@ func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
if stashEntry == nil {
return gui.newStringTask("main", gui.Tr.SLocalize("NoStashEntries"))
}
v.FocusPoint(0, gui.State.Panels.Stash.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries), v); err != nil {
return err
}
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
)
if err := gui.newPtyTask("main", cmd); err != nil {
if err := gui.newCmdTask("main", cmd); err != nil {
gui.Log.Error(err)
}
@@ -52,12 +56,17 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
gui.refreshSelectedLine(&gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries))
stashView := gui.getStashView()
isFocused := gui.g.CurrentView().Name() == "stash"
list, err := utils.RenderList(gui.State.StashEntries, isFocused)
if err != nil {
return err
}
displayStrings := presentation.GetStashEntryListDisplayStrings(gui.State.StashEntries)
gui.renderDisplayStrings(stashView, displayStrings)
v := gui.getStashView()
v.Clear()
fmt.Fprint(v, list)
if err := gui.resetOrigin(stashView); err != nil {
if err := gui.resetOrigin(v); err != nil {
return err
}
return nil
@@ -95,11 +104,9 @@ func (gui *Gui) stashDo(g *gocui.Gui, v *gocui.View, method string) error {
return gui.createErrorPanel(g, errorMessage)
}
if err := gui.GitCommand.StashDo(stashEntry.Index, method); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshStashEntries(g); err != nil {
return gui.createErrorPanel(g, err.Error())
gui.createErrorPanel(g, err.Error())
}
gui.refreshStashEntries(g)
return gui.refreshFiles()
}
@@ -109,11 +116,9 @@ func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
}
return gui.createPromptPanel(gui.g, gui.getFilesView(), gui.Tr.SLocalize("StashChanges"), "", func(g *gocui.Gui, v *gocui.View) error {
if err := stashFunc(gui.trimmedContent(v)); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshStashEntries(g); err != nil {
return gui.createErrorPanel(g, err.Error())
gui.createErrorPanel(g, err.Error())
}
gui.refreshStashEntries(g)
return gui.refreshFiles()
})
}
+11 -20
View File
@@ -6,7 +6,7 @@ import (
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -22,20 +22,11 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
// contents end up cleared
g.Update(func(*gocui.Gui) error {
v.Clear()
// TODO: base this off of the current branch
state.pushables, state.pullables = gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
if err := gui.updateWorkTreeState(); err != nil {
return err
}
trackColor := color.FgYellow
if state.pushables == "0" && state.pullables == "0" {
trackColor = color.FgGreen
} else if state.pushables == "?" && state.pullables == "?" {
trackColor = color.FgRed
}
status := utils.ColoredString(fmt.Sprintf("↑%s↓%s", state.pushables, state.pullables), trackColor)
status := fmt.Sprintf("↑%s↓%s", state.pushables, state.pullables)
branches := gui.State.Branches
if gui.State.WorkingTreeState != "normal" {
@@ -44,7 +35,7 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
if len(branches) > 0 {
branch := branches[0]
name := utils.ColoredString(branch.Name, presentation.GetBranchColor(branch.Name))
name := utils.ColoredString(branch.Name, commands.GetBranchColor(branch.Name))
repoName := utils.GetCurrentRepoName()
status += fmt.Sprintf(" %s → %s", repoName, name)
}
@@ -145,14 +136,6 @@ func lazygitTitle() string {
}
func (gui *Gui) updateWorkTreeState() error {
rebaseMode, err := gui.GitCommand.RebaseMode()
if err != nil {
return err
}
if rebaseMode != "" {
gui.State.WorkingTreeState = "rebasing"
return nil
}
merging, err := gui.GitCommand.IsInMergeState()
if err != nil {
return err
@@ -161,6 +144,14 @@ func (gui *Gui) updateWorkTreeState() error {
gui.State.WorkingTreeState = "merging"
return nil
}
rebaseMode, err := gui.GitCommand.RebaseMode()
if err != nil {
return err
}
if rebaseMode != "" {
gui.State.WorkingTreeState = "rebasing"
return nil
}
gui.State.WorkingTreeState = "normal"
return nil
}
+9 -19
View File
@@ -3,7 +3,6 @@ package gui
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
)
// list panel functions
@@ -34,7 +33,9 @@ func (gui *Gui) handleTagSelect(g *gocui.Gui, v *gocui.View) error {
if tag == nil {
return gui.newStringTask("main", "No tags")
}
v.FocusPoint(0, gui.State.Panels.Tags.SelectedLine)
if err := gui.focusPoint(0, gui.State.Panels.Tags.SelectedLine, len(gui.State.Tags), v); err != nil {
return err
}
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(tag.Name),
@@ -55,7 +56,7 @@ func (gui *Gui) refreshTags() error {
gui.State.Tags = tags
if gui.getBranchesView().Context == "tags" {
return gui.renderTagsWithSelection()
gui.renderTagsWithSelection()
}
return nil
@@ -65,8 +66,9 @@ func (gui *Gui) renderTagsWithSelection() error {
branchesView := gui.getBranchesView()
gui.refreshSelectedLine(&gui.State.Panels.Tags.SelectedLine, len(gui.State.Tags))
displayStrings := presentation.GetTagListDisplayStrings(gui.State.Tags)
gui.renderDisplayStrings(branchesView, displayStrings)
if err := gui.renderListPanel(branchesView, gui.State.Tags); err != nil {
return err
}
if gui.g.CurrentView() == branchesView && branchesView.Context == "tags" {
if err := gui.handleTagSelect(gui.g, branchesView); err != nil {
return err
@@ -104,13 +106,7 @@ func (gui *Gui) handleDeleteTag(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.DeleteTag(tag.Name); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
if err := gui.refreshCommits(g); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshTags(); err != nil {
return gui.createErrorPanel(g, err.Error())
}
return nil
return gui.refreshTags()
}, nil)
}
@@ -141,13 +137,7 @@ func (gui *Gui) handleCreateTag(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.CreateLightweightTag(v.Buffer(), ""); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
if err := gui.refreshCommits(g); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshTags(); err != nil {
return gui.createErrorPanel(g, err.Error())
}
return nil
return gui.refreshTags()
})
}
+3 -17
View File
@@ -18,17 +18,7 @@ func (gui *Gui) newCmdTask(viewName string, cmd *exec.Cmd) error {
manager := gui.getManager(view)
r, err := cmd.StdoutPipe()
if err != nil {
return err
}
cmd.Stderr = cmd.Stdout
if err := cmd.Start(); err != nil {
return err
}
if err := manager.NewTask(manager.NewCmdTask(r, cmd, height+oy+10, nil)); err != nil {
if err := manager.NewTask(manager.NewCmdTask(cmd, height+oy+10)); err != nil {
return err
}
@@ -59,8 +49,7 @@ func (gui *Gui) newStringTask(viewName string, str string) error {
manager := gui.getManager(view)
f := func(stop chan struct{}) error {
gui.renderString(gui.g, viewName, str)
return nil
return gui.renderString(gui.g, viewName, str)
}
if err := manager.NewTask(f); err != nil {
@@ -80,10 +69,7 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
view.Clear()
},
func() {
gui.g.Update(func(*gocui.Gui) error {
gui.Log.Warn("updating view")
return nil
})
gui.g.Update(func(*gocui.Gui) error { return nil })
})
gui.viewBufferManagerMap[view.Name()] = manager
}
+3 -1
View File
@@ -47,7 +47,9 @@ func (gui *Gui) startUpdating(newVersion string) {
func (gui *Gui) onUpdateFinish(err error) error {
gui.State.Updating = false
gui.statusManager.removeStatus("updating")
gui.renderString(gui.g, "appStatus", "")
if err := gui.renderString(gui.g, "appStatus", ""); err != nil {
return err
}
if err != nil {
return gui.createErrorPanel(gui.g, "Update failed: "+err.Error())
}
+40 -8
View File
@@ -218,18 +218,25 @@ func (gui *Gui) resetOrigin(v *gocui.View) error {
return v.SetOrigin(0, 0)
}
// if the cursor down past the last item, move it to the last line
func (gui *Gui) focusPoint(cx int, cy int, lineCount int, v *gocui.View) error {
v.FocusPoint(cx, cy)
return nil
}
func (gui *Gui) cleanString(s string) string {
output := string(bom.Clean([]byte(s)))
return utils.NormalizeLinefeeds(output)
}
func (gui *Gui) setViewContent(g *gocui.Gui, v *gocui.View, s string) {
func (gui *Gui) setViewContent(g *gocui.Gui, v *gocui.View, s string) error {
v.Clear()
fmt.Fprint(v, gui.cleanString(s))
return nil
}
// renderString resets the origin of a view and sets its content
func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) {
func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error {
g.Update(func(*gocui.Gui) error {
v, err := g.View(viewName)
if err != nil {
@@ -241,9 +248,9 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) {
if err := v.SetCursor(0, 0); err != nil {
return err
}
gui.setViewContent(gui.g, v, s)
return nil
return gui.setViewContent(gui.g, v, s)
})
return nil
}
func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
@@ -256,8 +263,7 @@ func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
}
func (gui *Gui) renderOptionsMap(optionsMap map[string]string) error {
gui.renderString(gui.g, "options", gui.optionsMapToString(optionsMap))
return nil
return gui.renderString(gui.g, "options", gui.optionsMapToString(optionsMap))
}
// TODO: refactor properly
@@ -343,6 +349,22 @@ func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error {
return err
}
// generalFocusLine takes a lineNumber to focus, and a bottomLine to ensure we can see
func (gui *Gui) generalFocusLine(lineNumber int, bottomLine int, v *gocui.View) error {
_, height := v.Size()
overScroll := bottomLine - height + 1
if overScroll < 0 {
overScroll = 0
}
if err := v.SetOrigin(0, overScroll); err != nil {
return err
}
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
return err
}
return nil
}
func (gui *Gui) changeSelectedLine(line *int, total int, change int) {
// TODO: find out why we're doing this
if *line == -1 {
@@ -365,13 +387,18 @@ func (gui *Gui) refreshSelectedLine(line *int, total int) {
}
}
func (gui *Gui) renderDisplayStrings(v *gocui.View, displayStrings [][]string) {
func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error {
gui.g.Update(func(g *gocui.Gui) error {
list := utils.RenderDisplayStrings(displayStrings)
isFocused := gui.g.CurrentView().Name() == v.Name()
list, err := utils.RenderList(items, isFocused)
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
v.Clear()
fmt.Fprint(v, list)
return nil
})
return nil
}
func (gui *Gui) renderPanelOptions() error {
@@ -387,6 +414,11 @@ func (gui *Gui) renderPanelOptions() error {
return gui.renderGlobalOptions()
}
func (gui *Gui) handleFocusView(g *gocui.Gui, v *gocui.View) error {
_, err := gui.g.SetCurrentView(v.Name())
return err
}
func (gui *Gui) isPopupPanel(viewName string) bool {
return viewName == "commitMessage" || viewName == "credentials" || viewName == "confirmation" || viewName == "menu"
}
+3 -93
View File
@@ -501,19 +501,16 @@ func addEnglish(i18nObject *i18n.Bundle) error {
Other: `select hunk`,
}, &i18n.Message{
ID: "StageSelection",
Other: `toggle line staged / unstaged`,
Other: `stage selection`,
}, &i18n.Message{
ID: "ResetSelection",
Other: `delete change (git reset)`,
Other: `reset selection`,
}, &i18n.Message{
ID: "ToggleDragSelect",
Other: `toggle drag select`,
}, &i18n.Message{
ID: "ToggleSelectHunk",
Other: `toggle select hunk`,
}, &i18n.Message{
ID: "ToggleSelectionForPatch",
Other: `add/remove line(s) to patch`,
},
&i18n.Message{
ID: "TogglePanel",
@@ -567,54 +564,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "RebaseOptionsTitle",
Other: "Rebase Options",
}, &i18n.Message{
ID: "CommitMessageTitle",
Other: "Commit Message",
}, &i18n.Message{
ID: "Local-BranchesTitle",
Other: "Branches Tab",
}, &i18n.Message{
ID: "SearchTitle",
Other: "Search",
}, &i18n.Message{
ID: "TagsTitle",
Other: "Tags Tab",
}, &i18n.Message{
ID: "Branch-CommitsTitle",
Other: "Commits Tab",
}, &i18n.Message{
ID: "MenuTitle",
Other: "Menu",
}, &i18n.Message{
ID: "RemotesTitle",
Other: "Remotes Tab",
}, &i18n.Message{
ID: "CredentialsTitle",
Other: "Credentials",
}, &i18n.Message{
ID: "Remote-BranchesTitle",
Other: "Remote Branches (in Remotes tab)",
}, &i18n.Message{
ID: "Patch-BuildingTitle",
Other: "Patch Building",
}, &i18n.Message{
ID: "InformationTitle",
Other: "Information",
}, &i18n.Message{
ID: "SecondaryTitle",
Other: "Secondary",
}, &i18n.Message{
ID: "Reflog-CommitsTitle",
Other: "Reflog Tab",
}, &i18n.Message{
ID: "Title",
Other: "Title",
}, &i18n.Message{
ID: "GlobalTitle",
Other: "Global Keybindings",
}, &i18n.Message{
ID: "MerginTitle",
Other: "Mergin",
}, &i18n.Message{
ID: "ConflictsResolved",
Other: "all merge conflicts resolved. Continue?",
@@ -702,12 +651,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "ScrollUp",
Other: "scroll up",
}, &i18n.Message{
ID: "scrollUpMainPanel",
Other: "scroll up main panel",
}, &i18n.Message{
ID: "scrollDownMainPanel",
Other: "scroll down main panel",
}, &i18n.Message{
ID: "AmendCommitTitle",
Other: "Amend Commit",
@@ -749,7 +692,7 @@ func addEnglish(i18nObject *i18n.Bundle) error {
Other: "view commit's files",
}, &i18n.Message{
ID: "CommitFilesTitle",
Other: "Commit Files",
Other: "Commit files",
}, &i18n.Message{
ID: "goBack",
Other: "go back",
@@ -1008,39 +951,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "prevScreenMode",
Other: "prev screen mode",
}, &i18n.Message{
ID: "startSearch",
Other: "start search",
}, &i18n.Message{
ID: "Panel",
Other: "Panel",
}, &i18n.Message{
ID: "Keybindings",
Other: "Keybindings",
}, &i18n.Message{
ID: "renameBranch",
Other: "rename branch",
}, &i18n.Message{
ID: "NewBranchNamePrompt",
Other: "Enter new branch name for branch",
}, &i18n.Message{
ID: "RenameBranchWarning",
Other: "This branch is tracking a remote. This action will only rename the local branch name, not the name of the remote branch. Continue?",
}, &i18n.Message{
ID: "openMenu",
Other: "open menu",
}, &i18n.Message{
ID: "closeMenu",
Other: "close menu",
}, &i18n.Message{
ID: "resetCherryPick",
Other: "reset cherry-picked (copied) commits selection",
}, &i18n.Message{
ID: "nextTab",
Other: "next tab",
}, &i18n.Message{
ID: "prevTab",
Other: "previous tab",
},
)
}
+30 -27
View File
@@ -8,7 +8,6 @@ import (
"sync"
"time"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/sirupsen/logrus"
)
@@ -23,6 +22,7 @@ type Task struct {
type ViewBufferManager struct {
writer io.Writer
waitingTask *Task
currentTask *Task
waitingMutex sync.Mutex
taskIDMutex sync.Mutex
@@ -45,20 +45,27 @@ func (m *ViewBufferManager) ReadLines(n int) {
}()
}
func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, linesToRead int, onDone func()) func(chan struct{}) error {
func (m *ViewBufferManager) NewCmdTask(cmd *exec.Cmd, linesToRead int) func(chan struct{}) error {
return func(stop chan struct{}) error {
r, err := cmd.StdoutPipe()
if err != nil {
return err
}
cmd.Stderr = cmd.Stdout
if err := cmd.Start(); err != nil {
return err
}
go func() {
<-stop
if err := commands.Kill(cmd); err != nil {
m.Log.Warn(err)
}
if onDone != nil {
onDone()
if cmd.ProcessState == nil {
if err := kill(cmd); err != nil {
m.Log.Warn(err)
}
}
}()
loadingMutex := sync.Mutex{}
// not sure if it's the right move to redefine this or not
m.readLines = make(chan int, 1024)
@@ -75,13 +82,11 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, linesToRead i
defer ticker.Stop()
select {
case <-ticker.C:
loadingMutex.Lock()
if !loaded {
m.beforeStart()
_, _ = m.writer.Write([]byte("loading..."))
m.writer.Write([]byte("loading..."))
m.refreshView()
}
loadingMutex.Unlock()
case <-stop:
return
}
@@ -93,16 +98,13 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, linesToRead i
case linesToRead := <-m.readLines:
for i := 0; i < linesToRead; i++ {
ok := scanner.Scan()
loadingMutex.Lock()
if !loaded {
m.beforeStart()
loaded = true
}
loadingMutex.Unlock()
select {
case <-stop:
m.refreshView()
break outer
default:
}
@@ -110,11 +112,10 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, linesToRead i
m.refreshView()
break outer
}
_, _ = m.writer.Write(append(scanner.Bytes(), []byte("\n")...))
m.writer.Write(append(scanner.Bytes(), []byte("\n")...))
}
m.refreshView()
case <-stop:
m.refreshView()
break outer
}
}
@@ -123,12 +124,6 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, linesToRead i
m.Log.Warn(err)
}
m.refreshView()
if onDone != nil {
onDone()
}
close(done)
}()
@@ -146,7 +141,7 @@ func (t *ViewBufferManager) Close() {
return
}
c := make(chan struct{})
c := make(chan struct{}, 1)
go func() {
t.currentTask.Stop()
@@ -175,10 +170,7 @@ func (m *ViewBufferManager) NewTask(f func(stop chan struct{}) error) error {
m.waitingMutex.Lock()
defer m.waitingMutex.Unlock()
m.Log.Infof("done waiting")
if taskID < m.newTaskId {
m.Log.Infof("returning cos the task is obsolete")
return
}
@@ -222,4 +214,15 @@ func (t *Task) Stop() {
<-t.notifyStopped
t.Log.Info("received notifystopped message")
t.stopped = true
return
}
// kill kills a process
func kill(cmd *exec.Cmd) error {
if cmd.Process == nil {
// somebody got to it before we were able to, poor bastard
return nil
}
return cmd.Process.Kill()
}
+51 -19
View File
@@ -6,10 +6,13 @@ import (
"log"
"os"
"path/filepath"
"reflect"
"regexp"
"strings"
"time"
"github.com/go-errors/errors"
"github.com/fatih/color"
)
@@ -111,6 +114,46 @@ func Min(x, y int) int {
return y
}
type Displayable interface {
GetDisplayStrings(bool) []string
}
// RenderList takes a slice of items, confirms they implement the Displayable
// interface, then generates a list of their displaystrings to write to a panel's
// buffer
func RenderList(slice interface{}, isFocused bool) (string, error) {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
return "", errors.New("RenderList given a non-slice type")
}
displayables := make([]Displayable, s.Len())
for i := 0; i < s.Len(); i++ {
value, ok := s.Index(i).Interface().(Displayable)
if !ok {
return "", errors.New("item does not implement the Displayable interface")
}
displayables[i] = value
}
return renderDisplayableList(displayables, isFocused)
}
// renderDisplayableList takes a list of displayable items, obtains their display
// strings via GetDisplayStrings() and then returns a single string containing
// each item's string representation on its own line, with appropriate horizontal
// padding between the item's own strings
func renderDisplayableList(items []Displayable, isFocused bool) (string, error) {
if len(items) == 0 {
return "", nil
}
stringArrays := getDisplayStringArrays(items, isFocused)
return RenderDisplayStrings(stringArrays), nil
}
func RenderDisplayStrings(displayStringsArr [][]string) string {
padWidths := getPadWidths(displayStringsArr)
paddedDisplayStrings := getPaddedDisplayStrings(displayStringsArr, padWidths)
@@ -177,6 +220,14 @@ func displayArraysAligned(stringArrays [][]string) bool {
return true
}
func getDisplayStringArrays(displayables []Displayable, isFocused bool) [][]string {
stringArrays := make([][]string, len(displayables))
for i, item := range displayables {
stringArrays[i] = item.GetDisplayStrings(isFocused)
}
return stringArrays
}
// IncludesString if the list contains the string
func IncludesString(list []string, a string) bool {
for _, b := range list {
@@ -297,22 +348,3 @@ func PrevIntInCycle(sl []int, current int) int {
}
return sl[len(sl)-1]
}
// TruncateWithEllipsis returns a string, truncated to a certain length, with an ellipsis
func TruncateWithEllipsis(str string, limit int) string {
if limit == 1 && len(str) > 1 {
return "."
}
if limit == 2 && len(str) > 2 {
return ".."
}
ellipsis := "..."
if len(str) <= limit {
return str
}
remainingLength := limit - len(ellipsis)
return str[0:remainingLength] + "..."
}
+160
View File
@@ -196,6 +196,166 @@ func TestDisplayArraysAligned(t *testing.T) {
}
}
type myDisplayable struct {
strings []string
}
type myStruct struct{}
// GetDisplayStrings is a function.
func (d *myDisplayable) GetDisplayStrings(isFocused bool) []string {
if isFocused {
return append(d.strings, "blah")
}
return d.strings
}
// TestGetDisplayStringArrays is a function.
func TestGetDisplayStringArrays(t *testing.T) {
type scenario struct {
input []Displayable
isFocused bool
expected [][]string
}
scenarios := []scenario{
{
[]Displayable{
Displayable(&myDisplayable{[]string{"a", "b"}}),
Displayable(&myDisplayable{[]string{"c", "d"}}),
},
false,
[][]string{{"a", "b"}, {"c", "d"}},
},
{
[]Displayable{
Displayable(&myDisplayable{[]string{"a", "b"}}),
Displayable(&myDisplayable{[]string{"c", "d"}}),
},
true,
[][]string{{"a", "b", "blah"}, {"c", "d", "blah"}},
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, getDisplayStringArrays(s.input, s.isFocused))
}
}
// TestRenderDisplayableList is a function.
func TestRenderDisplayableList(t *testing.T) {
type scenario struct {
input []Displayable
isFocused bool
expectedString string
expectedErrorMessage string
}
scenarios := []scenario{
{
[]Displayable{
Displayable(&myDisplayable{[]string{}}),
Displayable(&myDisplayable{[]string{}}),
},
false,
"\n",
"",
},
{
[]Displayable{
Displayable(&myDisplayable{[]string{"aa", "b"}}),
Displayable(&myDisplayable{[]string{"c", "d"}}),
},
false,
"aa b\nc d",
"",
},
{
[]Displayable{
Displayable(&myDisplayable{[]string{"a"}}),
Displayable(&myDisplayable{[]string{"b", "c"}}),
},
false,
"a \nb c",
"",
},
{
[]Displayable{
Displayable(&myDisplayable{[]string{"a"}}),
Displayable(&myDisplayable{[]string{"b"}}),
},
true,
"a blah\nb blah",
"",
},
}
for _, s := range scenarios {
str, err := renderDisplayableList(s.input, s.isFocused)
assert.EqualValues(t, s.expectedString, str)
if s.expectedErrorMessage != "" {
assert.EqualError(t, err, s.expectedErrorMessage)
} else {
assert.NoError(t, err)
}
}
}
// TestRenderList is a function.
func TestRenderList(t *testing.T) {
type scenario struct {
input interface{}
isFocused bool
expectedString string
expectedErrorMessage string
}
scenarios := []scenario{
{
[]*myDisplayable{
{[]string{"aa", "b"}},
{[]string{"c", "d"}},
},
false,
"aa b\nc d",
"",
},
{
[]*myStruct{
{},
{},
},
false,
"",
"item does not implement the Displayable interface",
},
{
&myStruct{},
false,
"",
"RenderList given a non-slice type",
},
{
[]*myDisplayable{
{[]string{"a"}},
},
true,
"a blah",
"",
},
}
for _, s := range scenarios {
str, err := RenderList(s.input, s.isFocused)
assert.EqualValues(t, s.expectedString, str)
if s.expectedErrorMessage != "" {
assert.EqualError(t, err, s.expectedErrorMessage)
} else {
assert.NoError(t, err)
}
}
}
// TestGetPaddedDisplayStrings is a function.
func TestGetPaddedDisplayStrings(t *testing.T) {
type scenario struct {
+17 -70
View File
@@ -12,7 +12,6 @@ import (
"fmt"
"log"
"os"
"sort"
"strings"
"github.com/jesseduffield/lazygit/pkg/app"
@@ -61,85 +60,33 @@ func formatTitle(title string) string {
func formatBinding(binding *gui.Binding) string {
if binding.Alternative != "" {
return fmt.Sprintf(" <kbd>%s</kbd>: %s (%s)\n", gui.GetKeyDisplay(binding.Key), binding.Description, binding.Alternative)
return fmt.Sprintf(" <kbd>%s</kbd>: %s (%s)\n", binding.GetKey(), binding.Description, binding.Alternative)
}
return fmt.Sprintf(" <kbd>%s</kbd>: %s\n", gui.GetKeyDisplay(binding.Key), binding.Description)
return fmt.Sprintf(" <kbd>%s</kbd>: %s\n", binding.GetKey(), binding.Description)
}
func getBindingSections(mApp *app.App) []*bindingSection {
bindingSections := []*bindingSection{}
bindings := mApp.Gui.GetInitialKeybindings()
type contextAndViewType struct {
context string
viewName string
}
contextAndViewBindingMap := map[contextAndViewType][]*gui.Binding{}
for _, binding := range bindings {
contexts := []string{}
if len(binding.Contexts) == 0 {
contexts = append(contexts, "")
} else {
contexts = append(contexts, binding.Contexts...)
// TODO: add context-based keybindings
for _, binding := range mApp.Gui.GetInitialKeybindings() {
if binding.Description == "" {
continue
}
for _, context := range contexts {
key := contextAndViewType{context: context, viewName: binding.ViewName}
existing := contextAndViewBindingMap[key]
if existing == nil {
contextAndViewBindingMap[key] = []*gui.Binding{binding}
} else {
contextAndViewBindingMap[key] = append(contextAndViewBindingMap[key], binding)
}
}
}
type groupedBindingsType struct {
contextAndView contextAndViewType
bindings []*gui.Binding
}
groupedBindings := make([]groupedBindingsType, len(contextAndViewBindingMap))
for contextAndView, contextBindings := range contextAndViewBindingMap {
groupedBindings = append(groupedBindings, groupedBindingsType{contextAndView: contextAndView, bindings: contextBindings})
}
sort.Slice(groupedBindings, func(i, j int) bool {
first := groupedBindings[i].contextAndView
second := groupedBindings[j].contextAndView
if first.viewName == "" {
return true
}
if second.viewName == "" {
return false
}
return first.viewName < second.viewName || (first.viewName == second.viewName && first.context < second.context)
})
for _, group := range groupedBindings {
contextAndView := group.contextAndView
contextBindings := group.bindings
mApp.Log.Warn("viewname: " + contextAndView.viewName + ", context: " + contextAndView.context)
viewName := contextAndView.viewName
viewName := binding.ViewName
if viewName == "" {
viewName = "global"
}
translatedView := localisedTitle(mApp, viewName)
var title string
if contextAndView.context == "" {
addendum := " " + mApp.Tr.SLocalize("Panel")
if viewName == "global" {
addendum = ""
}
title = fmt.Sprintf("%s%s", translatedView, addendum)
} else {
translatedContextName := localisedTitle(mApp, contextAndView.context)
title = fmt.Sprintf("%s %s (%s)", translatedView, mApp.Tr.SLocalize("Panel"), translatedContextName)
}
title := localisedTitle(mApp, viewName)
bindingSections = addBinding(title, bindingSections, binding)
}
for contextName, contextBindings := range mApp.Gui.GetContextMap() {
translatedView := localisedTitle(mApp, contextBindings[0].ViewName)
translatedContextName := localisedTitle(mApp, contextName)
title := fmt.Sprintf("%s (%s)", translatedView, translatedContextName)
for _, binding := range contextBindings {
bindingSections = addBinding(title, bindingSections, binding)
@@ -170,7 +117,7 @@ func addBinding(title string, bindingSections []*bindingSection, binding *gui.Bi
}
func formatSections(mApp *app.App, bindingSections []*bindingSection) string {
content := fmt.Sprintf("# Lazygit %s\n", mApp.Tr.SLocalize("Keybindings"))
content := fmt.Sprintf("# Lazygit %s\n", mApp.Tr.SLocalize("menu"))
for _, section := range bindingSections {
content += formatTitle(section.title)
+4 -4
View File
@@ -152,13 +152,13 @@ func (ei *escapeInterpreter) outputNormal() error {
switch {
case p >= 30 && p <= 37:
ei.curFgColor |= Attribute(p - 30 + 1)
ei.curFgColor = Attribute(p - 30 + 1)
case p == 39:
ei.curFgColor |= ColorDefault
ei.curFgColor = ColorDefault
case p >= 40 && p <= 47:
ei.curBgColor |= Attribute(p - 40 + 1)
ei.curBgColor = Attribute(p - 40 + 1)
case p == 49:
ei.curBgColor |= ColorDefault
ei.curBgColor = ColorDefault
case p == 1:
ei.curFgColor |= AttrBold
case p == 4:
+3 -24
View File
@@ -10,7 +10,6 @@ import (
"strings"
"sync"
"time"
"unicode"
"github.com/go-errors/errors"
@@ -467,38 +466,18 @@ func (v *View) Rewind() {
v.readOffset = 0
}
func containsUpcaseChar(str string) bool {
for _, ch := range str {
if unicode.IsUpper(ch) {
return true
}
}
return false
}
func (v *View) updateSearchPositions() {
if v.searcher.searchString != "" {
var normalizeRune func(r rune) rune
var normalizedSearchStr string
// if we have any uppercase characters we'll do a case-sensitive search
if containsUpcaseChar(v.searcher.searchString) {
normalizedSearchStr = v.searcher.searchString
normalizeRune = func(r rune) rune { return r }
} else {
normalizedSearchStr = strings.ToLower(v.searcher.searchString)
normalizeRune = unicode.ToLower
}
v.searcher.searchPositions = []cellPos{}
for y, line := range v.lines {
lineLoop:
for x, _ := range line {
if normalizeRune(line[x].chr) == rune(normalizedSearchStr[0]) {
for offset := 1; offset < len(normalizedSearchStr); offset++ {
if line[x].chr == rune(v.searcher.searchString[0]) {
for offset := 1; offset < len(v.searcher.searchString); offset++ {
if len(line)-1 < x+offset {
continue lineLoop
}
if normalizeRune(line[x+offset].chr) != rune(normalizedSearchStr[offset]) {
if line[x+offset].chr != rune(v.searcher.searchString[offset]) {
continue lineLoop
}
}
+1 -45
View File
@@ -1,5 +1,4 @@
# github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
## explicit
github.com/cloudfoundry/jibber_jabber
# github.com/davecgh/go-spew v1.1.1
github.com/davecgh/go-spew/spew
@@ -11,21 +10,13 @@ github.com/emirpasic/gods/trees
github.com/emirpasic/gods/trees/binaryheap
github.com/emirpasic/gods/utils
# github.com/fatih/color v1.7.0
## explicit
github.com/fatih/color
# github.com/fsnotify/fsnotify v1.4.7
## explicit
github.com/fsnotify/fsnotify
# github.com/go-errors/errors v1.0.1
## explicit
github.com/go-errors/errors
# github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
## explicit
github.com/golang-collections/collections/stack
# github.com/golang/protobuf v1.3.2
## explicit
# github.com/google/go-cmp v0.3.1
## explicit
# github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/hcl
github.com/hashicorp/hcl/hcl/ast
@@ -38,86 +29,61 @@ github.com/hashicorp/hcl/json/parser
github.com/hashicorp/hcl/json/scanner
github.com/hashicorp/hcl/json/token
# github.com/integrii/flaggy v1.4.0
## explicit
github.com/integrii/flaggy
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jbenet/go-context/io
# github.com/jesseduffield/gocui v0.3.1-0.20200309001002-7765949e1c8a
## explicit
# github.com/jesseduffield/gocui v0.3.1-0.20200224201655-5024a02682ed
github.com/jesseduffield/gocui
# github.com/jesseduffield/pty v1.2.1
## explicit
github.com/jesseduffield/pty
# github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9
## explicit
github.com/jesseduffield/termbox-go
# github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
## explicit
github.com/kardianos/osext
# github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd
github.com/kevinburke/ssh_config
# github.com/konsorten/go-windows-terminal-sequences v1.0.2
## explicit
github.com/konsorten/go-windows-terminal-sequences
# github.com/magiconair/properties v1.8.1
github.com/magiconair/properties
# github.com/mattn/go-colorable v0.1.4
## explicit
github.com/mattn/go-colorable
# github.com/mattn/go-isatty v0.0.11
## explicit
github.com/mattn/go-isatty
# github.com/mattn/go-runewidth v0.0.8
## explicit
github.com/mattn/go-runewidth
# github.com/mgutz/str v1.2.0
## explicit
github.com/mgutz/str
# github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-homedir
# github.com/mitchellh/gox v1.0.1
## explicit
# github.com/mitchellh/mapstructure v1.1.2
github.com/mitchellh/mapstructure
# github.com/nicksnyder/go-i18n/v2 v2.0.3
## explicit
github.com/nicksnyder/go-i18n/v2/i18n
github.com/nicksnyder/go-i18n/v2/internal
github.com/nicksnyder/go-i18n/v2/internal/plural
# github.com/onsi/ginkgo v1.10.3
## explicit
# github.com/onsi/gomega v1.7.1
## explicit
# github.com/pelletier/go-toml v1.6.0
## explicit
github.com/pelletier/go-toml
# github.com/pmezard/go-difflib v1.0.0
github.com/pmezard/go-difflib/difflib
# github.com/sergi/go-diff v1.0.0
github.com/sergi/go-diff/diffmatchpatch
# github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0
## explicit
github.com/shibukawa/configdir
# github.com/sirupsen/logrus v1.4.2
## explicit
github.com/sirupsen/logrus
# github.com/spf13/afero v1.2.2
## explicit
github.com/spf13/afero
github.com/spf13/afero/mem
# github.com/spf13/cast v1.3.0
github.com/spf13/cast
# github.com/spf13/jwalterweatherman v1.1.0
## explicit
github.com/spf13/jwalterweatherman
# github.com/spf13/pflag v1.0.5
## explicit
github.com/spf13/pflag
# github.com/spf13/viper v1.6.1
## explicit
github.com/spf13/viper
# github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
## explicit
github.com/spkg/bom
# github.com/src-d/gcfg v1.4.0
github.com/src-d/gcfg
@@ -125,17 +91,14 @@ github.com/src-d/gcfg/scanner
github.com/src-d/gcfg/token
github.com/src-d/gcfg/types
# github.com/stretchr/testify v1.4.0
## explicit
github.com/stretchr/testify/assert
# github.com/subosito/gotenv v1.2.0
github.com/subosito/gotenv
# github.com/tcnksm/go-gitconfig v0.1.2
## explicit
github.com/tcnksm/go-gitconfig
# github.com/xanzy/ssh-agent v0.2.1
github.com/xanzy/ssh-agent
# golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
## explicit
golang.org/x/crypto/cast5
golang.org/x/crypto/chacha20
golang.org/x/crypto/curve25519
@@ -153,25 +116,20 @@ golang.org/x/crypto/ssh
golang.org/x/crypto/ssh/agent
golang.org/x/crypto/ssh/knownhosts
# golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
## explicit
golang.org/x/net/context
golang.org/x/net/internal/socks
golang.org/x/net/proxy
# golang.org/x/sys v0.0.0-20191210023423-ac6580df4449
## explicit
golang.org/x/sys/cpu
golang.org/x/sys/unix
golang.org/x/sys/windows
# golang.org/x/text v0.3.2
## explicit
golang.org/x/text/internal/language
golang.org/x/text/internal/language/compact
golang.org/x/text/internal/tag
golang.org/x/text/language
golang.org/x/text/transform
golang.org/x/text/unicode/norm
# gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
## explicit
# gopkg.in/ini.v1 v1.51.0
gopkg.in/ini.v1
# gopkg.in/src-d/go-billy.v4 v4.3.2
@@ -181,7 +139,6 @@ gopkg.in/src-d/go-billy.v4/helper/polyfill
gopkg.in/src-d/go-billy.v4/osfs
gopkg.in/src-d/go-billy.v4/util
# gopkg.in/src-d/go-git.v4 v4.13.1
## explicit
gopkg.in/src-d/go-git.v4
gopkg.in/src-d/go-git.v4/config
gopkg.in/src-d/go-git.v4/internal/revision
@@ -226,5 +183,4 @@ gopkg.in/src-d/go-git.v4/utils/merkletrie/noder
# gopkg.in/warnings.v0 v0.1.2
gopkg.in/warnings.v0
# gopkg.in/yaml.v2 v2.2.7
## explicit
gopkg.in/yaml.v2