Compare commits

...

51 Commits

Author SHA1 Message Date
Jesse Duffield
95b147079f allow applying patch directly 2020-03-26 21:44:45 +11:00
Jesse Duffield
83757f1065 limit size of menu panel 2020-03-26 21:44:33 +11:00
Jesse Duffield
f2036b42e5 only load new reflog entries 2020-03-26 21:44:33 +11:00
Jesse Duffield
21b7d41845 relax limit on commit list and reset on branch change 2020-03-26 21:44:33 +11:00
Jesse Duffield
91a404d033 separate commits from cherry pick state 2020-03-26 21:44:33 +11:00
Jesse Duffield
d027cf969c better handling of current branch name 2020-03-26 20:37:06 +11:00
Jesse Duffield
c7f68a2ef9 delete unused assets 2020-03-26 19:18:43 +11:00
Jesse Duffield
78e55a05c1 another staging gif 2020-03-26 19:15:15 +11:00
Jesse Duffield
ca71555d0b Update README.md 2020-03-26 19:10:49 +11:00
Jesse Duffield
77fdac01ff better staging gif 2020-03-26 19:10:37 +11:00
Jesse Duffield
8301fae01e Update README.md 2020-03-26 19:04:48 +11:00
Jesse Duffield
e9161ad702 add staging gif 2020-03-26 19:04:26 +11:00
Jesse Duffield
a0a139da1f add rebasing gif 2020-03-26 18:43:10 +11:00
Francisco Miamoto
8f13d1da91 change binary releases order 2020-03-26 18:32:38 +11:00
Francisco Miamoto
d5fe9ce2c7 add table of contents to readme 2020-03-26 18:32:38 +11:00
Jesse Duffield
37acc17cf3 more lenient getting of short shas 2020-03-26 18:30:02 +11:00
Jesse Duffield
569ec5919c Update README.md 2020-03-26 09:14:14 +11:00
Dawid Dziurla
19719becf5 workflows: run goreleaser as a build step for CI 2020-03-25 21:26:15 +11:00
Dawid Dziurla
e64057b803 workflows: install gox in separate step in /tmp directory
avoid Go trying to add a dependency to go.mod file
2020-03-25 21:26:15 +11:00
Dawid Dziurla
672667aa3e goreleaser: stop ignoring arm64 build for freebsd 2020-03-25 21:26:15 +11:00
Dawid Dziurla
8a06b6067e go mod vendor 2020-03-25 21:26:15 +11:00
Dawid Dziurla
2dcc52abd0 go mod tidy 2020-03-25 21:26:15 +11:00
Dawid Dziurla
c831ad39c9 pkg: use upstream pty package 2020-03-25 21:26:15 +11:00
Jesse Duffield
0cf78ea9ad Update Undoing.md 2020-03-25 20:35:10 +11:00
Jesse Duffield
3d51fbf354 Update README.md 2020-03-25 20:32:44 +11:00
Jesse Duffield
e7a2c7cc3e update cheatsheet 2020-03-25 20:24:03 +11:00
Jesse Duffield
708a078412 document undo 2020-03-25 20:17:46 +11:00
Jesse Duffield
bbcc4b7b70 just disallow undo/redo while rebasing because you need more info than just the reflog 2020-03-25 09:39:04 +11:00
Jesse Duffield
45bba0a3c5 ignore redundant actions when undoing and redoing 2020-03-25 09:39:04 +11:00
Jesse Duffield
d105e2690a vastly improve the logic for undo and redo 2020-03-25 09:39:04 +11:00
Jesse Duffield
32d3e497c3 fix tests 2020-03-25 09:39:04 +11:00
Jesse Duffield
30a5d1b486 move into undoing file 2020-03-25 09:39:04 +11:00
Jesse Duffield
6b3ea56add refactor undo and redo 2020-03-25 09:39:04 +11:00
Jesse Duffield
c3aefdb98e stateless undos and redos 2020-03-25 09:39:04 +11:00
Jesse Duffield
094939451d more explicit env vars 2020-03-25 09:39:04 +11:00
Jesse Duffield
0e23f44b84 support reflog action prefix 2020-03-25 09:39:04 +11:00
Jesse Duffield
daecdd7c2b redoing 2020-03-25 09:39:04 +11:00
Jesse Duffield
7c8df28d01 add waiting status to checkout ref handler 2020-03-25 09:39:04 +11:00
Jesse Duffield
65917272a2 undoing status 2020-03-25 09:39:04 +11:00
Jesse Duffield
137fd80fdb note that undo functionality is experimental 2020-03-25 09:39:04 +11:00
Jesse Duffield
98fbc61221 better formatted reflog list 2020-03-25 09:39:04 +11:00
Jesse Duffield
f80d15062b use reflog undo history pointer 2020-03-25 09:39:04 +11:00
Jesse Duffield
b1b0219f04 autostash changes when hard resetting 2020-03-25 09:39:04 +11:00
Jesse Duffield
b1941c33f7 undo via rebase 2020-03-25 09:39:04 +11:00
J. B. Rainsberger
a15a7b607d Made the humor more concise and clear. 2020-03-25 09:18:25 +11:00
J. B. Rainsberger
d50283f5ee improved spelling 2020-03-25 09:18:25 +11:00
J. B. Rainsberger
6508d3b872 inject more humor into the README 2020-03-25 09:18:25 +11:00
J. B. Rainsberger
65b8cef1b8 Fixed an incorrect criticism of git in the README.
It's true that parts of git are genuinely difficult to use, so
we don't need to overstate that difficulty in order to state the
value proposition of lazygit. If `git add -p` can't split a hunk
any further, one is not _completely_ stuck, but one does need to
edit the hunk in a way that, after years, I still need a few
attempts to get right. The fact that many otherwise-experienced
git users don't even know that one can do that is itself a
testament to the UX problems that lazygit is trying to address.
2020-03-25 09:18:25 +11:00
Jesse Duffield
5d460e1e5e add tab keybindings 2020-03-23 23:25:00 +11:00
Jesse Duffield
3d3e0be7bd more compatible commands 2020-03-23 22:33:17 +11:00
Dawid Dziurla
c06c0b7133 workflows: git fetch --unshallow before goreleaser step 2020-03-22 21:49:41 +11:00
81 changed files with 1186 additions and 296 deletions

View File

@@ -11,16 +11,14 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- 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

View File

@@ -31,7 +31,6 @@ jobs:
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"
uses: goreleaser/goreleaser-action@v1
with:
args: --skip-publish --snapshot

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

View File

@@ -1,28 +1,50 @@
# 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
A simple terminal UI for git commands, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
![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)
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!*
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 to step through each hunk and if a hunk can't be split down any further but contains code you don't want to stage, you have to edit an arcane patch file _by hand_? *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.
![Gif](/docs/resources/lazygit-example.gif)
![Gif](/docs/resources/staging.gif)
- [Installation](https://github.com/jesseduffield/lazygit#installation)
- [Usage](https://github.com/jesseduffield/lazygit#usage),
[Keybindings](/docs/keybindings)
- [Cool Features](https://github.com/jesseduffield/lazygit#cool-features)
- [Contributing](https://github.com/jesseduffield/lazygit#contributing)
- [Video Tutorial](https://youtu.be/VDXvbHZYeKY)
- [Rebase Magic Video Tutorial](https://youtu.be/4XaToVut_hs)
- [Twitch Stream](https://www.twitch.tv/jesseduffield)
## Table of contents
[<img src="https://i.imgur.com/sVEktDn.png">](https://youtu.be/CPLdltN7wgE)
- [Installation](#installation)
- [Binary releases](#binary-releases)
- [Homebrew](#homebrew)
- [MacPorts](#macports)
- [Ubuntu](#ubuntu)
- [Void Linux](#void-linux)
- [Scoop (Windows)](<#scoop-(windows)>)
- [Arch Linux](#arch-linux)
- [Fedora and CentOS 7](#fedora-and-centos-7)
- [Conda](#conda)
- [Go](#go)
- [Usage](#usage)
- [Keybindings](#keybindings)
- [Changing directory on exit](#changing-directory-on-exit)
- [Undo/Redo](#undo/redo)
- [Configuration](#configuration)
- [Custom pagers](#configuration)
- [Tutorials](#tutorials)
- [Cool Features](#cool-features)
- [Contributing](#contributing)
- [Donate](#donate)
- [Alternatives](#alternatives)
Github Sponsors is matching all donations dollar-for-dollar for 12 months so if you're feeling generous consider [sponsoring me](https://github.com/sponsors/jesseduffield)
[<img src="https://i.imgur.com/sVEktDn.png">](https://youtu.be/CPLdltN7wgE)
## Installation
### Binary Releases
For Windows, Mac OS or Linux, you can download a binary release [here](/releases).
### 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.
@@ -67,6 +89,7 @@ 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:
@@ -109,10 +132,6 @@ Released versions are available for different platforms, see <https://anaconda.o
conda install -c conda-forge lazygit
```
### Binary Release (Windows/Linux/OSX)
You can download a binary release [here](https://github.com/jesseduffield/lazygit/releases).
### Go
```sh
@@ -125,18 +144,24 @@ may need to add `~/go/bin` to your \$PATH (MacOS/Linux), or `%HOME%\go\bin`
(Windows). Not to be mistaked for `C:\Go\bin` (which is for Go's own binaries,
not apps like Lazygit).
## Usage
Call `lazygit` in your terminal inside a git repository. If you want, you can
Call `lazygit` in your terminal inside a git repository.
```sh
$ lazygit
```
If you want, you can
also add an alias for this with `echo "alias lg='lazygit'" >> ~/.zshrc` (or
whichever rc file you're using).
- Basic video tutorial [here](https://youtu.be/VDXvbHZYeKY).
- Rebase Magic tutorial [here](https://youtu.be/4XaToVut_hs)
- List of keybindings
[here](/docs/keybindings).
### Keybindings
## Changing Directory On Exit
You can check out the 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):
@@ -156,14 +181,25 @@ lg()
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`.
### Undo/Redo
See the [docs](/docs/Undoing.md)
## Configuration
Check the [configuration docs](docs/Config.md).
Check out the [configuration docs](docs/Config.md).
## Custom Pagers
### Custom Pagers
See the [docs](docs/Custom_Pagers.md)
## Tutorials
- [Video Tutorial](https://youtu.be/VDXvbHZYeKY)
- [Rebase Magic Video Tutorial](https://youtu.be/4XaToVut_hs)
- [Twitch Stream](https://www.twitch.tv/jesseduffield)
## Cool features
- Adding files easily
@@ -179,14 +215,14 @@ See the [docs](docs/Custom_Pagers.md)
### Interactive Rebasing
![Interactive Rebasing](/docs/resources/interactive-rebase.png)
![Interactive Rebasing](/docs/resources/rebase.gif)
## Contributing
We love your input! Please check out the [contributing guide](CONTRIBUTING.md).
For contributor discussion about things not better discussed here in the repo, join the slack channel
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazygit/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M)
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazygit/shared_invite/zt-5bo2clzo-hB8ZTVN5dWUCqj5QFiQVLA)
## Donate

View File

@@ -83,6 +83,8 @@ Default path for the config file:
prevTab: '['
nextScreenMode: '+'
prevScreenMode: '_'
undo: 'z'
redo: '<c-z>'
status:
checkForUpdate: 'u'
recentRepos: '<enter>'
@@ -138,7 +140,6 @@ Default path for the config file:
toggleDragSelect-alt: 'V'
toggleSelectHunk: 'a'
pickBothHunks: 'b'
undo: 'z'
```
## Platform Defaults

24
docs/Undoing.md Normal file
View File

@@ -0,0 +1,24 @@
# Undo/Redo in lazygit
![Gif](/docs/resources/undo2.gif)
## Keybindings:
'z' to undo, 'ctrl+z' to redo
## How it works
If you're as clumsy as me you'll probably have felt the pain of botching an interactive rebase or doing a hard reset onto the wrong commit. Luckily, the reflog allows you to trace your steps and make things right again, but I personally can't stand trying to make sense of the reflog.
Lazygit can read through your reflog for you and walk back action by action so that you don't even need to read the reflog. If lazygit finds a reflog entry where you checked out a branch, we'll checkout the original branch. If the entry is from a commit being applied, we'll go back to the commit before that. If we hit an interactive rebase, we'll go back to the commit you were on just before you started it.
## You can even undo things you did outside of lazygit!
Because lazygit just uses the reflog to keep track of things, it doesn't matter whether you're trying to undo something you did in lazygit or directly on the command line. You can open lazygit for the first time and start undoing thing in your repo! Likewise, lazygit marks its undos/redos in the reflog so if you quit the application and come back, lazygit still knows where you're up to.
## Limitations
There are limitations: firstly, lazygit can only undo things that are recorded in the reflog. That means changes to your working tree or stash aren't covered. Secondly, anything permanent you do like pushing to a remote can't be undone. Thirdly, actions like creating a branch won't be undone, because they're not stored in the reflog.
If you are mid-rebase, undo/redo is not supported, because the reflog doesn't enough contain information about what specific things have happened inside that rebase. If you want to undo out of a rebase, it's best to abort the rebase (the default keybinding for bringing up rebase options is 'm').
Undo/Redo is a new feature so if you find a bug let us know. The worst case scenario is that you'll just need to look at your reflog and manually put yourself back on track.

View File

@@ -11,11 +11,20 @@
<kbd>p</kbd>: pull
<kbd>R</kbd>: refresh
<kbd>x</kbd>: open menu
<kbd>z</kbd>: undo (via reflog) (experimental)
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: prev screen mode
<kbd>:</kbd>: execute custom command
</pre>
## Branches Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
</pre>
## Branches Panel (Branches Tab)
<pre>
@@ -83,6 +92,8 @@
## Commits Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>/</kbd>: start search
</pre>
@@ -110,6 +121,7 @@
<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
</pre>
## Commits Panel (Reflog Tab)

View File

@@ -11,11 +11,20 @@
<kbd>p</kbd>: pull
<kbd>R</kbd>: verversen
<kbd>x</kbd>: open menu
<kbd>z</kbd>: undo (via reflog) (experimental)
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: prev screen mode
<kbd>:</kbd>: voor aangepast commando uit
</pre>
## Branches Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
</pre>
## Branches Panel (Branches Tab)
<pre>
@@ -83,6 +92,8 @@
## Commits Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>/</kbd>: start search
</pre>
@@ -110,6 +121,7 @@
<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
</pre>
## Commits Panel (Reflog Tab)

View File

@@ -11,11 +11,20 @@
<kbd>p</kbd>: pull
<kbd>R</kbd>: odśwież
<kbd>x</kbd>: open menu
<kbd>z</kbd>: undo (via reflog) (experimental)
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: prev screen mode
<kbd>:</kbd>: execute custom command
</pre>
## Gałęzie Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
</pre>
## Gałęzie Panel (Branches Tab)
<pre>
@@ -83,6 +92,8 @@
## Commity Panel
<pre>
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>/</kbd>: start search
</pre>
@@ -110,6 +121,7 @@
<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
</pre>
## Commity Panel (Reflog Tab)
@@ -152,7 +164,7 @@
<kbd>►</kbd>: select next conflict
<kbd>▲</kbd>: select top hunk
<kbd>▼</kbd>: select bottom hunk
<kbd>z</kbd>: undo
<kbd>z</kbd>: cofnij
</pre>
## Main Panel (Normal)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

BIN
docs/resources/rebase.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
docs/resources/staging.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

BIN
docs/resources/undo2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 KiB

3
go.mod
View File

@@ -4,6 +4,7 @@ go 1.14
require (
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.10-0.20191209115840-8ab47f72e854
github.com/fatih/color v1.7.0
github.com/fsnotify/fsnotify v1.4.7
github.com/go-errors/errors v1.0.1
@@ -12,7 +13,6 @@ require (
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/pty v1.2.1
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 +20,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

12
go.sum
View File

@@ -24,6 +24,8 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.10-0.20191209115840-8ab47f72e854 h1:NB4neYMzyBsw52kUdkTrQm4Q05ErObCdwLvJptpfJSc=
github.com/creack/pty v1.1.10-0.20191209115840-8ab47f72e854/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -68,8 +70,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 +78,8 @@ 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/pty v1.2.1 h1:7xYBiwNH0PpWqC8JmvrPq1a/ksNqyCavzWu9pbBGYWI=
github.com/jesseduffield/pty v1.2.1/go.mod h1:7jlS40+UhOqkZJDIG1B/H21xnuET/+fvbbnHCa8wSIo=
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=
@@ -121,10 +117,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=

View File

@@ -3,7 +3,9 @@ package commands
// Branch : A git branch
// duplicating this for now
type Branch struct {
Name string
Name string
// the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf'
DisplayName string
Recency string
Pushables string
Pullables string

View File

@@ -34,16 +34,8 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand) (*BranchLis
}, nil
}
func (b *BranchListBuilder) obtainCurrentBranchName() string {
branchName, err := b.GitCommand.CurrentBranchName()
if err != nil {
panic(err.Error())
}
return branchName
}
func (b *BranchListBuilder) obtainBranches() []*Branch {
cmdStr := `git branch --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)"`
cmdStr := `git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`
output, err := b.GitCommand.OSCommand.RunCommandWithOutput(cmdStr)
if err != nil {
panic(err)
@@ -51,40 +43,48 @@ func (b *BranchListBuilder) obtainBranches() []*Branch {
trimmedOutput := strings.TrimSpace(output)
outputLines := strings.Split(trimmedOutput, "\n")
branches := make([]*Branch, len(outputLines))
for i, line := range outputLines {
branches := make([]*Branch, 0, len(outputLines))
for _, line := range outputLines {
if line == "" {
continue
}
split := strings.Split(line, SEPARATION_CHAR)
name := split[1]
branches[i] = &Branch{
branch := &Branch{
Name: name,
Pullables: "?",
Pushables: "?",
Head: split[0] == "*",
}
upstreamName := split[2]
if upstreamName == "" {
branches = append(branches, branch)
continue
}
branches[i].UpstreamName = upstreamName
branch.UpstreamName = upstreamName
track := split[3]
re := regexp.MustCompile(`ahead (\d+)`)
match := re.FindStringSubmatch(track)
if len(match) > 1 {
branches[i].Pushables = match[1]
branch.Pushables = match[1]
} else {
branches[i].Pushables = "0"
branch.Pushables = "0"
}
re = regexp.MustCompile(`behind (\d+)`)
match = re.FindStringSubmatch(track)
if len(match) > 1 {
branches[i].Pullables = match[1]
branch.Pullables = match[1]
} else {
branches[i].Pullables = "0"
branch.Pullables = "0"
}
branches = append(branches, branch)
}
return branches
@@ -92,7 +92,6 @@ func (b *BranchListBuilder) obtainBranches() []*Branch {
// Build the list of branches for the current repo
func (b *BranchListBuilder) Build() []*Branch {
currentBranchName := b.obtainCurrentBranchName()
branches := b.obtainBranches()
reflogBranches := b.obtainReflogBranches()
@@ -116,19 +115,23 @@ outer:
branches = append(branchesWithRecency, branches...)
if len(branches) == 0 {
branches = append([]*Branch{{Name: currentBranchName}}, branches...)
}
foundHead := false
for i, branch := range branches {
if branch.Head {
branch.Name = currentBranchName
foundHead = true
branch.Recency = " *"
branches = append(branches[0:i], branches[i+1:]...)
branches = append([]*Branch{branch}, branches...)
break
}
}
if !foundHead {
currentBranchName, currentBranchDisplayName, err := b.GitCommand.CurrentBranchName()
if err != nil {
panic(err)
}
branches = append([]*Branch{{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"}}, branches...)
}
return branches
}

View File

@@ -2,14 +2,19 @@ package commands
// Commit : A git commit
type Commit struct {
Sha string
Name string
Status string // one of "unpushed", "pushed", "merged", "rebasing" or "selected"
DisplayString string
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
Sha string
Name string
Status string // one of "unpushed", "pushed", "merged", "rebasing" or "selected"
Action string // one of "", "pick", "edit", "squash", "reword", "drop", "fixup"
Tags []string
ExtraInfo string // something like 'HEAD -> master, tag: v0.15.2'
Author string
Date string
}
func (c *Commit) ShortSha() string {
if len(c.Sha) < 8 {
return c.Sha
}
return c.Sha[:8]
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
@@ -70,13 +71,12 @@ func (c *CommitListBuilder) extractCommitFromLine(line string) *Commit {
}
return &Commit{
Sha: sha,
Name: message,
DisplayString: line,
Tags: tags,
ExtraInfo: extraInfo,
Date: date,
Author: author,
Sha: sha,
Name: message,
Tags: tags,
ExtraInfo: extraInfo,
Date: date,
Author: author,
}
}
@@ -100,15 +100,20 @@ func (c *CommitListBuilder) GetCommits(limit bool) ([]*Commit, error) {
}
unpushedCommits := c.getUnpushedCommits()
log := c.getLog(limit)
cmd := c.getLogCmd(limit)
// now we can split it up and turn it into commits
for _, line := range utils.SplitLines(log) {
err = RunLineOutputCmd(cmd, func(line string) (bool, error) {
commit := c.extractCommitFromLine(line)
_, unpushed := unpushedCommits[commit.Sha[:8]]
_, unpushed := unpushedCommits[commit.ShortSha()]
commit.Status = map[bool]string{true: "unpushed", false: "pushed"}[unpushed]
commits = append(commits, commit)
return false, nil
})
if err != nil {
return nil, err
}
if rebaseMode != "" {
currentCommit := commits[len(rebasingCommits)]
blue := color.New(color.FgYellow)
@@ -121,11 +126,6 @@ func (c *CommitListBuilder) GetCommits(limit bool) ([]*Commit, error) {
return nil, err
}
commits, err = c.setCommitCherryPickStatuses(commits)
if err != nil {
return nil, err
}
for _, commit := range commits {
for _, entry := range c.DiffEntries {
if entry.Sha == commit.Sha {
@@ -267,19 +267,8 @@ func (c *CommitListBuilder) setCommitMergedStatuses(commits []*Commit) ([]*Commi
return commits, nil
}
func (c *CommitListBuilder) setCommitCherryPickStatuses(commits []*Commit) ([]*Commit, error) {
for _, commit := range commits {
for _, cherryPickedCommit := range c.CherryPickedCommits {
if commit.Sha == cherryPickedCommit.Sha {
commit.Copied = true
}
}
}
return commits, nil
}
func (c *CommitListBuilder) getMergeBase() (string, error) {
currentBranch, err := c.GitCommand.CurrentBranchName()
currentBranch, _, err := c.GitCommand.CurrentBranchName()
if err != nil {
return "", err
}
@@ -310,18 +299,11 @@ func (c *CommitListBuilder) getUnpushedCommits() map[string]bool {
}
// getLog gets the git log.
func (c *CommitListBuilder) getLog(limit bool) string {
func (c *CommitListBuilder) getLogCmd(limit bool) *exec.Cmd {
limitFlag := ""
if limit {
limitFlag = "-30"
limitFlag = "-300"
}
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))
if err != nil {
// assume if there is an error there are no commits yet for this branch
return ""
}
return result
return c.OSCommand.ExecutableFromString(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))
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/pty"
"github.com/creack/pty"
)
// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout

View File

@@ -322,8 +322,8 @@ func (c *GitCommand) Fetch(unamePassQuestion func(string) string, canAskForCrede
}
// ResetToCommit reset to commit
func (c *GitCommand) ResetToCommit(sha string, strength string) error {
return c.OSCommand.RunCommand("git reset --%s %s", strength, sha)
func (c *GitCommand) ResetToCommit(sha string, strength string, options RunCommandOptions) error {
return c.OSCommand.RunCommandWithOptions(fmt.Sprintf("git reset --%s %s", strength, sha), options)
}
// NewBranch create new branch
@@ -331,19 +331,29 @@ func (c *GitCommand) NewBranch(name string, baseBranch string) error {
return c.OSCommand.RunCommand("git checkout -b %s %s", name, baseBranch)
}
// CurrentBranchName is a function.
func (c *GitCommand) CurrentBranchName() (string, error) {
// CurrentBranchName get the current branch name and displayname.
// the first returned string is the name and the second is the displayname
// e.g. name is 123asdf and displayname is '(HEAD detached at 123asdf)'
func (c *GitCommand) CurrentBranchName() (string, string, error) {
branchName, err := c.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD")
if err != nil || branchName == "HEAD\n" {
output, err := c.OSCommand.RunCommandWithOutput("git branch --contains")
if err != nil {
return "", err
}
re := regexp.MustCompile(CurrentBranchNameRegex)
match := re.FindStringSubmatch(output)
branchName = match[1]
if err == nil && branchName != "HEAD\n" {
trimmedBranchName := strings.TrimSpace(branchName)
return trimmedBranchName, trimmedBranchName, nil
}
return strings.TrimSpace(branchName), nil
output, err := c.OSCommand.RunCommandWithOutput("git branch --contains")
if err != nil {
return "", "", err
}
for _, line := range utils.SplitLines(output) {
re := regexp.MustCompile(CurrentBranchNameRegex)
match := re.FindStringSubmatch(line)
if len(match) > 0 {
branchName = match[1]
displayBranchName := match[0][2:]
return branchName, displayBranchName, nil
}
}
return "HEAD", "HEAD", nil
}
// DeleteBranch delete branch
@@ -522,12 +532,17 @@ func (c *GitCommand) DiscardUnstagedFileChanges(file *File) error {
}
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
func (c *GitCommand) Checkout(branch string, force bool) error {
type CheckoutOptions struct {
Force bool
EnvVars []string
}
func (c *GitCommand) Checkout(branch string, options CheckoutOptions) error {
forceArg := ""
if force {
if options.Force {
forceArg = "--force "
}
return c.OSCommand.RunCommand("git checkout %s %s", forceArg, branch)
return c.OSCommand.RunCommandWithOptions(fmt.Sprintf("git checkout %s %s", forceArg, branch), RunCommandOptions{EnvVars: options.EnvVars})
}
// PrepareCommitSubProcess prepares a subprocess for `git commit`
@@ -866,7 +881,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 show --pretty= --name-only --no-renames %s", commitSha)
files, err := c.OSCommand.RunCommandWithOutput("git diff-tree --no-commit-id --name-only -r --no-renames %s", commitSha)
if err != nil {
return nil, err
}
@@ -1105,26 +1120,42 @@ func (c *GitCommand) FetchRemote(remoteName string) error {
return c.OSCommand.RunCommand("git fetch %s", remoteName)
}
func (c *GitCommand) GetReflogCommits() ([]*Commit, error) {
output, err := c.OSCommand.RunCommandWithOutput("git reflog --abbrev=20")
// GetNewReflogCommits only returns the new reflog commits since the given lastReflogCommit
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
func (c *GitCommand) GetNewReflogCommits(lastReflogCommit *Commit) ([]*Commit, error) {
output, err := c.OSCommand.RunCommandWithOutput("git reflog --abbrev=20 --date=iso")
if err != nil {
return nil, err
// assume error means we have no reflog
return []*Commit{}, nil
}
lines := strings.Split(strings.TrimSpace(output), "\n")
commits := make([]*Commit, len(lines))
re := regexp.MustCompile(`(\w+).*HEAD@\{\d+\}: (.*)`)
for i, line := range lines {
commits := make([]*Commit, 0, len(lines))
re := regexp.MustCompile(`(\w+).*HEAD@\{([^\}]+)\}: (.*)`)
cmd := c.OSCommand.ExecutableFromString("git reflog --abbrev=20 --date=iso")
err = RunLineOutputCmd(cmd, func(line string) (bool, error) {
match := re.FindStringSubmatch(line)
if len(match) <= 1 {
continue
return false, nil
}
commits[i] = &Commit{
commit := &Commit{
Sha: match[1],
Name: match[2],
Name: match[3],
Date: match[2],
Status: "reflog",
}
if lastReflogCommit != nil && commit.Sha == lastReflogCommit.Sha && commit.Date == lastReflogCommit.Date {
// after this point we already have these reflogs loaded so we'll simply return the new ones
return true, nil
}
commits = append(commits, commit)
return false, nil
})
if err != nil {
return nil, err
}
return commits, nil

View File

@@ -628,7 +628,7 @@ func TestGitCommandResetToCommit(t *testing.T) {
return exec.Command("echo")
}
assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard"))
assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard", RunCommandOptions{}))
}
// TestGitCommandNewBranch is a function.
@@ -1439,7 +1439,7 @@ func TestGitCommandCheckout(t *testing.T) {
t.Run(s.testName, func(t *testing.T) {
gitCmd := NewDummyGitCommand()
gitCmd.OSCommand.command = s.command
s.test(gitCmd.Checkout("test", s.force))
s.test(gitCmd.Checkout("test", CheckoutOptions{Force: s.force}))
})
}
}
@@ -1549,7 +1549,7 @@ func TestGitCommandCurrentBranchName(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
test func(string, error)
test func(string, string, error)
}
scenarios := []scenario{
@@ -1559,9 +1559,10 @@ func TestGitCommandCurrentBranchName(t *testing.T) {
assert.Equal(t, "git", cmd)
return exec.Command("echo", "master")
},
func(output string, err error) {
func(name string, displayname string, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "master", output)
assert.EqualValues(t, "master", name)
assert.EqualValues(t, "master", displayname)
},
},
{
@@ -1580,9 +1581,32 @@ func TestGitCommandCurrentBranchName(t *testing.T) {
return nil
},
func(output string, err error) {
func(name string, displayname string, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "master", output)
assert.EqualValues(t, "master", name)
assert.EqualValues(t, "master", displayname)
},
},
{
"handles a detached head",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
switch args[0] {
case "symbolic-ref":
assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
return exec.Command("test")
case "branch":
assert.EqualValues(t, []string{"branch", "--contains"}, args)
return exec.Command("echo", "* (HEAD detached at 123abcd)")
}
return nil
},
func(name string, displayname string, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "123abcd", name)
assert.EqualValues(t, "(HEAD detached at 123abcd)", displayname)
},
},
{
@@ -1591,9 +1615,10 @@ func TestGitCommandCurrentBranchName(t *testing.T) {
assert.Equal(t, "git", cmd)
return exec.Command("test")
},
func(output string, err error) {
func(name string, displayname string, err error) {
assert.Error(t, err)
assert.EqualValues(t, "", output)
assert.EqualValues(t, "", name)
assert.EqualValues(t, "", displayname)
},
},
}
@@ -1907,7 +1932,7 @@ func TestGitCommandGetCommitFiles(t *testing.T) {
"123456",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git show --pretty= --name-only --no-renames 123456",
Expect: "git diff-tree --no-commit-id --name-only -r --no-renames 123456",
Replace: "echo 'hello\nworld'",
},
}),

View File

@@ -1,6 +1,7 @@
package commands
import (
"bufio"
"fmt"
"io/ioutil"
"os"
@@ -64,6 +65,22 @@ func (c *OSCommand) SetBeforeExecuteCmd(cmd func(*exec.Cmd)) {
c.beforeExecuteCmd = cmd
}
type RunCommandOptions struct {
EnvVars []string
}
func (c *OSCommand) RunCommandWithOutputWithOptions(command string, options RunCommandOptions) (string, error) {
c.Log.WithField("command", command).Info("RunCommand")
cmd := c.ExecutableFromString(command)
cmd.Env = append(cmd.Env, options.EnvVars...)
return sanitisedCommandOutput(cmd.CombinedOutput())
}
func (c *OSCommand) RunCommandWithOptions(command string, options RunCommandOptions) error {
_, err := c.RunCommandWithOutputWithOptions(command, options)
return err
}
// RunCommandWithOutput wrapper around commands returning their output and error
// NOTE: If you don't pass any formatArgs we'll just use the command directly,
// however there's a bizarre compiler error/warning when you pass in a formatString
@@ -409,3 +426,31 @@ func Kill(cmd *exec.Cmd) error {
}
return cmd.Process.Kill()
}
func RunLineOutputCmd(cmd *exec.Cmd, onLine func(line string) (bool, error)) error {
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
return err
}
scanner := bufio.NewScanner(stdoutPipe)
scanner.Split(bufio.ScanLines)
if err := cmd.Start(); err != nil {
return err
}
for scanner.Scan() {
line := scanner.Text()
stop, err := onLine(line)
if err != nil {
return err
}
if stop {
cmd.Process.Kill()
break
}
}
cmd.Wait()
return nil
}

View File

@@ -314,6 +314,8 @@ keybinding:
prevTab: '['
nextScreenMode: '+'
prevScreenMode: '_'
undo: 'z'
redo: '<c-z>'
status:
checkForUpdate: 'u'
recentRepos: '<enter>'
@@ -370,7 +372,6 @@ keybinding:
toggleDragSelect-alt: 'V'
toggleSelectHunk: 'a'
pickBothHunks: 'b'
undo: 'z'
`)
}

View File

@@ -106,7 +106,7 @@ func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
}
branch := gui.getSelectedBranch()
return gui.handleCheckoutRef(branch.Name)
return gui.handleCheckoutRef(branch.Name, handleCheckoutRefOptions{})
}
func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error {
@@ -136,53 +136,74 @@ func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
message := gui.Tr.SLocalize("SureForceCheckout")
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 {
if err := gui.GitCommand.Checkout(branch.Name, commands.CheckoutOptions{Force: true}); err != nil {
_ = gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(g)
}, nil)
}
func (gui *Gui) handleCheckoutRef(ref string) error {
if err := gui.GitCommand.Checkout(ref, false); err != nil {
// note, this will only work for english-language git commands. If we force git to use english, and the error isn't this one, then the user will receive an english command they may not understand. I'm not sure what the best solution to this is. Running the command once in english and a second time in the native language is one option
type handleCheckoutRefOptions struct {
WaitingStatus string
EnvVars []string
}
if strings.Contains(err.Error(), "Please commit your changes or stash them before you switch branch") {
// offer to autostash changes
return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("AutoStashTitle"), gui.Tr.SLocalize("AutoStashPrompt"), func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + ref); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.GitCommand.Checkout(ref, false); err != nil {
return gui.createErrorPanel(g, err.Error())
}
// checkout successful so we select the new branch
gui.State.Panels.Branches.SelectedLine = 0
if err := gui.GitCommand.StashDo(0, "pop"); err != nil {
if err := gui.refreshSidePanels(g); err != nil {
return err
}
return gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(g)
}, nil)
}
if err := gui.createErrorPanel(gui.g, err.Error()); err != nil {
return err
}
func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions) error {
waitingStatus := options.WaitingStatus
if waitingStatus == "" {
waitingStatus = gui.Tr.SLocalize("CheckingOutStatus")
}
gui.State.Panels.Branches.SelectedLine = 0
gui.State.Panels.Commits.SelectedLine = 0
return gui.refreshSidePanels(gui.g)
cmdOptions := commands.CheckoutOptions{Force: false, EnvVars: options.EnvVars}
onSuccess := func() {
gui.State.Panels.Branches.SelectedLine = 0
gui.State.Panels.Commits.SelectedLine = 0
// loading a heap of commits is slow so we limit them whenever doing a reset
gui.State.Panels.Commits.LimitCommits = true
}
return gui.WithWaitingStatus(waitingStatus, func() error {
if err := gui.GitCommand.Checkout(ref, cmdOptions); err != nil {
// note, this will only work for english-language git commands. If we force git to use english, and the error isn't this one, then the user will receive an english command they may not understand. I'm not sure what the best solution to this is. Running the command once in english and a second time in the native language is one option
if strings.Contains(err.Error(), "Please commit your changes or stash them before you switch branch") {
// offer to autostash changes
return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("AutoStashTitle"), gui.Tr.SLocalize("AutoStashPrompt"), func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + ref); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.GitCommand.Checkout(ref, cmdOptions); err != nil {
return gui.createErrorPanel(g, err.Error())
}
onSuccess()
if err := gui.GitCommand.StashDo(0, "pop"); err != nil {
if err := gui.refreshSidePanels(g); err != nil {
return err
}
return gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(g)
}, nil)
}
if err := gui.createErrorPanel(gui.g, err.Error()); err != nil {
return err
}
}
onSuccess()
return gui.refreshSidePanels(gui.g)
})
}
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 {
return gui.handleCheckoutRef(gui.trimmedContent(v))
return gui.handleCheckoutRef(gui.trimmedContent(v), handleCheckoutRefOptions{})
})
}
@@ -456,7 +477,7 @@ func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) 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 {
if err := gui.GitCommand.Checkout(newName, commands.CheckoutOptions{Force: false}); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
@@ -477,5 +498,8 @@ func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) currentBranch() *commands.Branch {
if len(gui.State.Branches) == 0 {
return nil
}
return gui.State.Branches[0]
}

View File

@@ -35,7 +35,7 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
}
state := gui.State.Panels.Commits
if state.SelectedLine > 20 && state.LimitCommits {
if state.SelectedLine > 290 && state.LimitCommits {
state.LimitCommits = false
go func() {
if err := gui.refreshCommitsWithLimit(); err != nil {
@@ -351,14 +351,21 @@ func (gui *Gui) handleCopyCommit(g *gocui.Gui, v *gocui.View) error {
return gui.refreshCommits(gui.g)
}
func (gui *Gui) cherryPickedCommitShaMap() map[string]bool {
commitShaMap := map[string]bool{}
for _, commit := range gui.State.CherryPickedCommits {
commitShaMap[commit.Sha] = true
}
return commitShaMap
}
func (gui *Gui) addCommitToCherryPickedCommits(index int) {
// not super happy with modifying the state of the Commits array here
// but the alternative would be very tricky
gui.State.Commits[index].Copied = true
commitShaMap := gui.cherryPickedCommitShaMap()
commitShaMap[gui.State.Commits[index].Sha] = true
newCommits := []*commands.Commit{}
for _, commit := range gui.State.Commits {
if commit.Copied {
if commitShaMap[commit.Sha] {
// duplicating just the things we need to put in the rebase TODO list
newCommits = append(newCommits, &commands.Commit{Name: commit.Name, Sha: commit.Sha})
}
@@ -368,13 +375,13 @@ func (gui *Gui) addCommitToCherryPickedCommits(index int) {
}
func (gui *Gui) handleCopyCommitRange(g *gocui.Gui, v *gocui.View) error {
// whenever I add a commit, I need to make sure I retain its order
commitShaMap := gui.cherryPickedCommitShaMap()
// find the last commit that is copied that's above our position
// if there are none, startIndex = 0
startIndex := 0
for index, commit := range gui.State.Commits[0:gui.State.Panels.Commits.SelectedLine] {
if commit.Copied {
if commitShaMap[commit.Sha] {
startIndex = index
}
}
@@ -385,7 +392,7 @@ func (gui *Gui) handleCopyCommitRange(g *gocui.Gui, v *gocui.View) error {
gui.addCommitToCherryPickedCommits(index)
}
return gui.refreshCommits(gui.g)
return gui.renderBranchCommitsWithSelection()
}
// HandlePasteCommits begins a cherry-pick rebase with the commits the user has copied
@@ -540,7 +547,7 @@ func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error {
}
return gui.createConfirmationPanel(g, gui.getCommitsView(), true, gui.Tr.SLocalize("checkoutCommit"), gui.Tr.SLocalize("SureCheckoutThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
return gui.handleCheckoutRef(commit.Sha)
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
}, nil)
}
@@ -548,7 +555,7 @@ 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)
displayStrings := presentation.GetCommitListDisplayStrings(gui.State.Commits, gui.State.ScreenMode != SCREEN_NORMAL, gui.cherryPickedCommitShaMap())
gui.renderDisplayStrings(commitsView, displayStrings)
if gui.g.CurrentView() == commitsView && commitsView.Context == "branch-commits" {
if err := gui.handleCommitSelect(gui.g, commitsView); err != nil {

View File

@@ -39,7 +39,8 @@ func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui, returnFocusOnClose bool) e
panic(err)
}
}
g.DeleteKeybindings("confirmation")
g.DeleteKeybinding("confirmation", gocui.KeyEnter, gocui.ModNone)
g.DeleteKeybinding("confirmation", gocui.KeyEsc, gocui.ModNone)
return g.DeleteView("confirmation")
}
@@ -61,6 +62,9 @@ func (gui *Gui) getConfirmationPanelDimensions(g *gocui.Gui, wrap bool, prompt s
width, height := g.Size()
panelWidth := 4 * width / 7
panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth)
if panelHeight > height*3/4 {
panelHeight = height * 3 / 4
}
return width/2 - panelWidth/2,
height/2 - panelHeight/2 - panelHeight%2 - 1,
width/2 + panelWidth/2,
@@ -149,6 +153,7 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go
"keyBindConfirm": "enter",
},
)
gui.renderString(g, "options", actions)
if err := g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(handleConfirm, returnFocusOnClose)); err != nil {
return err

View File

@@ -343,6 +343,20 @@ func (gui *Gui) scrollDownSecondary(g *gocui.Gui, v *gocui.View) error {
return gui.scrollDownView("secondary")
}
func (gui *Gui) scrollUpConfirmationPanel(g *gocui.Gui, v *gocui.View) error {
if v.Editable {
return nil
}
return gui.scrollUpView("confirmation")
}
func (gui *Gui) scrollDownConfirmationPanel(g *gocui.Gui, v *gocui.View) error {
if v.Editable {
return nil
}
return gui.scrollDownView("confirmation")
}
func (gui *Gui) handleRefresh(g *gocui.Gui, v *gocui.View) error {
return gui.refreshSidePanels(g)
}

View File

@@ -313,6 +313,20 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleCreateOptionsMenu,
},
{
ViewName: "",
Key: gui.getKey("universal.undo"),
Modifier: gocui.ModNone,
Handler: gui.reflogUndo,
Description: gui.Tr.SLocalize("undoReflog"),
},
{
ViewName: "",
Key: gui.getKey("universal.redo"),
Modifier: gocui.ModNone,
Handler: gui.reflogRedo,
Description: gui.Tr.SLocalize("redoReflog"),
},
{
ViewName: "status",
Key: gui.getKey("universal.edit"),
@@ -618,16 +632,18 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.SLocalize("viewResetOptions"),
},
{
ViewName: "branches",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextBranchesTab,
ViewName: "branches",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextBranchesTab,
Description: gui.Tr.SLocalize("nextTab"),
},
{
ViewName: "branches",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevBranchesTab,
ViewName: "branches",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevBranchesTab,
Description: gui.Tr.SLocalize("prevTab"),
},
{
ViewName: "branches",
@@ -654,16 +670,18 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.SLocalize("fetchRemote"),
},
{
ViewName: "commits",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextCommitsTab,
ViewName: "commits",
Key: gui.getKey("universal.nextTab"),
Modifier: gocui.ModNone,
Handler: gui.handleNextCommitsTab,
Description: gui.Tr.SLocalize("nextTab"),
},
{
ViewName: "commits",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevCommitsTab,
ViewName: "commits",
Key: gui.getKey("universal.prevTab"),
Modifier: gocui.ModNone,
Handler: gui.handlePrevCommitsTab,
Description: gui.Tr.SLocalize("prevTab"),
},
{
ViewName: "commits",
@@ -1338,10 +1356,10 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
{
ViewName: "main",
Contexts: []string{"merging"},
Key: gui.getKey("main.undo"),
Key: gui.getKey("universal.undo"),
Modifier: gocui.ModNone,
Handler: gui.handlePopFileSnapshot,
Description: gui.Tr.SLocalize("Undo"),
Description: gui.Tr.SLocalize("undo"),
},
{
ViewName: "branches",
@@ -1444,6 +1462,30 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleSearchEscape,
},
{
ViewName: "confirmation",
Key: gui.getKey("universal.prevItem"),
Modifier: gocui.ModNone,
Handler: gui.scrollUpConfirmationPanel,
},
{
ViewName: "confirmation",
Key: gui.getKey("universal.nextItem"),
Modifier: gocui.ModNone,
Handler: gui.scrollDownConfirmationPanel,
},
{
ViewName: "confirmation",
Key: gui.getKey("universal.prevItem-alt"),
Modifier: gocui.ModNone,
Handler: gui.scrollUpConfirmationPanel,
},
{
ViewName: "confirmation",
Key: gui.getKey("universal.nextItem-alt"),
Modifier: gocui.ModNone,
Handler: gui.scrollDownConfirmationPanel,
},
}
for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} {
@@ -1474,7 +1516,14 @@ 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,
Description: gui.Tr.SLocalize("startSearch"),
})
}
}

View File

@@ -255,7 +255,7 @@ func (gui *Gui) renderMergeOptions() error {
fmt.Sprintf("%s %s", gui.getKeyDisplay("universal.prevBlock"), gui.getKeyDisplay("universal.nextBlock")): gui.Tr.SLocalize("navigateConflicts"),
gui.getKeyDisplay("universal.select"): gui.Tr.SLocalize("pickHunk"),
gui.getKeyDisplay("main.pickBothHunks"): gui.Tr.SLocalize("pickBothHunks"),
gui.getKeyDisplay("main.undo"): gui.Tr.SLocalize("undo"),
gui.getKeyDisplay("universal.undo"): gui.Tr.SLocalize("undo"),
})
}

View File

@@ -20,6 +20,10 @@ func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error
displayString: "pull patch out into index",
onPress: gui.handlePullPatchIntoWorkingTree,
},
{
displayString: "apply patch",
onPress: gui.handleApplyPatch,
},
{
displayString: "reset patch",
onPress: gui.handleResetPatch,
@@ -116,6 +120,17 @@ func (gui *Gui) handlePullPatchIntoWorkingTree() error {
})
}
func (gui *Gui) handleApplyPatch() error {
if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
return err
}
if err := gui.GitCommand.PatchManager.ApplyPatches(false); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
return gui.refreshSidePanels(gui.g)
}
func (gui *Gui) handleResetPatch() error {
gui.GitCommand.PatchManager.Reset()
return gui.refreshCommitFilesView()

View File

@@ -22,14 +22,18 @@ func GetBranchListDisplayStrings(branches []*commands.Branch, fullDescription bo
// getBranchDisplayStrings returns the display string of branch
func getBranchDisplayStrings(b *commands.Branch, fullDescription bool) []string {
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name))
displayName := b.Name
if b.DisplayName != "" {
displayName = b.DisplayName
}
coloredName := utils.ColoredString(displayName, 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)
coloredName = fmt.Sprintf("%s %s", coloredName, track)
}
recencyColor := color.FgCyan
@@ -38,10 +42,10 @@ func getBranchDisplayStrings(b *commands.Branch, fullDescription bool) []string
}
if fullDescription {
return []string{utils.ColoredString(b.Recency, recencyColor), displayName, utils.ColoredString(b.UpstreamName, color.FgYellow)}
return []string{utils.ColoredString(b.Recency, recencyColor), coloredName, utils.ColoredString(b.UpstreamName, color.FgYellow)}
}
return []string{utils.ColoredString(b.Recency, recencyColor), displayName}
return []string{utils.ColoredString(b.Recency, recencyColor), coloredName}
}
// GetBranchColor branch color

View File

@@ -9,10 +9,10 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool) [][]string {
func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool) [][]string {
lines := make([][]string, len(commits))
var displayFunc func(*commands.Commit) []string
var displayFunc func(*commands.Commit, map[string]bool) []string
if fullDescription {
displayFunc = getFullDescriptionDisplayStringsForCommit
} else {
@@ -20,13 +20,13 @@ func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription boo
}
for i := range commits {
lines[i] = displayFunc(commits[i])
lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap)
}
return lines
}
func getFullDescriptionDisplayStringsForCommit(c *commands.Commit) []string {
func getFullDescriptionDisplayStringsForCommit(c *commands.Commit, cherryPickedCommitShaMap map[string]bool) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
@@ -58,7 +58,7 @@ func getFullDescriptionDisplayStringsForCommit(c *commands.Commit) []string {
shaColor = defaultColor
}
if c.Copied {
if cherryPickedCommitShaMap[c.Sha] {
shaColor = copied
}
@@ -74,10 +74,10 @@ func getFullDescriptionDisplayStringsForCommit(c *commands.Commit) []string {
truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)
return []string{shaColor.Sprint(c.Sha[:8]), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(c.Name)}
return []string{shaColor.Sprint(c.ShortSha()), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(c.Name)}
}
func getDisplayStringsForCommit(c *commands.Commit) []string {
func getDisplayStringsForCommit(c *commands.Commit, cherryPickedCommitShaMap map[string]bool) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
@@ -109,7 +109,7 @@ func getDisplayStringsForCommit(c *commands.Commit) []string {
shaColor = defaultColor
}
if c.Copied {
if cherryPickedCommitShaMap[c.Sha] {
shaColor = copied
}
@@ -122,5 +122,5 @@ func getDisplayStringsForCommit(c *commands.Commit) []string {
tagString = utils.ColoredStringDirect(strings.Join(c.Tags, " "), tagColor) + " "
}
return []string{shaColor.Sprint(c.Sha[:8]), actionString + tagString + defaultColor.Sprint(c.Name)}
return []string{shaColor.Sprint(c.ShortSha()), actionString + tagString + defaultColor.Sprint(c.Name)}
}

View File

@@ -0,0 +1,37 @@
package presentation
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetReflogCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool) [][]string {
lines := make([][]string, len(commits))
var displayFunc func(*commands.Commit) []string
if fullDescription {
displayFunc = getFullDescriptionDisplayStringsForReflogCommit
} else {
displayFunc = getDisplayStringsForReflogCommit
}
for i := range commits {
lines[i] = displayFunc(commits[i])
}
return lines
}
func getFullDescriptionDisplayStringsForReflogCommit(c *commands.Commit) []string {
defaultColor := color.New(theme.DefaultTextColor)
return []string{utils.ColoredString(c.ShortSha(), color.FgBlue), utils.ColoredString(c.Date, color.FgMagenta), defaultColor.Sprint(c.Name)}
}
func getDisplayStringsForReflogCommit(c *commands.Commit) []string {
defaultColor := color.New(theme.DefaultTextColor)
return []string{utils.ColoredString(c.ShortSha(), color.FgBlue), defaultColor.Sprint(c.Name)}
}

View File

@@ -5,7 +5,7 @@ package gui
import (
"os/exec"
"github.com/jesseduffield/pty"
"github.com/creack/pty"
)
func (gui *Gui) onResize() error {

View File

@@ -47,12 +47,17 @@ func (gui *Gui) handleReflogCommitSelect(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) refreshReflogCommits() error {
commits, err := gui.GitCommand.GetReflogCommits()
var lastReflogCommit *commands.Commit
if len(gui.State.ReflogCommits) > 0 {
lastReflogCommit = gui.State.ReflogCommits[0]
}
commits, err := gui.GitCommand.GetNewReflogCommits(lastReflogCommit)
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
gui.State.ReflogCommits = commits
gui.State.ReflogCommits = append(commits, gui.State.ReflogCommits...)
if gui.getCommitsView().Context == "reflog-commits" {
return gui.renderReflogCommitsWithSelection()
@@ -65,7 +70,7 @@ 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)
displayStrings := presentation.GetReflogCommitListDisplayStrings(gui.State.ReflogCommits, gui.State.ScreenMode != SCREEN_NORMAL)
gui.renderDisplayStrings(commitsView, displayStrings)
if gui.g.CurrentView() == commitsView && commitsView.Context == "reflog-commits" {
if err := gui.handleReflogCommitSelect(gui.g, commitsView); err != nil {
@@ -83,7 +88,7 @@ func (gui *Gui) handleCheckoutReflogCommit(g *gocui.Gui, v *gocui.View) error {
}
err := gui.createConfirmationPanel(g, gui.getCommitsView(), true, gui.Tr.SLocalize("checkoutCommit"), gui.Tr.SLocalize("SureCheckoutThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
return gui.handleCheckoutRef(commit.Sha)
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
}, nil)
if err != nil {
return err

View File

@@ -76,7 +76,7 @@ func (gui *Gui) handleCheckoutRemoteBranch(g *gocui.Gui, v *gocui.View) error {
if remoteBranch == nil {
return nil
}
if err := gui.handleCheckoutRef(remoteBranch.RemoteName + "/" + remoteBranch.Name); err != nil {
if err := gui.handleCheckoutRef(remoteBranch.RemoteName+"/"+remoteBranch.Name, handleCheckoutRefOptions{}); err != nil {
return err
}
return gui.switchBranchesPanelContext("local-branches")

View File

@@ -4,13 +4,44 @@ import (
"fmt"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
)
func (gui *Gui) resetToRef(ref string, strength string, options commands.RunCommandOptions) error {
if err := gui.GitCommand.ResetToCommit(ref, strength, options); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
if err := gui.switchCommitsPanelContext("branch-commits"); err != nil {
return err
}
gui.State.Panels.Commits.SelectedLine = 0
gui.State.Panels.ReflogCommits.SelectedLine = 0
// loading a heap of commits is slow so we limit them whenever doing a reset
gui.State.Panels.Commits.LimitCommits = true
if err := gui.refreshCommits(gui.g); err != nil {
return err
}
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
}
return gui.handleCommitSelect(gui.g, gui.getCommitsView())
}
func (gui *Gui) createResetMenu(ref string) error {
strengths := []string{"soft", "mixed", "hard"}
menuItems := make([]*menuItem, len(strengths))
for i, strength := range strengths {
innerStrength := strength
strength := strength
menuItems[i] = &menuItem{
displayStrings: []string{
fmt.Sprintf("%s reset", strength),
@@ -19,31 +50,7 @@ func (gui *Gui) createResetMenu(ref string) error {
),
},
onPress: func() error {
if err := gui.GitCommand.ResetToCommit(ref, innerStrength); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
if err := gui.switchCommitsPanelContext("branch-commits"); err != nil {
return err
}
gui.State.Panels.Commits.SelectedLine = 0
gui.State.Panels.ReflogCommits.SelectedLine = 0
if err := gui.refreshCommits(gui.g); err != nil {
return err
}
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
}
return gui.handleCommitSelect(gui.g, gui.getCommitsView())
return gui.resetToRef(ref, strength, commands.RunCommandOptions{})
},
}
}

View File

@@ -81,7 +81,7 @@ func (gui *Gui) handleCheckoutTag(g *gocui.Gui, v *gocui.View) error {
if tag == nil {
return nil
}
if err := gui.handleCheckoutRef(tag.Name); err != nil {
if err := gui.handleCheckoutRef(tag.Name, handleCheckoutRefOptions{}); err != nil {
return err
}
return gui.switchBranchesPanelContext("local-branches")

View File

@@ -81,7 +81,6 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
},
func() {
gui.g.Update(func(*gocui.Gui) error {
gui.Log.Warn("updating view")
return nil
})
})

202
pkg/gui/undoing.go Normal file
View File

@@ -0,0 +1,202 @@
package gui
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// Quick summary of how this all works:
// when you want to undo or redo, we start from the top of the reflog and work
// down until we've reached the last user-initiated reflog entry that hasn't already been undone
// we then do the reverse of what that reflog describes.
// When we do this, we create a new reflog entry, and tag it as either an undo or redo
// Then, next time we want to undo, we'll use those entries to know which user-initiated
// actions we can skip. E.g. if I do do three things, A, B, and C, and hit undo twice,
// the reflog will read UUCBA, and when I read the first two undos, I know to skip the following
// two user actions, meaning we end up undoing reflog entry C. Redoing works in a similar way.
const (
CHECKOUT = iota
COMMIT
REBASE
CURRENT_REBASE
)
type reflogAction struct {
kind int // one of CHECKOUT, REBASE, and COMMIT
from string
to string
}
// Here we're going through the reflog and maintaining a counter that represents how many
// undos/redos/user actions we've seen. when we hit a user action we call the callback specifying
// what the counter is up to and the nature of the action.
// If we find ourselves mid-rebase, we just return because undo/redo mid rebase
// requires knowledge of previous TODO file states, which you can't just get from the reflog.
// Though we might support this later, hence the use of the CURRENT_REBASE action kind.
func (gui *Gui) parseReflogForActions(onUserAction func(counter int, action reflogAction) (bool, error)) error {
counter := 0
reflogCommits := gui.State.ReflogCommits
rebaseFinishCommitSha := ""
var action *reflogAction
for reflogCommitIdx, reflogCommit := range reflogCommits {
action = nil
prevCommitSha := ""
if len(reflogCommits)-1 >= reflogCommitIdx+1 {
prevCommitSha = reflogCommits[reflogCommitIdx+1].Sha
}
if rebaseFinishCommitSha == "" {
if ok, _ := utils.FindStringSubmatch(reflogCommit.Name, `^\[lazygit undo\]`); ok {
counter++
} else if ok, _ := utils.FindStringSubmatch(reflogCommit.Name, `^\[lazygit redo\]`); ok {
counter--
} else if ok, _ := utils.FindStringSubmatch(reflogCommit.Name, `^rebase -i \(abort\)|^rebase -i \(finish\)`); ok {
rebaseFinishCommitSha = reflogCommit.Sha
} else if ok, match := utils.FindStringSubmatch(reflogCommit.Name, `^checkout: moving from ([\S]+) to ([\S]+)`); ok {
action = &reflogAction{kind: CHECKOUT, from: match[1], to: match[2]}
} else if ok, _ := utils.FindStringSubmatch(reflogCommit.Name, `^commit|^reset: moving to|^pull`); ok {
action = &reflogAction{kind: COMMIT, from: prevCommitSha, to: reflogCommit.Sha}
} else if ok, _ := utils.FindStringSubmatch(reflogCommit.Name, `^rebase -i \(start\)`); ok {
// if we're here then we must be currently inside an interactive rebase
action = &reflogAction{kind: CURRENT_REBASE, from: prevCommitSha}
}
} else if ok, _ := utils.FindStringSubmatch(reflogCommit.Name, `^rebase -i \(start\)`); ok {
action = &reflogAction{kind: REBASE, from: prevCommitSha, to: rebaseFinishCommitSha}
rebaseFinishCommitSha = ""
}
if action != nil {
if action.kind != CURRENT_REBASE && action.from == action.to {
// if we're going from one place to the same place we'll ignore the action.
continue
}
ok, err := onUserAction(counter, *action)
if ok {
return err
}
counter--
}
}
return nil
}
func (gui *Gui) reflogUndo(g *gocui.Gui, v *gocui.View) error {
undoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit undo]"}
undoingStatus := gui.Tr.SLocalize("UndoingStatus")
if gui.State.WorkingTreeState == "rebasing" {
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("cantUndoWhileRebasing"))
}
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
if counter != 0 {
return false, nil
}
switch action.kind {
case COMMIT, REBASE:
return true, gui.handleHardResetWithAutoStash(action.from, handleHardResetWithAutoStashOptions{
EnvVars: undoEnvVars,
WaitingStatus: undoingStatus,
})
case CHECKOUT:
return true, gui.handleCheckoutRef(action.from, handleCheckoutRefOptions{
EnvVars: undoEnvVars,
WaitingStatus: undoingStatus,
})
}
gui.Log.Error("didn't match on the user action when trying to undo")
return true, nil
})
}
func (gui *Gui) reflogRedo(g *gocui.Gui, v *gocui.View) error {
redoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit redo]"}
redoingStatus := gui.Tr.SLocalize("RedoingStatus")
if gui.State.WorkingTreeState == "rebasing" {
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("cantRedoWhileRebasing"))
}
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
// if we're redoing and the counter is zero, we just return
if counter == 0 {
return true, nil
} else if counter > 1 {
return false, nil
}
switch action.kind {
case COMMIT, REBASE:
return true, gui.handleHardResetWithAutoStash(action.to, handleHardResetWithAutoStashOptions{
EnvVars: redoEnvVars,
WaitingStatus: redoingStatus,
})
case CHECKOUT:
return true, gui.handleCheckoutRef(action.to, handleCheckoutRefOptions{
EnvVars: redoEnvVars,
WaitingStatus: redoingStatus,
})
}
gui.Log.Error("didn't match on the user action when trying to redo")
return true, nil
})
}
type handleHardResetWithAutoStashOptions struct {
WaitingStatus string
EnvVars []string
}
// only to be used in the undo flow for now
func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHardResetWithAutoStashOptions) error {
// if we have any modified tracked files we need to ask the user if they want us to stash for them
dirtyWorkingTree := false
for _, file := range gui.State.Files {
if file.Tracked {
dirtyWorkingTree = true
break
}
}
reset := func() error {
if err := gui.resetToRef(commitSha, "hard", commands.RunCommandOptions{EnvVars: options.EnvVars}); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
return nil
}
if dirtyWorkingTree {
// offer to autostash changes
return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("AutoStashTitle"), gui.Tr.SLocalize("AutoStashPrompt"), func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(options.WaitingStatus, func() error {
if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + commitSha); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := reset(); err != nil {
return err
}
if err := gui.GitCommand.StashDo(0, "pop"); err != nil {
if err := gui.refreshSidePanels(g); err != nil {
return err
}
return gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(g)
})
}, nil)
}
return gui.WithWaitingStatus(options.WaitingStatus, func() error {
if err := reset(); err != nil {
return err
}
return gui.refreshSidePanels(gui.g)
})
}

View File

@@ -360,6 +360,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "undo",
Other: "undo",
}, &i18n.Message{
ID: "undoReflog",
Other: "undo (via reflog) (experimental)",
}, &i18n.Message{
ID: "redoReflog",
Other: "redo (via reflog) (experimental)",
}, &i18n.Message{
ID: "pop",
Other: "pop",
@@ -741,6 +747,15 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CherryPickingStatus",
Other: "cherry-picking",
}, &i18n.Message{
ID: "UndoingStatus",
Other: "undoing",
}, &i18n.Message{
ID: "RedoingStatus",
Other: "redoing",
}, &i18n.Message{
ID: "CheckingOutStatus",
Other: "checking out",
}, &i18n.Message{
ID: "CommitFiles",
Other: "Commit files",
@@ -1035,6 +1050,18 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &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",
}, &i18n.Message{
ID: "cantUndoWhileRebasing",
Other: "Can't undo while rebasing",
}, &i18n.Message{
ID: "cantRedoWhileRebasing",
Other: "Can't redo while rebasing",
},
)
}

View File

@@ -316,3 +316,9 @@ func TruncateWithEllipsis(str string, limit int) string {
remainingLength := limit - len(ellipsis)
return str[0:remainingLength] + "..."
}
func FindStringSubmatch(str string, regexpStr string) (bool, []string) {
re := regexp.MustCompile(regexpStr)
match := re.FindStringSubmatch(str)
return len(match) > 0, match
}

14
vendor/github.com/creack/pty/Dockerfile.riscv generated vendored Normal file
View File

@@ -0,0 +1,14 @@
FROM golang:1.13
# Clone and complie a riscv compatible version of the go compiler.
RUN git clone https://review.gerrithub.io/riscv/riscv-go /riscv-go
# riscvdev branch HEAD as of 2019-06-29.
RUN cd /riscv-go && git checkout 04885fddd096d09d4450726064d06dd107e374bf
ENV PATH=/riscv-go/misc/riscv:/riscv-go/bin:$PATH
RUN cd /riscv-go/src && GOROOT_BOOTSTRAP=$(go env GOROOT) ./make.bash
ENV GOROOT=/riscv-go
# Make sure we compile.
WORKDIR pty
ADD . .
RUN GOOS=linux GOARCH=riscv go build

View File

@@ -4,7 +4,7 @@ Pty is a Go package for using unix pseudo-terminals.
## Install
go get github.com/kr/pty
go get github.com/creack/pty
## Example
@@ -14,7 +14,7 @@ Pty is a Go package for using unix pseudo-terminals.
package main
import (
"github.com/kr/pty"
"github.com/creack/pty"
"io"
"os"
"os/exec"
@@ -50,7 +50,7 @@ import (
"os/signal"
"syscall"
"github.com/kr/pty"
"github.com/creack/pty"
"golang.org/x/crypto/ssh/terminal"
)

4
vendor/github.com/creack/pty/go.mod generated vendored Normal file
View File

@@ -0,0 +1,4 @@
module github.com/creack/pty
go 1.13

View File

@@ -1,4 +1,4 @@
// +build !windows
// +build !windows,!solaris
package pty

30
vendor/github.com/creack/pty/ioctl_solaris.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
package pty
import (
"golang.org/x/sys/unix"
"unsafe"
)
const (
// see /usr/include/sys/stropts.h
I_PUSH = uintptr((int32('S')<<8 | 002))
I_STR = uintptr((int32('S')<<8 | 010))
I_FIND = uintptr((int32('S')<<8 | 013))
// see /usr/include/sys/ptms.h
ISPTM = (int32('P') << 8) | 1
UNLKPT = (int32('P') << 8) | 2
PTSSTTY = (int32('P') << 8) | 3
ZONEPT = (int32('P') << 8) | 4
OWNERPT = (int32('P') << 8) | 5
)
type strioctl struct {
ic_cmd int32
ic_timout int32
ic_len int32
ic_dp unsafe.Pointer
}
func ioctl(fd, cmd, ptr uintptr) error {
return unix.IoctlSetInt(int(fd), uint(cmd), int(ptr))
}

View File

@@ -46,6 +46,6 @@ func ptsname(f *os.File) (string, error) {
func unlockpt(f *os.File) error {
var u _C_int
// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
// use TIOCSPTLCK with a pointer to zero to clear the lock
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
}

View File

@@ -11,7 +11,7 @@ func open() (pty, tty *os.File, err error) {
* from ptm(4):
* The PTMGET command allocates a free pseudo terminal, changes its
* ownership to the caller, revokes the access privileges for all previous
* users, opens the file descriptors for the master and slave devices and
* users, opens the file descriptors for the pty and tty devices and
* returns them to the caller in struct ptmget.
*/

139
vendor/github.com/creack/pty/pty_solaris.go generated vendored Normal file
View File

@@ -0,0 +1,139 @@
package pty
/* based on:
http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c
*/
import (
"errors"
"golang.org/x/sys/unix"
"os"
"strconv"
"syscall"
"unsafe"
)
const NODEV = ^uint64(0)
func open() (pty, tty *os.File, err error) {
masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|unix.O_NOCTTY, 0)
//masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC|unix.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
p := os.NewFile(uintptr(masterfd), "/dev/ptmx")
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
err = grantpt(p)
if err != nil {
return nil, nil, err
}
err = unlockpt(p)
if err != nil {
return nil, nil, err
}
slavefd, err := syscall.Open(sname, os.O_RDWR|unix.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
t := os.NewFile(uintptr(slavefd), sname)
// pushing terminal driver STREAMS modules as per pts(7)
for _, mod := range([]string{"ptem", "ldterm", "ttcompat"}) {
err = streams_push(t, mod)
if err != nil {
return nil, nil, err
}
}
return p, t, nil
}
func minor(x uint64) uint64 {
return x & 0377
}
func ptsdev(fd uintptr) uint64 {
istr := strioctl{ISPTM, 0, 0, nil}
err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr)))
if err != nil {
return NODEV
}
var status unix.Stat_t
err = unix.Fstat(int(fd), &status)
if err != nil {
return NODEV
}
return uint64(minor(status.Rdev))
}
func ptsname(f *os.File) (string, error) {
dev := ptsdev(f.Fd())
if dev == NODEV {
return "", errors.New("not a master pty")
}
fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10)
// access(2) creates the slave device (if the pty exists)
// F_OK == 0 (unistd.h)
err := unix.Access(fn, 0)
if err != nil {
return "", err
}
return fn, nil
}
type pt_own struct {
pto_ruid int32
pto_rgid int32
}
func grantpt(f *os.File) error {
if ptsdev(f.Fd()) == NODEV {
return errors.New("not a master pty")
}
var pto pt_own
pto.pto_ruid = int32(os.Getuid())
// XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty"
pto.pto_rgid = int32(os.Getgid())
var istr strioctl
istr.ic_cmd = OWNERPT
istr.ic_timout = 0
istr.ic_len = int32(unsafe.Sizeof(istr))
istr.ic_dp = unsafe.Pointer(&pto)
err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
if err != nil {
return errors.New("access denied")
}
return nil
}
func unlockpt(f *os.File) error {
istr := strioctl{UNLKPT, 0, 0, nil}
return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
}
// push STREAMS modules if not already done so
func streams_push(f *os.File, mod string) error {
var err error
buf := []byte(mod)
// XXX I_FIND is not returning an error when the module
// is already pushed even though truss reports a return
// value of 1. A bug in the Go Solaris syscall interface?
// XXX without this we are at risk of the issue
// https://www.illumos.org/issues/9042
// but since we are not using libc or XPG4.2, we should not be
// double-pushing modules
err = ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0])))
if err != nil {
return nil
}
err = ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
return err
}

View File

@@ -1,4 +1,4 @@
// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd
// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd,!solaris
package pty

View File

@@ -39,12 +39,15 @@ func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) {
if c.Stderr == nil {
c.Stderr = tty
}
c.Stdin = tty
if c.Stdin == nil {
c.Stdin = tty
}
if c.SysProcAttr == nil {
c.SysProcAttr = &syscall.SysProcAttr{}
}
c.SysProcAttr.Setctty = true
c.SysProcAttr.Setsid = true
c.SysProcAttr.Ctty = int(tty.Fd())
err = c.Start()
if err != nil {
pty.Close()

50
vendor/github.com/creack/pty/test_crosscompile.sh generated vendored Normal file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/env sh
# Test script checking that all expected os/arch compile properly.
# Does not actually test the logic, just the compilation so we make sure we don't break code depending on the lib.
echo2() {
echo $@ >&2
}
trap end 0
end() {
[ "$?" = 0 ] && echo2 "Pass." || (echo2 "Fail."; exit 1)
}
cross() {
os=$1
shift
echo2 "Build for $os."
for arch in $@; do
echo2 " - $os/$arch"
GOOS=$os GOARCH=$arch go build
done
echo2
}
set -e
cross linux amd64 386 arm arm64 ppc64 ppc64le s390x mips mipsle mips64 mips64le
cross darwin amd64 386 arm arm64
cross freebsd amd64 386 arm
cross netbsd amd64 386 arm
cross openbsd amd64 386
cross dragonfly amd64
cross solaris amd64
# Not expected to work but should still compile.
cross windows amd64 386 arm
# TODO: Fix compilation error on openbsd/arm.
# TODO: Merge the solaris PR.
# Some os/arch require a different compiler. Run in docker.
if ! hash docker; then
# If docker is not present, stop here.
return
fi
echo2 "Build for linux."
echo2 " - linux/riscv"
docker build -t test -f Dockerfile.riscv .

View File

@@ -1,4 +1,4 @@
// +build !windows
// +build !windows,!solaris
package pty
@@ -8,15 +8,15 @@ import (
"unsafe"
)
// InheritSize applies the terminal size of master to slave. This should be run
// in a signal handler for syscall.SIGWINCH to automatically resize the slave when
// the master receives a window size change notification.
func InheritSize(master, slave *os.File) error {
size, err := GetsizeFull(master)
// InheritSize applies the terminal size of pty to tty. This should be run
// in a signal handler for syscall.SIGWINCH to automatically resize the tty when
// the pty receives a window size change notification.
func InheritSize(pty, tty *os.File) error {
size, err := GetsizeFull(pty)
if err != nil {
return err
}
err = Setsize(slave, size)
err = Setsize(tty, size)
if err != nil {
return err
}

51
vendor/github.com/creack/pty/util_solaris.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
//
package pty
import (
"os"
"golang.org/x/sys/unix"
)
const (
TIOCGWINSZ = 21608 // 'T' << 8 | 104
TIOCSWINSZ = 21607 // 'T' << 8 | 103
)
// Winsize describes the terminal size.
type Winsize struct {
Rows uint16 // ws_row: Number of rows (in cells)
Cols uint16 // ws_col: Number of columns (in cells)
X uint16 // ws_xpixel: Width in pixels
Y uint16 // ws_ypixel: Height in pixels
}
// GetsizeFull returns the full terminal size description.
func GetsizeFull(t *os.File) (size *Winsize, err error) {
var wsz *unix.Winsize
wsz, err = unix.IoctlGetWinsize(int(t.Fd()), TIOCGWINSZ)
if err != nil {
return nil, err
} else {
return &Winsize{wsz.Row, wsz.Col, wsz.Xpixel, wsz.Ypixel}, nil
}
}
// Get Windows Size
func Getsize(t *os.File) (rows, cols int, err error) {
var wsz *unix.Winsize
wsz, err = unix.IoctlGetWinsize(int(t.Fd()), TIOCGWINSZ)
if err != nil {
return 80, 25, err
} else {
return int(wsz.Row), int(wsz.Col), nil
}
}
// Setsize resizes t to s.
func Setsize(t *os.File, ws *Winsize) error {
wsz := unix.Winsize{ws.Rows, ws.Cols, ws.X, ws.Y}
return unix.IoctlSetWinsize(int(t.Fd()), TIOCSWINSZ, &wsz)
}

13
vendor/github.com/creack/pty/ztypes_freebsd_arm64.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0xff
)
type fiodgnameArg struct {
Len int32
Buf *byte
}

11
vendor/github.com/creack/pty/ztypes_riscvx.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs types.go
// +build riscv riscv64
package pty
type (
_C_int int32
_C_uint uint32
)

8
vendor/modules.txt vendored
View File

@@ -1,6 +1,9 @@
# github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
## explicit
github.com/cloudfoundry/jibber_jabber
# github.com/creack/pty v1.1.10-0.20191209115840-8ab47f72e854
## explicit
github.com/creack/pty
# github.com/davecgh/go-spew v1.1.1
github.com/davecgh/go-spew/spew
# github.com/emirpasic/gods v1.12.0
@@ -45,9 +48,6 @@ github.com/jbenet/go-context/io
# github.com/jesseduffield/gocui v0.3.1-0.20200309001002-7765949e1c8a
## explicit
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
@@ -75,8 +75,6 @@ github.com/mattn/go-runewidth
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