Compare commits

...

116 Commits
v0.7 ... v0.8.2

Author SHA1 Message Date
Jesse Duffield
c3767bb3b3 update go.sum 2019-09-15 21:16:19 +10:00
Jesse Duffield
b92d27ee7f force underlying go commands under gox to use the vendor directory 2019-09-15 21:16:19 +10:00
Jesse Duffield
6eff139c40 use vendor directory in test.sh 2019-09-15 21:16:19 +10:00
Jesse Duffield
07462303ab bump gocui 2019-09-15 21:16:19 +10:00
Jesse Duffield
d12f81b44e add autoFetch to config doc 2019-09-08 11:20:35 +10:00
matejcik
600112780c use git.autoFetch config option 2019-09-08 11:20:15 +10:00
matejcik
4c73c8889f move git config options to top-level in default config 2019-09-08 11:20:15 +10:00
matejcik
68d5c2bc10 use gui.g directly 2019-09-08 11:20:15 +10:00
matejcik
7db1fee877 startBackgroundFetch does not return errors 2019-09-08 11:20:15 +10:00
matejcik
8f786e3fd9 configurable auto-fetch 2019-09-08 11:20:15 +10:00
Dawid Dziurla
1c704e11f2 adjust CI to Go modules
relatively brought in line with lazydocker's config
2019-09-01 21:24:03 +10:00
Dawid Dziurla
e0dd1cb29d switch to Go modules 2019-09-01 21:24:03 +10:00
Giorgio Previtera
827837b0b9 477 Remove unnecessary variable check
hasInlineMergeConflicts is always true with hasMergeConflicts is true
2019-07-27 11:05:23 +10:00
Giorgio Previtera
e83ef9858b #477 Remove NeedMerge boolean
Instead of storing the status in a new variable, derive it from
the existing three fields
2019-07-27 11:05:23 +10:00
Giorgio Previtera
504d506575 477 Add new NeedReset property to File and update tests
Use a boolean to determin if a file needs to be reset. We want to reset
the file when discrading changes if there is a conflict.
2019-07-27 11:05:23 +10:00
Giorgio Previtera
823b436b53 477 Remove duplicate checkout
We already checout the file calling `c.DiscardUnstagedFileChanges`
2019-07-27 11:05:23 +10:00
Giorgio Previtera
212327d746 #477 Discard changes when there are merge conflicts
If there are merge conflicts, reset the file and discard all changes
2019-07-27 11:05:23 +10:00
Christian Muehlhaeuser
cc138fc70e Simplified boolean comparison 2019-07-27 10:55:21 +10:00
Christian Muehlhaeuser
d953712377 err was assigned but never checked 2019-07-27 10:55:01 +10:00
Christian Muehlhaeuser
69ac0036e6 Swallow errors entirely, instead of assigning and ignoring them 2019-07-27 10:53:19 +10:00
Christian Muehlhaeuser
975a5315b0 Simplified code a bit 2019-07-27 10:52:06 +10:00
Christian Muehlhaeuser
8f734b11e3 Removed unnecessary string conversion 2019-07-27 10:51:17 +10:00
Tomáš Horáček
17b4cabc71 Add syntax highlighting to Config.md
It's easier to read...
2019-07-27 10:48:30 +10:00
Jesse Duffield
75db4faf69 show actual error when trying to check out a branch that doesn't exist 2019-07-14 14:31:48 +10:00
haowei
e1f5601d4b fix typo 2019-07-14 14:24:59 +10:00
Giorgio Previtera
b60ecdaa24 472 - Update error message 2019-07-14 14:24:18 +10:00
Giorgio Previtera
9fb9962ce7 472 - Don't panic if not in a repository
Display a friendly message and exit with an error if not
in a Git repository. Using the same approach used in this PR:
https://github.com/jesseduffield/lazydocker/pull/14/files
2019-07-14 14:24:18 +10:00
Jesse Duffield
25c93c678d Create FUNDING.yml 2019-06-30 09:50:28 +10:00
Jesse Duffield
b8baef7b8f use fork of roll that doesn't care about errors 2019-06-29 20:56:46 +10:00
Jesse Duffield
235a47bb80 Revert "emergency situation: we're not logging to rollrus while we're past the request quota"
This reverts commit c107eed890.
2019-06-29 20:56:46 +10:00
Jesse Duffield
c107eed890 emergency situation: we're not logging to rollrus while we're past the request quota 2019-06-24 12:29:44 +10:00
Jesse Duffield
abddea060e revert menu panel error panel usage 2019-06-08 20:29:25 +10:00
Jesse Duffield
3e40369fd2 add GIT_OPTIONAL_LOCKS=0 env var to all commands 2019-06-06 20:53:35 +10:00
Jesse Duffield
0f0fda1660 allow stashing staged changes
reinstate old stash functionality with the 's' keybinding
2019-06-06 20:50:19 +10:00
Jesse Duffield
bd2170a99c request explicit return from subprocess
Previously we were recording output from subprocesses using a multiwriter
and hooking that up to the cmd's stdout to write to both os.Stdout and
a buffer. We would then display the output after the program finished.

This worked well for commands like 'ls' but not for commands like 'vi'
which expect you to be in a tty, and when you've got the cmd's stdout
pointing at a multiwriter, the subprogram thinks we're not in a tty
and then things like terminal corruption can happen. This was the case
with neovim, and even in vim a warning was given with a pause before
starting the program.

Now we're chucking out the multiwriter and instead making it that you
need to press enter after the program has finished to return to lazygit.
This allows you to view the output of the program (e.g. if it's ls) and
then decide that you want to return. It's one level of unnecessary
redirection for editors like vim, but even they could potentially have
output to stderr/stdout that you want to look at before returning.

 Please enter the commit message for your changes. Lines starting
2019-05-26 21:19:54 +10:00
Jesse Duffield
c039e5bed0 support going to start/end of line and deleting lines in simple editor 2019-05-26 12:42:17 +10:00
Jesse Duffield
527c025a0c use shift+j/k to scroll main, ctrl+j/k to move commits 2019-05-25 16:48:17 +10:00
Jesse Duffield
53cded77f1 fix padding with coloures strings 2019-05-19 15:25:33 +10:00
Jesse Duffield
4a4dc676fc simplify code for logging output of subprocess 2019-05-18 11:30:10 +10:00
Jesse Duffield
c61bfbdd4c Support opening lazygit in a submodule 2019-05-12 17:59:49 +10:00
Suhas Karanth
e38d9d5f22 Add alternatives for scroll actions to context map 2019-05-12 16:20:42 +10:00
Suhas Karanth
97f060d38d Add field Alternative to gui.Binding
Document and use alternative keybinding for generating cheatsheet. Add
alt keybinding fn+up/down for scroll up/down actions.

Also run `go run scripts/generate_cheatsheet.go`.
2019-05-12 16:20:42 +10:00
Jesse Duffield
357b8fa98f Bump gocui fork 2019-05-09 21:27:35 +10:00
mjarkk
8754d766e2 Made not enough space pannel looks better on 1 height 2019-05-07 08:47:41 +02:00
mjarkk
2388c3ee9a Fixed some sugestions from jesseduffield 2019-05-06 20:04:54 +02:00
Mark Kopenga
61890cb9de Merge branch 'master' into smaller-ui 2019-05-06 15:24:36 +02:00
Jesse Duffield
5a0d0bb299 support resetting to a commit in either soft, hard, or mixed mode 2019-05-06 22:44:38 +10:00
Jesse Duffield
2746b1bd38 Prevent crash when opening in small window
We were crashing when opening lazygit in a small window because the limit view
was the only view that got created, and there were two functions that referenced
either the 'current' view or the files view, neither of which existed.

Now those functions just return nil if the view does not exist
2019-05-06 22:39:35 +10:00
Suhas Karanth
e09aac6450 Improve directory check for .git
Return error if the .git exists but is not a directory. This provides a
slightly better failure message for git repo with submodules in case
the '.git' is a file which provides the reference to the parent's .git
folder with the submodule inside.
2019-05-06 21:37:42 +10:00
mjarkk
19a6368377 Changed the way how the view height are set 2019-05-05 15:57:35 +02:00
mjarkk
e6122122e9 Updated the gocui package 2019-05-05 11:50:51 +02:00
mjarkk
492614ebc7 Made the ui even smaller 2019-04-26 08:24:14 +02:00
Mark Kopenga
d31f0ed39b Merge branch 'master' into smaller-ui 2019-04-26 07:46:45 +02:00
mjarkk
b505c295d2 Fixed another view things 2019-04-26 13:44:37 +10:00
mjarkk
0b9d7edd47 Fixed sugestions 2019-04-26 13:44:37 +10:00
mjarkk
e9fbb608a8 Translated missing sentences 2019-04-26 13:44:37 +10:00
mjarkk
6ba05c94ea Added another resizing step 2019-04-25 21:37:19 +02:00
mjarkk
07fec6d00e Made the go bot happy 2019-04-20 16:51:50 +02:00
mjarkk
a69b985086 Better UI on small screens 2019-04-20 15:56:23 +02:00
Jesse Duffield
471733fe03 add english translations to dutch/polish i18n files for translation later 2019-04-13 14:39:46 +10:00
Jesse Duffield
0d3a193ab5 Add 'w' keybinding in files panel to commit as a WIP
If your git.skipHookPrefix is set to, say, WIP, in your config, then
hitting 'w' in the files panel will bring up the commit message panel
with 'WIP' pre-filled, so you just need to hit enter to confirm
(or add some more to the message) in order to commit your changes
with the --no-verify flag, meaning the pre-commit hook will be skipped
2019-04-13 14:38:17 +10:00
Jesse Duffield
ab9fa291a8 Add skipHookPrefix to config
allows a user to specify a commit message prefix that will tell lazygit to skip
the pre-commit hook. This defaults to WIP. Setting it to the empty string will
disable the feature.

So if my message goes 'WIP: do the thing' then the pre-commit hook will not run
2019-04-13 14:38:17 +10:00
Jesse Duffield
cadc74eeec Update issue templates 2019-04-13 12:35:09 +10:00
Peter Lundberg
fc3a57b5e2 Change expected sha for DiscardOldFileChanges 2019-04-10 17:17:31 +10:00
Peter Lundberg
7ff07e1454 Always include atleast 2 commits when doing squash and fixup 2019-04-10 17:17:31 +10:00
Jesse Duffield
3e779bca8d bump gocui to fix invalid point crashing issue 2019-04-10 10:03:35 +10:00
Jesse Duffield
0f1abcb10c remove subprocess channel stuff 2019-04-07 17:15:01 +10:00
Jesse Duffield
55538a3695 support custom commands 2019-04-07 17:15:01 +10:00
Jesse Duffield
878a15aff4 remove verbose flag from go test 2019-04-07 13:13:40 +10:00
Jesse Duffield
60e33f5d8c Allow for creating fixup! commits 2019-04-07 13:13:40 +10:00
Claudia
b422692746 Remove custom Homebrew tap from instructions
Now that lazygit has been added to the Homebrew core repository
([1] [2]), Homebrew users no longer need to tap into
`jesseduffield/lazygit` to install it.

[1]: https://github.com/jesseduffield/lazygit/issues/256

[2]: https://github.com/Homebrew/homebrew-core/pull/37614
2019-04-06 18:33:27 +11:00
skanehira
f34be1896a fixed some #397 2019-04-06 13:02:20 +11:00
skanehira
c350cdba43 add feature of display diff between specific commits #397 2019-04-06 13:02:20 +11:00
Jesse Duffield
1a933eaa73 pass length of options to createMenu 2019-03-23 13:26:17 +11:00
Jesse Duffield
bd46e9c5b0 delete menu keybinding before setting new one 2019-03-23 13:26:17 +11:00
Jesse Duffield
09b7ae21bc always attempt to discard changes from current file 2019-03-23 13:26:17 +11:00
Jesse Duffield
acfc961909 move soft reset keybinding into reset options 2019-03-23 13:26:17 +11:00
Jesse Duffield
f502f75e1f add more options for resetting files in the working tree 2019-03-23 13:26:17 +11:00
Jesse Duffield
ff97ef7b94 support discarding unstaged changes 2019-03-23 13:26:17 +11:00
Jesse Duffield
a2c780b085 retain commit message if precommit hook fails 2019-03-23 13:07:36 +11:00
Jesse Duffield
b99305c909 push codecov result before compiling on all platforms 2019-03-23 12:00:43 +11:00
Jesse Duffield
d84dfc23e7 Rely on model rather than view to focus a point
Currently when we want to focus a point on a view (i.e. highlight a
line and ensure it's within the bounds of a view's box, we use the
LinesHeight method on the view to work out how many lines in total
there are.

This is bad because for example if we come back from editing a file,
the view will have no contents so LinesHeight == 0, but we might
be trying to select line 10 because there are actual ten things we
expect to be rendered already. This causes a crash when e.g. 10 is
greater than the height of the view.

So we need to pass in to our FocusPoint method the actual number of
items we want to render, rather than having the method rely on the
LinesHeight, so that the method knows to scroll a bit before setting
the cursor's y position.

Unfortunately this makes for some awkward code with our current setup.
We don't have a good interface type on these state objects so we now
need to explicitly obtain the len() of whatever array we're rendering.

In the case of the menu panel this is even more awkward because the items
list is just an interface{} and it's not easy to get the list of that, so
now when we instantiate a menu we need to pass in the count of items
as well.

The better solution would be to define an interface with a getItems
and getLength method and have all these item arrays become structs
implementing the interface, but I am too lazy to do this right now :)
2019-03-23 11:54:25 +11:00
Jesse Duffield
9d8fd3594e remove go modules
Perhaps one day we'll revisit this, but right now supporting go modules is just a headache.
Dep does everything we need and it's really easy to work with, and given that supporting both simultaneously is too cumbersome, and I'm too lazy to make the switch to go modules properly, I'm sticking with go dep for now.
2019-03-22 21:28:25 +11:00
Jesse Duffield
e68dbeb7eb organise keybindings better 2019-03-22 20:20:06 +11:00
skanehira
32ddf0c296 generate commit files keybind 2019-03-18 09:49:23 +11:00
skanehira
c453bfeb32 generate the cheatsheet for each supported language 2019-03-18 09:49:23 +11:00
skanehira
f6ca450d36 add commit files keybind to Keybindings_en.md 2019-03-18 09:49:23 +11:00
Jesse Duffield
d5f617ec92 show some more errors in the gui rather than panicking 2019-03-16 12:51:48 +11:00
Jesse Duffield
6d104bfa91 show file remove error in gui rather than panic 2019-03-16 12:51:48 +11:00
Jesse Duffield
e583cc2519 allow autostashing changes when checking out a branch 2019-03-16 12:51:48 +11:00
Kristijan Husak
0d208b7957 Update bitbucket pull request url. 2019-03-16 11:58:26 +11:00
Jesse Duffield
43e5c042a2 prompt user to git init when outside a repo 2019-03-16 11:38:16 +11:00
Jesse Duffield
39844ffef9 allow a LOG_LEVEL env var to be set 2019-03-16 10:52:30 +11:00
Jesse Duffield
f5c8aac97d add two more tests 2019-03-16 10:20:27 +11:00
Jesse Duffield
b6447ebdbb allow adding a file viewed from the commit files panel 2019-03-16 10:20:27 +11:00
Jesse Duffield
466fc4227e fix tests 2019-03-16 10:20:27 +11:00
Jesse Duffield
c034c88be4 display test name when running tests 2019-03-16 10:20:27 +11:00
Jesse Duffield
72830efc45 add some tests 2019-03-16 10:20:27 +11:00
Jesse Duffield
c98eddc185 appease golangci 2019-03-16 10:20:27 +11:00
Jesse Duffield
3b2353b5ae remove redundant call to refreshCommitFilesView
We already call this function inside the refreshCommitsView function.
We call it there because it's logical that A) one occurs whenever the other does and
B) the commit files only get refreshed after we've updated the commits themselves
2019-03-16 10:20:27 +11:00
Jesse Duffield
3f567c952c i18n for error message about a feature being disabled for GPG users 2019-03-16 10:20:27 +11:00
Jesse Duffield
4f7f6a073c allow user to discard old file changes for a given commit 2019-03-16 10:20:27 +11:00
Jesse Duffield
0e008cc15f allow user to checkout old files 2019-03-16 10:20:27 +11:00
Jesse Duffield
1ad9c6faac minor cleanup 2019-03-16 10:20:27 +11:00
skanehira
06fe726ee7 Add feature of display committed file list #383 2019-03-16 10:20:27 +11:00
skanehira
1b6e46973e remove the -o option from Dockerfile 2019-03-08 15:51:23 +11:00
Jesse Duffield
63e2ccfccf bump go version in CI 2019-03-05 21:56:23 +11:00
Jesse Duffield
6fd4d49db7 update go.sum 2019-03-05 21:56:23 +11:00
Jesse Duffield
2393bc791d fix cpu drainage issue 2019-03-05 21:56:23 +11:00
Jesse Duffield
398f91decb Merge branch 'master' of https://github.com/jesseduffield/lazygit 2019-03-04 20:40:51 +11:00
Jesse Duffield
c937a93f79 yet another rebase image for the readme 2019-03-04 20:40:46 +11:00
Jesse Duffield
9402d8b0c0 Update README.md 2019-03-04 20:39:00 +11:00
Jesse Duffield
2a0615da10 get higher res image of rebasing 2019-03-04 20:37:25 +11:00
Jesse Duffield
4be5eaae7b update readme again 2019-03-04 20:32:17 +11:00
Jesse Duffield
038dcb546e update readme with new keybindings link 2019-03-03 23:44:50 +11:00
Jesse Duffield
1184990e16 move updated keybindings file 2019-03-03 23:27:19 +11:00
585 changed files with 112107 additions and 26384 deletions

View File

@@ -2,23 +2,12 @@ version: 2
jobs:
build:
docker:
- image: circleci/golang:1.11
- image: circleci/golang:1.12
environment:
GO111MODULE: "on"
working_directory: /go/src/github.com/jesseduffield/lazygit
steps:
- checkout
- run:
name: Ensure go.mod file is up to date
command: |
export GO111MODULE=on
rm go.sum
mv go.mod /tmp/
go mod init
export GO111MODULE=auto
if [ $(diff /tmp/go.mod go.mod|wc -l) -gt 0 ]; then
diff /tmp/go.mod go.mod
exit 1;
fi
- run:
name: Run gofmt -s
command: |
@@ -28,28 +17,28 @@ jobs:
fi
- restore_cache:
keys:
- pkg-cache-{{ checksum "Gopkg.lock" }}-v3
- pkg-cache-{{ checksum "go.sum" }}-v5
- run:
name: Run tests
command: |
./test.sh
- run:
name: Compile project on every platform
command: |
go get github.com/mitchellh/gox
gox -parallel 10 -os "linux freebsd netbsd windows" -osarch "darwin/i386 darwin/amd64"
- 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 "Gopkg.lock" }}-v3
key: pkg-cache-{{ checksum "go.sum" }}-v5
paths:
- ~/.cache/go-build
release:
docker:
- image: circleci/golang:1.10
- image: circleci/golang:1.12
working_directory: /go/src/github.com/jesseduffield/lazygit
steps:
- checkout

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
# These are supported funding model platforms
ko_fi: jesseduffield
custom: ['https://donorbox.org/lazygit']

View File

@@ -1,6 +1,9 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---

14
.github/ISSUE_TEMPLATE/discussion.md vendored Normal file
View File

@@ -0,0 +1,14 @@
---
name: Discussion
about: Begin a discussion
title: ''
labels: discussion
assignees: ''
---
**Topic**
A clear and concise description of what you want to discuss
**Your thoughts**
What you have to say about the topic

View File

@@ -1,6 +1,9 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---

View File

@@ -5,7 +5,7 @@
FROM golang:alpine
WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY ./ .
RUN CGO_ENABLED=0 GOOS=linux go build -o lazygit .
RUN CGO_ENABLED=0 GOOS=linux go build
FROM alpine:latest
RUN apk add -U git xdg-utils

652
Gopkg.lock generated
View File

@@ -1,652 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:e24ea5dbc89fbab51635ee32e5be4f61a9267cae20788efcae4c07efb4abec99"
name = "github.com/aws/aws-sdk-go"
packages = [
"aws",
"aws/awserr",
"aws/awsutil",
"aws/client",
"aws/client/metadata",
"aws/corehandlers",
"aws/credentials",
"aws/credentials/ec2rolecreds",
"aws/credentials/endpointcreds",
"aws/credentials/stscreds",
"aws/csm",
"aws/defaults",
"aws/ec2metadata",
"aws/endpoints",
"aws/request",
"aws/session",
"aws/signer/v4",
"internal/sdkio",
"internal/sdkrand",
"internal/sdkuri",
"internal/shareddefaults",
"private/protocol",
"private/protocol/eventstream",
"private/protocol/eventstream/eventstreamapi",
"private/protocol/query",
"private/protocol/query/queryutil",
"private/protocol/rest",
"private/protocol/restxml",
"private/protocol/xml/xmlutil",
"service/s3",
"service/sts",
]
pruneopts = "NUT"
revision = "4324bc9d8865bdb3e6aa86ec7772ca1272d2750e"
version = "v1.15.21"
[[projects]]
branch = "master"
digest = "1:37011b20a70e205b93ebea5287e1afa5618db54bf3998c36ff5a8e4b146a170a"
name = "github.com/bgentry/go-netrc"
packages = ["netrc"]
pruneopts = "NUT"
revision = "9fd32a8b3d3d3f9d43c341bfe098430e07609480"
[[projects]]
branch = "master"
digest = "1:cd7ba2b29e93e2a8384e813dfc80ebb0f85d9214762e6ca89bb55a58092eab87"
name = "github.com/cloudfoundry/jibber_jabber"
packages = ["."]
pruneopts = "NUT"
revision = "bcc4c8345a21301bf47c032ff42dd1aae2fe3027"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "NUT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
digest = "1:de4a74b504df31145ffa8ca0c4edbffa2f3eb7f466753962184611b618fa5981"
name = "github.com/emirpasic/gods"
packages = [
"containers",
"lists",
"lists/arraylist",
"trees",
"trees/binaryheap",
"utils",
]
pruneopts = "NUT"
revision = "f6c17b524822278a87e3b3bd809fec33b51f5b46"
version = "v1.9.0"
[[projects]]
digest = "1:ade392a843b2035effb4b4a2efa2c3bab3eb29b992e98bacf9c898b0ecb54e45"
name = "github.com/fatih/color"
packages = ["."]
pruneopts = "NUT"
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
version = "v1.7.0"
[[projects]]
digest = "1:1b91ae0dc69a41d4c2ed23ea5cffb721ea63f5037ca4b81e6d6771fbb8f45129"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
pruneopts = "NUT"
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
digest = "1:ea1d5bfdb4ec5c2ee48c97865e6de1a28fa8c4849a3f56b27d521aa619038e06"
name = "github.com/go-errors/errors"
packages = ["."]
pruneopts = "NUT"
revision = "a6af135bd4e28680facf08a3d206b454abc877a4"
version = "v1.0.1"
[[projects]]
digest = "1:74d9b0a7b4107b41e0ade759fac64502876f82d29fb23d77b3dd24b194ee3dd5"
name = "github.com/go-ini/ini"
packages = ["."]
pruneopts = "NUT"
revision = "5cf292cae48347c2490ac1a58fe36735fb78df7e"
version = "v1.38.2"
[[projects]]
branch = "master"
digest = "1:4a8ed9b8cf22bd03bee5d74179fa06a282e4a73b6de949f7a865ff56cd2537e0"
name = "github.com/golang-collections/collections"
packages = ["stack"]
pruneopts = "NUT"
revision = "604e922904d35e97f98a774db7881f049cd8d970"
[[projects]]
branch = "master"
digest = "1:a5d940c38bf56f121721bfa747c66356df387cb9d5318c570c6d4170aab62862"
name = "github.com/hashicorp/go-cleanhttp"
packages = ["."]
pruneopts = "NUT"
revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d"
[[projects]]
branch = "master"
digest = "1:b634d733abf079dc191d359e5a8d31479f1795d00e656f8a018a459571046266"
name = "github.com/hashicorp/go-getter"
packages = ["helper/url"]
pruneopts = "NUT"
revision = "4bda8fa99001c61db3cad96b421d4c12a81f256d"
[[projects]]
branch = "master"
digest = "1:fbab03227343a0285fc74a68dd2ff46cda7edecbbe5a3e98d2cecd00cc67b217"
name = "github.com/hashicorp/go-safetemp"
packages = ["."]
pruneopts = "NUT"
revision = "b1a1dbde6fdc11e3ae79efd9039009e22d4ae240"
[[projects]]
digest = "1:0b06ffe0c0764e413a6738e3f045d6bb14117359aef80a09f8c60fbff2ecad6b"
name = "github.com/hashicorp/go-version"
packages = ["."]
pruneopts = "NUT"
revision = "b5a281d3160aa11950a6182bd9a9dc2cb1e02d50"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:11c6c696067d3127ecf332b10f89394d386d9083f82baf71f40f2da31841a009"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token",
]
pruneopts = "NUT"
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
branch = "master"
digest = "1:d457d39e88f678ed14ac29517c3d74927a48dbc6a9f073fa241cf364a68cbe5c"
name = "github.com/heroku/rollrus"
packages = ["."]
pruneopts = "NUT"
revision = "fc0cef2ff331aebb24cd4e9ded7e20650f3d7006"
[[projects]]
branch = "master"
digest = "1:62fe3a7ea2050ecbd753a71889026f83d73329337ada66325cbafd5dea5f713d"
name = "github.com/jbenet/go-context"
packages = ["io"]
pruneopts = "NUT"
revision = "d14ea06fba99483203c19d92cfcd13ebe73135f4"
[[projects]]
branch = "master"
digest = "1:490643e333b848f3d6ab772c21082d706663dcf4a3c0fbe9a4b4ef7b205ce6c7"
name = "github.com/jesseduffield/go-getter"
packages = ["."]
pruneopts = "NUT"
revision = "906e15686e6309ff310c1c10463ab53287c3a678"
[[projects]]
branch = "master"
digest = "1:31a87f65dc451471f411d04742d2cb5ab79a699b8c73666b8fc29f47a8f43f7e"
name = "github.com/jesseduffield/gocui"
packages = ["."]
pruneopts = "NUT"
revision = "b502ee11d6743144c86226ca0366adaed727214d"
[[projects]]
branch = "master"
digest = "1:a46c2f4863e5284ddb255c28750298e04bc8c0fc896bed6056e947673168b7be"
name = "github.com/jesseduffield/pty"
packages = ["."]
pruneopts = "NUT"
revision = "02db52c7e406c7abec44c717a173c7715e4c1b62"
[[projects]]
branch = "master"
digest = "1:3ab130f65766f5b7cc944d557df31c6a007ec017151705ec1e1b8719f2689021"
name = "github.com/jesseduffield/termbox-go"
packages = ["."]
pruneopts = "NUT"
revision = "1e272ff78dcb4c448870f464fda1cdcf2bf0b3dd"
[[projects]]
digest = "1:ac6d01547ec4f7f673311b4663909269bfb8249952de3279799289467837c3cc"
name = "github.com/jmespath/go-jmespath"
packages = ["."]
pruneopts = "NUT"
revision = "0b12d6b5"
[[projects]]
branch = "master"
digest = "1:263f9b0a0bcbfff9d5e7d9f2aa11f53995d98214fe0fb97e429e7a5f4534a0f9"
name = "github.com/kardianos/osext"
packages = ["."]
pruneopts = "NUT"
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
[[projects]]
digest = "1:8021af4dcbd531ae89433c8c3a6520e51064114aaf8eb1724c3cf911c497c9ba"
name = "github.com/kevinburke/ssh_config"
packages = ["."]
pruneopts = "NUT"
revision = "9fc7bb800b555d63157c65a904c86a2cc7b4e795"
version = "0.4"
[[projects]]
digest = "1:d244f8666a838fe6ad70ec8fe77f50ebc29fdc3331a2729ba5886bef8435d10d"
name = "github.com/magiconair/properties"
packages = ["."]
pruneopts = "NUT"
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
digest = "1:08c231ec84231a7e23d67e4b58f975e1423695a32467a362ee55a803f9de8061"
name = "github.com/mattn/go-colorable"
packages = ["."]
pruneopts = "NUT"
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
digest = "1:bc4f7eec3b7be8c6cb1f0af6c1e3333d5bb71072951aaaae2f05067b0803f287"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "NUT"
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
digest = "1:cb591533458f6eb6e2c1065ff3eac6b50263d7847deb23fc9f79b25bc608970e"
name = "github.com/mattn/go-runewidth"
packages = ["."]
pruneopts = "NUT"
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
version = "v0.0.2"
[[projects]]
digest = "1:a25c9a6b41e100f4ce164db80260f2b687095ba9d8b46a1d6072d3686cc020db"
name = "github.com/mgutz/str"
packages = ["."]
pruneopts = "NUT"
revision = "968bf66e3da857419e4f6e71b2d5c9ae95682dc4"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:a4df73029d2c42fabcb6b41e327d2f87e685284ec03edf76921c267d9cfc9c23"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
pruneopts = "NUT"
revision = "58046073cbffe2f25d425fe1331102f55cf719de"
[[projects]]
branch = "master"
digest = "1:18b773b92ac82a451c1276bd2776c1e55ce057ee202691ab33c8d6690efcc048"
name = "github.com/mitchellh/go-testing-interface"
packages = ["."]
pruneopts = "NUT"
revision = "a61a99592b77c9ba629d254a693acffaeb4b7e28"
[[projects]]
branch = "master"
digest = "1:5fe20cfe4ef484c237cec9f947b2a6fa90bad4b8610fd014f0e4211e13d82d5d"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "NUT"
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
[[projects]]
digest = "1:2c34c77bf3ec848da26e48af58fc511ed52750961fa848399d122882b8890928"
name = "github.com/nicksnyder/go-i18n"
packages = [
"v2/i18n",
"v2/internal",
"v2/internal/plural",
]
pruneopts = "NUT"
revision = "a16b91a3ba80db3a2301c70d1d302d42251c9079"
version = "v2.0.0-beta.5"
[[projects]]
digest = "1:cf254277d898b713195cc6b4a3fac8bf738b9f1121625df27843b52b267eec6c"
name = "github.com/pelletier/go-buffruneio"
packages = ["."]
pruneopts = "NUT"
revision = "c37440a7cf42ac63b919c752ca73a85067e05992"
version = "v0.2.0"
[[projects]]
digest = "1:51ea800cff51752ff68e12e04106f5887b4daec6f9356721238c28019f0b42db"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "NUT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "NUT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "NUT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:d917313f309bda80d27274d53985bc65651f81a5b66b820749ac7f8ef061fd04"
name = "github.com/sergi/go-diff"
packages = ["diffmatchpatch"]
pruneopts = "NUT"
revision = "1744e2970ca51c86172c8190fadad617561ed6e7"
version = "v1.0.0"
[[projects]]
digest = "1:41618aee8828e62dfe62d44f579c06892d0e98907d1c6d5bcd83bfe8536ec5a3"
name = "github.com/shibukawa/configdir"
packages = ["."]
pruneopts = "NUT"
revision = "e180dbdc8da04c4fa04272e875ce64949f38bd3e"
[[projects]]
digest = "1:b2339e83ce9b5c4f79405f949429a7f68a9a904fed903c672aac1e7ceb7f5f02"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "NUT"
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
version = "v1.0.6"
[[projects]]
digest = "1:330e9062b308ac597e28485699c02223bd052437a6eed32a173c9227dcb9d95a"
name = "github.com/spf13/afero"
packages = [
".",
"mem",
]
pruneopts = "NUT"
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
version = "v1.1.1"
[[projects]]
digest = "1:3fa7947ca83b98ae553590d993886e845a4bff19b7b007e869c6e0dd3b9da9cd"
name = "github.com/spf13/cast"
packages = ["."]
pruneopts = "NUT"
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:f29f83301ed096daed24a90f4af591b7560cb14b9cc3e1827abbf04db7269ab5"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "NUT"
revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2"
[[projects]]
digest = "1:e3707aeaccd2adc89eba6c062fec72116fe1fc1ba71097da85b4d8ae1668a675"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "NUT"
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
version = "v1.0.2"
[[projects]]
digest = "1:454979540e2a1582f375a17c106cf4e11e3bcac4baffb4af23e515c87f87de13"
name = "github.com/spf13/viper"
packages = ["."]
pruneopts = "NUT"
revision = "907c19d40d9a6c9bb55f040ff4ae45271a4754b9"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:0e9a5ac14bcc11f205031a671b28c7e05cb88b2ebbe06f383c1ab0b2c12c7cb5"
name = "github.com/spkg/bom"
packages = ["."]
pruneopts = "NUT"
revision = "59b7046e48ad6bac800c5e1dd5142282cbfcf154"
[[projects]]
digest = "1:ccca1dcd18bc54e23b517a3c5babeff2e3924a7d8fc1932162225876cfe4bfb0"
name = "github.com/src-d/gcfg"
packages = [
".",
"scanner",
"token",
"types",
]
pruneopts = "NUT"
revision = "f187355171c936ac84a82793659ebb4936bc1c23"
version = "v1.3.0"
[[projects]]
digest = "1:bacb8b590716ab7c33f2277240972c9582d389593ee8d66fc10074e0508b8126"
name = "github.com/stretchr/testify"
packages = ["assert"]
pruneopts = "NUT"
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
[[projects]]
branch = "master"
digest = "1:e42372d3f4921ec35df07f9b23239631e9d28580f7c1edcca212bc6daddc68fe"
name = "github.com/stvp/roll"
packages = ["."]
pruneopts = "NUT"
revision = "3627a5cbeaeaa68023abd02bb8687925265f2f63"
[[projects]]
digest = "1:cd5ffc5bda4e0296ab3e4de90dbb415259c78e45e7fab13694b14cde8ab74541"
name = "github.com/tcnksm/go-gitconfig"
packages = ["."]
pruneopts = "NUT"
revision = "d154598bacbf4501c095a309753c5d4af66caa81"
version = "v0.1.2"
[[projects]]
digest = "1:07e8742c479bab0066149ad02a710024154e76874fd0a2dba002d87702725825"
name = "github.com/ulikunitz/xz"
packages = [
".",
"internal/hash",
"internal/xlog",
"lzma",
]
pruneopts = "NUT"
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
version = "v0.5.4"
[[projects]]
digest = "1:3148cb3478c26a92b4c1a18abb9428234b281e278af6267840721a24b6cbc6a3"
name = "github.com/xanzy/ssh-agent"
packages = ["."]
pruneopts = "NUT"
revision = "640f0ab560aeb89d523bb6ac322b1244d5c3796c"
version = "v0.2.0"
[[projects]]
branch = "master"
digest = "1:dfcb1b2db354cafa48fc3cdafe4905a08bec4a9757919ab07155db0ca23855b4"
name = "golang.org/x/crypto"
packages = [
"cast5",
"curve25519",
"ed25519",
"ed25519/internal/edwards25519",
"internal/chacha20",
"internal/subtle",
"openpgp",
"openpgp/armor",
"openpgp/elgamal",
"openpgp/errors",
"openpgp/packet",
"openpgp/s2k",
"poly1305",
"ssh",
"ssh/agent",
"ssh/knownhosts",
"ssh/terminal",
]
pruneopts = "NUT"
revision = "de0752318171da717af4ce24d0a2e8626afaeb11"
[[projects]]
branch = "master"
digest = "1:76ee51c3f468493aff39dbacc401e8831fbb765104cbf613b89bef01cf4bad70"
name = "golang.org/x/net"
packages = ["context"]
pruneopts = "NUT"
revision = "c39426892332e1bb5ec0a434a079bf82f5d30c54"
[[projects]]
branch = "master"
digest = "1:ec76a40fbfda0c329ee58f4e3b14b4279a939efce89eca020e934e2e5234eddd"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "NUT"
revision = "98c5dad5d1a0e8a73845ecc8897d0bd56586511d"
[[projects]]
digest = "1:a95288ef1ef4dfad6cba7fe30843e1683f71bc28c912ca1ba3f6a539d44db739"
name = "golang.org/x/text"
packages = [
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"transform",
"unicode/cldr",
"unicode/norm",
]
pruneopts = "NUT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
digest = "1:47a697b155f5214ff14e68e39ce9c2e8d93e1fb035ae5ba7e247d044e0ce64e3"
name = "gopkg.in/src-d/go-billy.v4"
packages = [
".",
"helper/chroot",
"helper/polyfill",
"osfs",
"util",
]
pruneopts = "NUT"
revision = "83cf655d40b15b427014d7875d10850f96edba14"
version = "v4.2.0"
[[projects]]
digest = "1:e66078da2bd6e53c72518d7f6ae0c3c8c7f34c0df12c39435ce34a6bce165525"
name = "gopkg.in/src-d/go-git.v4"
packages = [
".",
"config",
"internal/revision",
"plumbing",
"plumbing/cache",
"plumbing/filemode",
"plumbing/format/config",
"plumbing/format/diff",
"plumbing/format/gitignore",
"plumbing/format/idxfile",
"plumbing/format/index",
"plumbing/format/objfile",
"plumbing/format/packfile",
"plumbing/format/pktline",
"plumbing/object",
"plumbing/protocol/packp",
"plumbing/protocol/packp/capability",
"plumbing/protocol/packp/sideband",
"plumbing/revlist",
"plumbing/storer",
"plumbing/transport",
"plumbing/transport/client",
"plumbing/transport/file",
"plumbing/transport/git",
"plumbing/transport/http",
"plumbing/transport/internal/common",
"plumbing/transport/server",
"plumbing/transport/ssh",
"storage",
"storage/filesystem",
"storage/filesystem/dotgit",
"storage/memory",
"utils/binary",
"utils/diff",
"utils/ioutil",
"utils/merkletrie",
"utils/merkletrie/filesystem",
"utils/merkletrie/index",
"utils/merkletrie/internal/frame",
"utils/merkletrie/noder",
]
pruneopts = "NUT"
revision = "43d17e14b714665ab5bc2ecc220b6740779d733f"
[[projects]]
digest = "1:b233ad4ec87ac916e7bf5e678e98a2cb9e8b52f6de6ad3e11834fc7a71b8e3bf"
name = "gopkg.in/warnings.v0"
packages = ["."]
pruneopts = "NUT"
revision = "ec4a0fea49c7b46c2aeb0b51aac55779c607e52b"
version = "v0.1.2"
[[projects]]
digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "NUT"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/cloudfoundry/jibber_jabber",
"github.com/fatih/color",
"github.com/go-errors/errors",
"github.com/golang-collections/collections/stack",
"github.com/heroku/rollrus",
"github.com/jesseduffield/go-getter",
"github.com/jesseduffield/gocui",
"github.com/jesseduffield/pty",
"github.com/kardianos/osext",
"github.com/mgutz/str",
"github.com/nicksnyder/go-i18n/v2/i18n",
"github.com/shibukawa/configdir",
"github.com/sirupsen/logrus",
"github.com/spf13/viper",
"github.com/spkg/bom",
"github.com/stretchr/testify/assert",
"github.com/tcnksm/go-gitconfig",
"golang.org/x/text/language",
"gopkg.in/src-d/go-git.v4",
"gopkg.in/src-d/go-git.v4/plumbing",
"gopkg.in/yaml.v2",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,50 +0,0 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
[prune]
go-tests = true
unused-packages = true
non-go = true
[[constraint]]
name = "github.com/fatih/color"
version = "1.7.0"
[[constraint]]
branch = "master"
name = "github.com/golang-collections/collections"
[[constraint]]
branch = "master"
name = "github.com/jesseduffield/gocui"
[[constraint]]
branch = "master"
name = "github.com/jesseduffield/pty"
[[constraint]]
name = "gopkg.in/src-d/go-git.v4"
revision = "43d17e14b714665ab5bc2ecc220b6740779d733f"
[[constraint]]
branch = "master"
name = "github.com/spkg/bom"

View File

@@ -1,31 +1,31 @@
# 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.
A simple terminal UI for git commands, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
Are YOU tired of typing every git command directly into the terminal, but you're
too stubborn to use Sourcetree because you'll never forgive Atlassian for making
Jira? This is the app for you!
![Gif](/docs/resources/lazygit-example.gif)
* [Installation](https://github.com/jesseduffield/lazygit#installation)
* [Usage](https://github.com/jesseduffield/lazygit#usage),
[Keybindings](https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md)
* [Cool Features](https://github.com/jesseduffield/lazygit#cool-features)
* [Contributing](https://github.com/jesseduffield/lazygit#contributing)
* [Video Tutorial](https://youtu.be/VDXvbHZYeKY)
* [Twitch Stream](https://www.twitch.tv/jesseduffield)
- [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)
- [Twitch Stream](https://www.twitch.tv/jesseduffield)
## Installation
### Homebrew
```sh
brew tap jesseduffield/lazygit
brew install lazygit
```
### Ubuntu
Packages for Ubuntu 16.04, 18.04 and 18.10 are available via [Launchpad PPA](https://launchpad.net/~lazygit-team).
**Release builds**
@@ -49,6 +49,7 @@ sudo apt-get install lazygit
```
### Void Linux
Packages for Void Linux are available in the distro repo
They follow upstream latest releases
@@ -58,91 +59,97 @@ sudo xbps-install -S lazygit
```
### Arch Linux
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
### Conda
Released versions are available for different platforms, see https://anaconda.org/conda-forge/lazygit
```sh
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
go get github.com/jesseduffield/lazygit
```
Please note:
If you get an error claiming that lazygit cannot be found or is not defined, you
may need to add `~/go/bin` to your $PATH (MacOS/Linux), or `%HOME%\go\bin`
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
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).
* List of keybindings
[here](/docs/Keybindings.md).
- Basic video tutorial [here](https://youtu.be/VDXvbHZYeKY).
- List of keybindings
[here](/docs/keybindings).
## Cool features
* Adding files easily
* Resolving merge conflicts
* Easily check out recent branches
* Scroll through logs/diffs of branches/commits/stash
* Quick pushing/pulling
* Squash down and rename commits
- Adding files easily
- Resolving merge conflicts
- Easily check out recent branches
- Scroll through logs/diffs of branches/commits/stash
- Quick pushing/pulling
- Squash down and rename commits
### Resolving merge conflicts
![Gif](/docs/resources/resolving-merge-conflicts.gif)
### Viewing commit diffs
![Viewing Commit Diffs](/docs/resources/viewing-commit-diffs.png)
### Interactive Rebasing
## Milestones
- [x] Easy Installation (homebrew, release binaries)
- [ ] Configurable Keybindings
- [ ] Configurable Color Themes
- [ ] Spawning Subprocesses (help needed - have a look at https://github.com/jesseduffield/lazygit/pull/18)
- [ ] Maintainability
- [ ] Performance
- [ ] i18n
![Interactive Rebasing](/docs/resources/interactive-rebase.png)
## 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)
## Donate
If you would like to support the development of lazygit, please donate
[![Donate](https://d1iczxrky3cnb2.cloudfront.net/button-medium-blue.png)](https://donorbox.org/lazygit)
## Work in progress
This is still a work in progress so there's still bugs to iron out and as this
is my first project in Go the code could no doubt use an increase in quality,
but I'll be improving on it whenever I find the time. If you have any feedback
feel free to [raise an issue](https://github.com/jesseduffield/lazygit/issues)/[submit a PR](https://github.com/jesseduffield/lazygit/pulls).
## Social
If you want to see what I (Jesse) am up to in terms of development, follow me on
[twitter](https://twitter.com/DuffieldJesse) or watch me program on
[twitch](https://www.twitch.tv/jesseduffield).
## Alternatives
If you find that lazygit doesn't quite satisfy your requirements, these may be a better fit:
- [tig](https://github.com/jonas/tig)

View File

@@ -2,7 +2,7 @@
## Default:
```
```yaml
gui:
# stuff relating to the UI
scrollHeight: 2 # how many lines you scroll by
@@ -21,6 +21,8 @@
merging:
# only applicable to unix users
manualCommit: false
skipHookPrefix: WIP
autoFetch: true
update:
method: prompt # can be: prompt | background | never
days: 14 # how often an update is checked for
@@ -32,21 +34,21 @@
### Windows:
```
```yaml
os:
openCommand: 'cmd /c "start "" {{filename}}"'
```
### Linux:
```
```yaml
os:
openCommand: 'sh -c "xdg-open {{filename}} >/dev/null"'
```
### OSX:
```
```yaml
os:
openCommand: 'open {{filename}}'
```
@@ -55,7 +57,7 @@
for users of VSCode
```
```yaml
os:
openCommand: 'code -r {{filename}}'
```

View File

@@ -1,85 +0,0 @@
# Keybindings:
## Global:
<pre>
<kbd>←</kbd><kbd>→</kbd><kbd>↑</kbd><kbd>↓</kbd>/<kbd>h</kbd><kbd>j</kbd><kbd>k</kbd><kbd>l</kbd>: navigate
<kbd>PgUp</kbd>/<kbd>PgDn</kbd> or <kbd>ctrl</kbd>+<kbd>u</kbd>/<kbd>ctrl</kbd>+<kbd>d</kbd>: scroll diff panel
(for <kbd>PgUp</kbd> and <kbd>PgDn</kbd>, use <kbd>fn</kbd>+<kbd>up</kbd>/<kbd>fn</kbd>+<kbd>down</kbd> on osx)
<kbd>q</kbd>: quit
<kbd>p</kbd>: pull
<kbd>shift</kbd>+<kbd>P</kbd>: push
</pre>
## Status Panel:
<pre>
<kbd>e</kbd>: edit config file
<kbd>o</kbd>: open config file
</pre>
## Files Panel:
<pre>
<kbd>space</kbd>: toggle staged
<kbd>a</kbd>: stage/unstage all
<kbd>c</kbd>: commit changes
<kbd>shift</kbd>+<kbd>C</kbd>: commit using git editor
<kbd>shift</kbd>+<kbd>S</kbd>: stash files
<kbd>t</kbd>: add patched (i.e. pick chunks of a file to add)
<kbd>o</kbd>: open
<kbd>e</kbd>: edit
<kbd>s</kbd>: open in sublime (requires 'subl' command)
<kbd>v</kbd>: open in vscode (requires 'code' command)
<kbd>i</kbd>: add to .gitignore
<kbd>d</kbd>: delete if untracked checkout if tracked (aka go away)
<kbd>shift</kbd>+<kbd>R</kbd>: refresh files
<kbd>shift</kbd>+<kbd>A</kbd>: abort merge
</pre>
## Branches Panel:
<pre>
<kbd>space</kbd>: checkout branch
<kbd>f</kbd>: force checkout branch
<kbd>m</kbd>: merge into currently checked out branch
<kbd>c</kbd>: checkout by name
<kbd>n</kbd>: new branch
<kbd>d</kbd>: delete branch
<kbd>D</kbd>: force delete branch
</pre>
## Commits Panel:
<pre>
<kbd>s</kbd>: squash down (only available for topmost commit)
<kbd>r</kbd>: rename commit
<kbd>shift</kbd>+<kbd>R</kbd>: rename commit using git editor
<kbd>g</kbd>: reset to this commit
</pre>
## Stash Panel:
<pre>
<kbd>space</kbd>: apply
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
</pre>
## Popup Panel:
<pre>
<kbd>esc</kbd>: close/cancel
<kbd>enter</kbd>: confirm
<kbd>tab</kbd>: enter newline (if editing)
</pre>
## Resolving Merge Conflicts (Diff Panel):
<pre>
<kbd>←</kbd><kbd>→</kbd>/<kbd>h</kbd><kbd>l</kbd>: navigate conflicts
<kbd>↑</kbd><kbd>↓</kbd>/<kbd>k</kbd><kbd>j</kbd>: select hunk
<kbd>space</kbd>: pick hunk
<kbd>b</kbd>: pick both hunks
<kbd>z</kbd>: undo (only available while still inside diff panel)
</pre>

View File

@@ -22,21 +22,22 @@
<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>: delete if untracked / checkout if tracked
<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>s</kbd>: soft reset to last commit
<kbd>a</kbd>: stage/unstage all
<kbd>t</kbd>: add patch
<kbd>D</kbd>: reset hard and remove untracked files
<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
@@ -61,9 +62,11 @@
<kbd>R</kbd>: rename commit with editor
<kbd>g</kbd>: reset to this commit
<kbd>f</kbd>: fixup commit
<kbd>F</kbd>: create fixup commit for this commit
<kbd>S</kbd>: squash above commits
<kbd>d</kbd>: delete commit
<kbd>J</kbd>: move commit down one
<kbd>K</kbd>: move commit up one
<kbd>ctrl+j</kbd>: move commit down one
<kbd>ctrl+k</kbd>: move commit up one
<kbd>e</kbd>: edit commit
<kbd>A</kbd>: amend commit with staged changes
<kbd>p</kbd>: pick commit (when mid-rebase)
@@ -71,6 +74,8 @@
<kbd>c</kbd>: copy commit (cherry-pick)
<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>: select commit to diff with another commit
</pre>
## Stash
@@ -81,11 +86,20 @@
<kbd>d</kbd>: drop
</pre>
## Commit files
<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
</pre>
## Main (Normal)
<pre>
<kbd>PgDn</kbd>: scroll down
<kbd>PgUp</kbd>: scroll up
<kbd>PgDn</kbd>: scroll down (fn+up)
<kbd>PgUp</kbd>: scroll up (fn+down)
</pre>
## Main (Staging)

View File

@@ -0,0 +1,128 @@
# Lazygit menu
## Global
<pre>
<kbd>m</kbd>: bekijk merge/rebase opties
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>R</kbd>: verversen
</pre>
## Status
<pre>
<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>
## 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
<kbd>o</kbd>: maak een pull-aanvraag
<kbd>c</kbd>: uitchecken bij naam
<kbd>F</kbd>: forceer checkout
<kbd>n</kbd>: nieuwe branch
<kbd>d</kbd>: verwijder branch
<kbd>r</kbd>: rebase branch
<kbd>M</kbd>: merge in met huidige checked out branch
<kbd>f</kbd>: fast-forward this branch from its upstream
</pre>
## Commits
<pre>
<kbd>s</kbd>: squash beneden
<kbd>r</kbd>: hernoem commit
<kbd>R</kbd>: rename commit with editor
<kbd>g</kbd>: reset naar deze commit
<kbd>f</kbd>: Fixup commit
<kbd>F</kbd>: creëer fixup commit voor deze commit
<kbd>S</kbd>: squash bovenstaande commits
<kbd>d</kbd>: verwijder commit
<kbd>ctrl+j</kbd>: verplaats commit 1 omlaag
<kbd>ctrl+k</kbd>: verplaats commit 1 omhoog
<kbd>e</kbd>: verander commit
<kbd>A</kbd>: wijzig commit met staged veranderingen
<kbd>p</kbd>: pick commit (when mid-rebase)
<kbd>t</kbd>: commit omgedaan maken
<kbd>c</kbd>: kopiëer commit (cherry-pick)
<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>: select commit to diff with another commit
</pre>
## Stash
<pre>
<kbd>space</kbd>: toepassen
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
</pre>
## Commit bestanden
<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
</pre>
## 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
<kbd>space</kbd>: pick hunk
<kbd>b</kbd>: pick beide hunks
<kbd>◄</kbd>: selecteer voorgaand conflict
<kbd>►</kbd>: selecteer volgende conflict
<kbd>▲</kbd>: selecteer bovenste hunk
<kbd>▼</kbd>: selecteer onderste hunk
<kbd>z</kbd>: ongedaan maken
</pre>
## Hoofd (Normaal)
<pre>
<kbd>PgDn</kbd>: scroll omlaag (fn+up)
<kbd>PgUp</kbd>: scroll omhoog (fn+down)
</pre>

View File

@@ -0,0 +1,128 @@
# Lazygit menu
## Globalne
<pre>
<kbd>m</kbd>: view merge/rebase options
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>R</kbd>: odśwież
</pre>
## Status
<pre>
<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>
## 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
<kbd>o</kbd>: utwórz żądanie wyciągnięcia
<kbd>c</kbd>: przełącz używając nazwy
<kbd>F</kbd>: wymuś przełączenie
<kbd>n</kbd>: nowa gałąź
<kbd>d</kbd>: usuń gałąź
<kbd>r</kbd>: rebase branch
<kbd>M</kbd>: scal do obecnej gałęzi
<kbd>f</kbd>: fast-forward this branch from its upstream
</pre>
## Commity
<pre>
<kbd>s</kbd>: ściśnij w dół
<kbd>r</kbd>: przemianuj commit
<kbd>R</kbd>: przemianuj commit w edytorze
<kbd>g</kbd>: zresetuj do tego commita
<kbd>f</kbd>: napraw commit
<kbd>F</kbd>: create fixup commit for this commit
<kbd>S</kbd>: squash above commits
<kbd>d</kbd>: delete commit
<kbd>ctrl+j</kbd>: move commit down one
<kbd>ctrl+k</kbd>: move commit up one
<kbd>e</kbd>: edit commit
<kbd>A</kbd>: amend commit with staged changes
<kbd>p</kbd>: pick commit (when mid-rebase)
<kbd>t</kbd>: revert commit
<kbd>c</kbd>: copy commit (cherry-pick)
<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>: select commit to diff with another commit
</pre>
## Schowek
<pre>
<kbd>space</kbd>: zastosuj
<kbd>g</kbd>: wyciągnij
<kbd>d</kbd>: porzuć
</pre>
## Commit files
<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
</pre>
## 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
<kbd>space</kbd>: pick hunk
<kbd>b</kbd>: pick both hunks
<kbd>◄</kbd>: select previous conflict
<kbd>►</kbd>: select next conflict
<kbd>▲</kbd>: select top hunk
<kbd>▼</kbd>: select bottom hunk
<kbd>z</kbd>: undo
</pre>

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

94
go.mod
View File

@@ -1,64 +1,68 @@
module github.com/jesseduffield/lazygit
go 1.12
require (
github.com/aws/aws-sdk-go v1.15.21
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 // indirect
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
github.com/aws/aws-sdk-go v1.15.21 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/davecgh/go-spew v1.1.0
github.com/emirpasic/gods v1.9.0
github.com/emirpasic/gods v1.9.0 // indirect
github.com/fatih/color v1.7.0
github.com/fsnotify/fsnotify v1.4.7
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gliderlabs/ssh v0.2.2 // indirect
github.com/go-errors/errors v1.0.1
github.com/go-ini/ini v1.38.2
github.com/go-ini/ini v1.38.2 // indirect
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc
github.com/hashicorp/go-version v1.0.0
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/google/go-cmp v0.3.1 // indirect
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186 // indirect
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 // indirect
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc // indirect
github.com/hashicorp/go-version v1.0.0 // indirect
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63
github.com/jesseduffield/gocui v0.0.0-20190303031804-b502ee11d674
github.com/jesseduffield/gocui v0.3.1-0.20190908012510-092b2290ee54
github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406
github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8
github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7
github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb // indirect
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55
github.com/magiconair/properties v1.8.0
github.com/mattn/go-colorable v0.0.9
github.com/mattn/go-isatty v0.0.3
github.com/mattn/go-runewidth v0.0.2
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55 // indirect
github.com/magiconair/properties v1.8.0 // indirect
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.3 // indirect
github.com/mattn/go-runewidth v0.0.2 // indirect
github.com/mgutz/str v1.2.0
github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699
github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80
github.com/pelletier/go-buffruneio v0.2.0
github.com/pelletier/go-toml v1.2.0
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0
github.com/sergi/go-diff v1.0.0
github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 // indirect
github.com/nicksnyder/go-i18n/v2 v2.0.2
github.com/onsi/ginkgo v1.9.0 // indirect
github.com/onsi/gomega v1.6.0 // indirect
github.com/pelletier/go-buffruneio v0.2.0 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/sergi/go-diff v1.0.0 // indirect
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0
github.com/sirupsen/logrus v1.0.6
github.com/spf13/afero v1.1.1
github.com/spf13/cast v1.2.0
github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834
github.com/spf13/pflag v1.0.2
github.com/sirupsen/logrus v1.3.0
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
github.com/spf13/afero v1.1.1 // indirect
github.com/spf13/cast v1.2.0 // indirect
github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 // indirect
github.com/spf13/pflag v1.0.2 // indirect
github.com/spf13/viper v1.1.0
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
github.com/src-d/gcfg v1.3.0
github.com/src-d/gcfg v1.3.0 // indirect
github.com/stretchr/testify v1.2.2
github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea
github.com/tcnksm/go-gitconfig v0.1.2
github.com/ulikunitz/xz v0.5.4
github.com/xanzy/ssh-agent v0.2.0
golang.org/x/crypto v0.0.0-20180808211826-de0752318171
golang.org/x/net v0.0.0-20180811021610-c39426892332
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0
golang.org/x/text v0.3.0
gopkg.in/src-d/go-billy.v4 v4.2.0
github.com/ulikunitz/xz v0.5.4 // indirect
github.com/xanzy/ssh-agent v0.2.0 // indirect
golang.org/x/text v0.3.2
gopkg.in/ini.v1 v1.46.0 // indirect
gopkg.in/src-d/go-billy.v4 v4.2.0 // indirect
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 // indirect
gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714
gopkg.in/warnings.v0 v0.1.2
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.2.1
)

105
go.sum
View File

@@ -1,22 +1,41 @@
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/aws/aws-sdk-go v1.15.21 h1:STLvc6RrpycslC1NRtTvt/YSgDkIGCTrB9K9vE5R2oQ=
github.com/aws/aws-sdk-go v1.15.21/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
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=
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ini/ini v1.38.2 h1:6Hl/z3p3iFkA0dlDfzYxuFuUGD+kaweypF6btsR2/Q4=
github.com/go-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186 h1:URgjUo+bs1KwatoNbwG0uCO4dHN4r1jsp4a5AGgHRjo=
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 h1:MFPzqpPED05pFyGjNPJEC2sXM6EHTzFyvX+0s0JoZ48=
@@ -27,24 +46,32 @@ github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnn
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 h1:qio0y/sQdhbHRA3cmgczo04MaSV2zw+n46G1owvgWIk=
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331/go.mod h1:BT+PgT529opmb6mcUY+Fg0IwVRRmwqFyavEMU17GnBg=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
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/go-getter v0.0.0-20180822080847-906e15686e63 h1:Nrr/yUxNjXWYK0B3IqcFlYh1ICnesJDB4ogcfOVc5Ns=
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63/go.mod h1:fNqjRf+4XnTo2PrGN1JRb79b/BeoHwP4lU00f39SQY0=
github.com/jesseduffield/gocui v0.0.0-20181209104758-fe55a32c8a4c h1:jEfh/vAtfF3pQ8xFhpYR/0S4iHo11VfaYelJmzZJm94=
github.com/jesseduffield/gocui v0.0.0-20181209104758-fe55a32c8a4c/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/gocui v0.3.1-0.20190908012510-092b2290ee54 h1:zK8KCB55hNdBadw5iJll46xQL6WXEwEXbt9B+QyYswo=
github.com/jesseduffield/gocui v0.3.1-0.20190908012510-092b2290ee54/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406 h1:iYMH6h6SuWuBkIzRtymosE8NpSgTK0oRMfyTdVWgxzc=
github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406/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-20180919093808-1e272ff78dcb h1:cFHYEWpQEfzFZVKiKZytCUX4UwQixKSw0kd3WhluPsY=
github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55 h1:S38dC4mEwxdw/U41+97VWdbun8mTcTjwg5Ujfg8QPME=
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
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/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
@@ -61,23 +88,31 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI=
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80 h1:7ory6RlsEkeK89iyV7Imz3sVz8YHeSw29w3PehpCWC0=
github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4=
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.6 h1:SooTCzUOOs899x/M7gmSS+dgL+AUfSWqAcHLN3auCic=
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.6/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU=
github.com/nicksnyder/go-i18n/v2 v2.0.2 h1:KsHGcTByIM0mHZKQGy0nlJLOjPNjQ6MVib/3PvsBDNY=
github.com/nicksnyder/go-i18n/v2 v2.0.2/go.mod h1:JXS4+OKhbcwDoVTEj0sLFWL1vOwec2g/YBAxZ9owJqY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc=
github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M=
github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
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/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
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.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I=
github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
@@ -92,29 +127,53 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJ
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/src-d/gcfg v1.3.0 h1:2BEDr8r0I0b8h/fOqwtxCEiq2HJu8n2JGZJQFGXWLjg=
github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea h1:jysxIKov/4GJ33wI2aRvuIK7yBwB28E5almlgDLPRpM=
github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea/go.mod h1:Ffmqrj3nXIMIjeA4uW3Qjj0Ud9eDoTG0fu4JxyAr/tE=
github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
github.com/ulikunitz/xz v0.5.4 h1:zATC2OoZ8H1TZll3FpbX+ikwmadbO699PE06cIkm9oU=
github.com/ulikunitz/xz v0.5.4/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
golang.org/x/crypto v0.0.0-20180808211826-de0752318171 h1:vYogbvSFj2YXcjQxFHu/rASSOt9sLytpCaSkiwQ135I=
golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0 h1:8H8QZJ30plJyIVj60H3lr8TZGIq2Fh3Cyrs/ZNg1foU=
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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 h1:rlLehGeYg6jfoyz/eDqDU1iRXLKfR42nnNh57ytKEWo=
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/src-d/go-billy.v4 v4.2.0 h1:VGbrP1EsYxtvVPEiHui+4//imr4E5MGEFLx66bQtusg=
gopkg.in/src-d/go-billy.v4 v4.2.0/go.mod h1:ZHSF0JP+7oD97194otDUCD7Ofbk63+xFcfWP5bT6h+Q=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714 h1:+wM2BGgQ1znCKBexOB4OrGVSDw8mtKNUSq3wqxZhi/k=
gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=

View File

@@ -52,6 +52,9 @@ func main() {
}
if err != nil {
if errorMessage, known := app.KnownError(err); known {
log.Fatal(errorMessage)
}
newErr := errors.Wrap(err, 0)
stackTrace := newErr.ErrorStack()
app.Log.Error(stackTrace)

View File

@@ -1,6 +1,7 @@
package app
import (
"bufio"
"fmt"
"io"
"io/ioutil"
@@ -8,12 +9,12 @@ import (
"path/filepath"
"strings"
"github.com/heroku/rollrus"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/updates"
"github.com/jesseduffield/rollrus"
"github.com/shibukawa/configdir"
"github.com/sirupsen/logrus"
)
@@ -32,9 +33,15 @@ type App struct {
ClientContext string
}
type errorMapping struct {
originalError string
newError string
}
func newProductionLogger(config config.AppConfigurer) *logrus.Logger {
log := logrus.New()
log.Out = ioutil.Discard
log.SetLevel(logrus.ErrorLevel)
return log
}
@@ -44,8 +51,18 @@ func globalConfigDir() string {
return configDir.Path
}
func getLogLevel() logrus.Level {
strLevel := os.Getenv("LOG_LEVEL")
level, err := logrus.ParseLevel(strLevel)
if err != nil {
return logrus.DebugLevel
}
return level
}
func newDevelopmentLogger(config config.AppConfigurer) *logrus.Logger {
log := logrus.New()
log.SetLevel(getLogLevel())
file, err := os.OpenFile(filepath.Join(globalConfigDir(), "development.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function)
@@ -103,12 +120,13 @@ func NewApp(config config.AppConfigurer) (*App, error) {
if err != nil {
return app, err
}
if err := app.setupRepo(); err != nil {
return app, err
}
app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr, app.Config)
if err != nil {
if strings.Contains(err.Error(), "Not a git repository") {
fmt.Println("Not in a git repository. Use `git init` to create a new one")
os.Exit(1)
}
return app, err
}
app.Gui, err = gui.NewGui(app.Log, app.GitCommand, app.OSCommand, app.Tr, config, app.Updater)
@@ -118,6 +136,24 @@ func NewApp(config config.AppConfigurer) (*App, error) {
return app, nil
}
func (app *App) setupRepo() error {
// if we are not in a git repo, we ask if we want to `git init`
if err := app.OSCommand.RunCommand("git status"); err != nil {
if !strings.Contains(err.Error(), "Not a git repository") {
return err
}
fmt.Print(app.Tr.SLocalize("CreateRepo"))
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
if strings.Trim(response, " \n") != "y" {
os.Exit(1)
}
if err := app.OSCommand.RunCommand("git init"); err != nil {
return err
}
}
return nil
}
func (app *App) Run() error {
if app.ClientContext == "INTERACTIVE_REBASE" {
return app.Rebase()
@@ -127,7 +163,8 @@ func (app *App) Run() error {
os.Exit(0)
}
return app.Gui.RunWithSubprocesses()
err := app.Gui.RunWithSubprocesses()
return err
}
// Rebase contains logic for when we've been run in demon mode, meaning we've
@@ -161,3 +198,22 @@ func (app *App) Close() error {
}
return nil
}
// KnownError takes an error and tells us whether it's an error that we know about where we can print a nicely formatted version of it rather than panicking with a stack trace
func (app *App) KnownError(err error) (string, bool) {
errorMessage := err.Error()
mappings := []errorMapping{
{
originalError: "fatal: not a git repository (or any of the parent directories): .git",
newError: app.Tr.SLocalize("notARepository"),
},
}
for _, mapping := range mappings {
if strings.Contains(errorMessage, mapping.originalError) {
return mapping.newError, true
}
}
return "", false
}

View File

@@ -18,7 +18,7 @@ type Branch struct {
Selected bool
}
// GetDisplayStrings returns the dispaly string of branch
// GetDisplayStrings returns the display string of branch
func (b *Branch) GetDisplayStrings(isFocused bool) []string {
displayName := utils.ColoredString(b.Name, b.GetColor())
if isFocused && b.Selected && b.Pushables != "" && b.Pullables != "" {

View File

@@ -9,7 +9,7 @@ import (
type Commit struct {
Sha string
Name string
Status string // one of "unpushed", "pushed", "merged", or "rebasing"
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
@@ -23,6 +23,7 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
white := color.New(color.FgWhite)
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,
@@ -39,6 +40,8 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
shaColor = green
case "rebasing":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = white
}

View File

@@ -0,0 +1,13 @@
package commands
// CommitFile : A git commit file
type CommitFile struct {
Sha string
Name string
DisplayString string
}
// GetDisplayStrings is a function.
func (f *CommitFile) GetDisplayStrings(isFocused bool) []string {
return []string{f.DisplayString}
}

14
pkg/commands/errors.go Normal file
View File

@@ -0,0 +1,14 @@
package commands
import "github.com/go-errors/errors"
// WrapError wraps an error for the sake of showing a stack trace at the top level
// the go-errors package, for some reason, does not return nil when you try to wrap
// a non-error, so we're just doing it here
func WrapError(err error) error {
if err == nil {
return err
}
return errors.Wrap(err, 0)
}

View File

@@ -5,14 +5,12 @@ package commands
import (
"bufio"
"bytes"
"os"
"strings"
"unicode/utf8"
"github.com/go-errors/errors"
"github.com/jesseduffield/pty"
"github.com/mgutz/str"
)
// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout
@@ -20,10 +18,7 @@ import (
// As return of output you need to give a string that will be written to stdin
// NOTE: If the return data is empty it won't written anything to stdin
func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error {
splitCmd := str.ToArgv(command)
cmd := c.command(splitCmd[0], splitCmd[1:]...)
cmd.Env = os.Environ()
cmd := c.ExecutableFromString(command)
cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")
var stderr bytes.Buffer

View File

@@ -14,6 +14,7 @@ type File struct {
HasInlineMergeConflicts bool
DisplayString string
Type string // one of 'file', 'directory', and 'other'
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
}
// GetDisplayStrings returns the display string of a file

View File

@@ -25,18 +25,18 @@ func verifyInGitRepo(runCmd func(string) error) error {
func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error {
for {
f, err := stat(".git")
_, err := stat(".git")
if err == nil && f.IsDir() {
if err == nil {
return nil
}
if !os.IsNotExist(err) {
return errors.Wrap(err, 0)
return WrapError(err)
}
if err = chdir(".."); err != nil {
return errors.Wrap(err, 0)
return WrapError(err)
}
}
}
@@ -72,6 +72,7 @@ type GitCommand struct {
getGlobalGitConfig func(string) (string, error)
getLocalGitConfig func(string) (string, error)
removeFile func(string) error
DotGitDir string
}
// NewGitCommand it runs git commands
@@ -99,6 +100,11 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
}
}
dotGitDir, err := findDotGitDir(os.Stat, ioutil.ReadFile)
if err != nil {
return nil, err
}
return &GitCommand{
Log: log,
OSCommand: osCommand,
@@ -109,10 +115,32 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
getGlobalGitConfig: gitconfig.Global,
getLocalGitConfig: gitconfig.Local,
removeFile: os.RemoveAll,
DotGitDir: dotGitDir,
}, nil
}
// GetStashEntries stash entryies
func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) {
f, err := stat(".git")
if err != nil {
return "", err
}
if f.IsDir() {
return ".git", nil
}
fileBytes, err := readFile(".git")
if err != nil {
return "", err
}
fileContent := string(fileBytes)
if !strings.HasPrefix(fileContent, "gitdir: ") {
return "", errors.New(".git is a file which suggests we are in a submodule but the file's contents do not contain a gitdir pointing to the actual .git directory")
}
return strings.TrimSpace(strings.TrimPrefix(fileContent, "gitdir: ")), nil
}
// GetStashEntries stash entries
func (c *GitCommand) GetStashEntries() []*StashEntry {
rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'")
stashEntries := []*StashEntry{}
@@ -148,6 +176,8 @@ func (c *GitCommand) GetStatusFiles() []*File {
filename := c.OSCommand.Unquote(statusString[3:])
_, untracked := map[string]bool{"??": true, "A ": true, "AM": true}[change]
_, hasNoStagedChanges := map[string]bool{" ": true, "U": true, "?": true}[stagedChange]
hasMergeConflicts := change == "UU" || change == "AA" || change == "DU"
hasInlineMergeConflicts := change == "UU" || change == "AA"
file := &File{
Name: filename,
@@ -156,9 +186,10 @@ func (c *GitCommand) GetStatusFiles() []*File {
HasUnstagedChanges: unstagedChange != " ",
Tracked: !untracked,
Deleted: unstagedChange == "D" || stagedChange == "D",
HasMergeConflicts: change == "UU" || change == "AA" || change == "DU",
HasInlineMergeConflicts: change == "UU" || change == "AA",
HasMergeConflicts: hasMergeConflicts,
HasInlineMergeConflicts: hasInlineMergeConflicts,
Type: c.OSCommand.FileType(filename),
ShortStatus: change,
}
files = append(files, file)
}
@@ -217,11 +248,11 @@ func includesInt(list []int, a int) bool {
// ResetAndClean removes all unstaged changes and removes all untracked files
func (c *GitCommand) ResetAndClean() error {
if err := c.OSCommand.RunCommand("git reset --hard HEAD"); err != nil {
if err := c.ResetHardHead(); err != nil {
return err
}
return c.OSCommand.RunCommand("git clean -fd")
return c.RemoveUntrackedFiles()
}
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
@@ -274,8 +305,8 @@ func (c *GitCommand) Fetch(unamePassQuestion func(string) string, canAskForCrede
}
// ResetToCommit reset to commit
func (c *GitCommand) ResetToCommit(sha string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git reset %s", sha))
func (c *GitCommand) ResetToCommit(sha string, strength string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git reset --%s %s", strength, sha))
}
// NewBranch create new branch
@@ -334,8 +365,8 @@ func (c *GitCommand) usingGpg() bool {
}
// Commit commits to git
func (c *GitCommand) Commit(message string) (*exec.Cmd, error) {
command := fmt.Sprintf("git commit -m %s", c.OSCommand.Quote(message))
func (c *GitCommand) Commit(message string, flags string) (*exec.Cmd, error) {
command := fmt.Sprintf("git commit %s -m %s", flags, c.OSCommand.Quote(message))
if c.usingGpg() {
return c.OSCommand.PrepareSubProcess(c.OSCommand.Platform.shell, c.OSCommand.Platform.shellArg, command), nil
}
@@ -408,7 +439,7 @@ func (c *GitCommand) UnStageFile(fileName string, tracked bool) error {
// GitStatus returns the plaintext short status of the repo
func (c *GitCommand) GitStatus() (string, error) {
return c.OSCommand.RunCommandWithOutput("git status --untracked-files=all --short")
return c.OSCommand.RunCommandWithOutput("git status --untracked-files=all --porcelain")
}
// IsInMergeState states whether we are still mid-merge
@@ -423,14 +454,14 @@ func (c *GitCommand) IsInMergeState() (bool, error) {
// RebaseMode returns "" for non-rebase mode, "normal" for normal rebase
// and "interactive" for interactive rebase
func (c *GitCommand) RebaseMode() (string, error) {
exists, err := c.OSCommand.FileExists(".git/rebase-apply")
exists, err := c.OSCommand.FileExists(fmt.Sprintf("%s/rebase-apply", c.DotGitDir))
if err != nil {
return "", err
}
if exists {
return "normal", nil
}
exists, err = c.OSCommand.FileExists(".git/rebase-merge")
exists, err = c.OSCommand.FileExists(fmt.Sprintf("%s/rebase-merge", c.DotGitDir))
if exists {
return "interactive", err
} else {
@@ -438,19 +469,25 @@ func (c *GitCommand) RebaseMode() (string, error) {
}
}
// RemoveFile directly
func (c *GitCommand) RemoveFile(file *File) error {
// DiscardAllFileChanges directly
func (c *GitCommand) DiscardAllFileChanges(file *File) error {
// if the file isn't tracked, we assume you want to delete it
quotedFileName := c.OSCommand.Quote(file.Name)
if file.HasStagedChanges {
if file.HasStagedChanges || file.HasMergeConflicts {
if err := c.OSCommand.RunCommand(fmt.Sprintf("git reset -- %s", quotedFileName)); err != nil {
return err
}
}
if !file.Tracked {
return c.removeFile(file.Name)
}
// if the file is tracked, we assume you want to just check it out
return c.DiscardUnstagedFileChanges(file)
}
// DiscardUnstagedFileChanges directly
func (c *GitCommand) DiscardUnstagedFileChanges(file *File) error {
quotedFileName := c.OSCommand.Quote(file.Name)
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -- %s", quotedFileName))
}
@@ -575,7 +612,7 @@ func (c *GitCommand) ApplyPatch(patch string) (string, error) {
return "", err
}
defer func() { _ = c.OSCommand.RemoveFile(filename) }()
defer func() { _ = c.OSCommand.Remove(filename) }()
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git apply --cached %s", c.OSCommand.Quote(filename)))
}
@@ -588,7 +625,7 @@ func (c *GitCommand) FastForward(branchName string) error {
func (c *GitCommand) RunSkipEditorCommand(command string) error {
cmd := c.OSCommand.ExecutableFromString(command)
cmd.Env = append(
os.Environ(),
cmd.Env,
"LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY",
"EDITOR="+c.OSCommand.GetLazygitPath(),
)
@@ -608,12 +645,12 @@ func (c *GitCommand) GenericMerge(commandType string, command string) error {
}
func (c *GitCommand) RewordCommit(commits []*Commit, index int) (*exec.Cmd, error) {
todo, err := c.GenerateGenericRebaseTodo(commits, index, "reword")
todo, sha, err := c.GenerateGenericRebaseTodo(commits, index, "reword")
if err != nil {
return nil, err
}
return c.PrepareInteractiveRebaseCommand(commits[index+1].Sha, todo, false)
return c.PrepareInteractiveRebaseCommand(sha, todo, false)
}
func (c *GitCommand) MoveCommitDown(commits []*Commit, index int) error {
@@ -638,12 +675,12 @@ func (c *GitCommand) MoveCommitDown(commits []*Commit, index int) error {
}
func (c *GitCommand) InteractiveRebase(commits []*Commit, index int, action string) error {
todo, err := c.GenerateGenericRebaseTodo(commits, index, action)
todo, sha, err := c.GenerateGenericRebaseTodo(commits, index, action)
if err != nil {
return err
}
cmd, err := c.PrepareInteractiveRebaseCommand(commits[index+1].Sha, todo, true)
cmd, err := c.PrepareInteractiveRebaseCommand(sha, todo, true)
if err != nil {
return err
}
@@ -658,7 +695,7 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
ex := c.OSCommand.GetLazygitPath()
debug := "FALSE"
if c.OSCommand.Config.GetDebug() == true {
if c.OSCommand.Config.GetDebug() {
debug = "TRUE"
}
@@ -697,38 +734,45 @@ func (c *GitCommand) SoftReset(baseSha string) error {
return c.OSCommand.RunCommand("git reset --soft " + baseSha)
}
func (c *GitCommand) GenerateGenericRebaseTodo(commits []*Commit, index int, action string) (string, error) {
if len(commits) <= index+1 {
// assuming they aren't picking the bottom commit
return "", errors.New(c.Tr.SLocalize("CannotRebaseOntoFirstCommit"))
func (c *GitCommand) GenerateGenericRebaseTodo(commits []*Commit, actionIndex int, action string) (string, string, error) {
baseIndex := actionIndex + 1
if len(commits) <= baseIndex {
return "", "", errors.New(c.Tr.SLocalize("CannotRebaseOntoFirstCommit"))
}
if action == "squash" || action == "fixup" {
baseIndex++
if len(commits) <= baseIndex {
return "", "", errors.New(c.Tr.SLocalize("CannotSquashOntoSecondCommit"))
}
}
todo := ""
for i, commit := range commits[0 : index+1] {
for i, commit := range commits[0:baseIndex] {
a := "pick"
if i == index {
if i == actionIndex {
a = action
}
todo = a + " " + commit.Sha + " " + commit.Name + "\n" + todo
}
return todo, nil
return todo, commits[baseIndex].Sha, nil
}
// AmendTo amends the given commit with whatever files are staged
func (c *GitCommand) AmendTo(sha string) error {
if err := c.OSCommand.RunCommand(fmt.Sprintf("git commit --fixup=%s", sha)); err != nil {
if err := c.CreateFixupCommit(sha); err != nil {
return err
}
return c.RunSkipEditorCommand(
fmt.Sprintf(
"git rebase --interactive --autostash --autosquash %s^", sha,
),
)
return c.SquashAllAboveFixupCommits(sha)
}
// EditRebaseTodo sets the action at a given index in the git-rebase-todo file
func (c *GitCommand) EditRebaseTodo(index int, action string) error {
fileName := ".git/rebase-merge/git-rebase-todo"
fileName := fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.DotGitDir)
bytes, err := ioutil.ReadFile(fileName)
if err != nil {
return err
@@ -760,7 +804,7 @@ func (c *GitCommand) getTodoCommitCount(content []string) int {
// MoveTodoDown moves a rebase todo item down by one position
func (c *GitCommand) MoveTodoDown(index int) error {
fileName := ".git/rebase-merge/git-rebase-todo"
fileName := fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.DotGitDir)
bytes, err := ioutil.ReadFile(fileName)
if err != nil {
return err
@@ -796,3 +840,169 @@ func (c *GitCommand) CherryPickCommits(commits []*Commit) error {
return c.OSCommand.RunPreparedCommand(cmd)
}
// GetCommitFiles get the specified commit files
func (c *GitCommand) GetCommitFiles(commitSha string) ([]*CommitFile, error) {
cmd := fmt.Sprintf("git show --pretty= --name-only %s", commitSha)
files, err := c.OSCommand.RunCommandWithOutput(cmd)
if err != nil {
return nil, err
}
commitFiles := make([]*CommitFile, 0)
for _, file := range strings.Split(strings.TrimRight(files, "\n"), "\n") {
commitFiles = append(commitFiles, &CommitFile{
Sha: commitSha,
Name: file,
DisplayString: file,
})
}
return commitFiles, nil
}
// ShowCommitFile get the diff of specified commit file
func (c *GitCommand) ShowCommitFile(commitSha, fileName string) (string, error) {
cmd := fmt.Sprintf("git show --color %s -- %s", commitSha, fileName)
return c.OSCommand.RunCommandWithOutput(cmd)
}
// CheckoutFile checks out the file for the given commit
func (c *GitCommand) CheckoutFile(commitSha, fileName string) error {
cmd := fmt.Sprintf("git checkout %s %s", commitSha, fileName)
return c.OSCommand.RunCommand(cmd)
}
// DiscardOldFileChanges discards changes to a file from an old commit
func (c *GitCommand) DiscardOldFileChanges(commits []*Commit, commitIndex int, fileName string) error {
if len(commits)-1 < commitIndex {
return errors.New("index outside of range of commits")
}
// we can make this GPG thing possible it just means we need to do this in two parts:
// one where we handle the possibility of a credential request, and the other
// where we continue the rebase
if c.usingGpg() {
return errors.New(c.Tr.SLocalize("DisabledForGPG"))
}
todo, sha, err := c.GenerateGenericRebaseTodo(commits, commitIndex, "edit")
if err != nil {
return err
}
cmd, err := c.PrepareInteractiveRebaseCommand(sha, todo, true)
if err != nil {
return err
}
if err := c.OSCommand.RunPreparedCommand(cmd); err != nil {
return err
}
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
if err := c.OSCommand.RunCommand(fmt.Sprintf("git cat-file -e HEAD^:%s", fileName)); err != nil {
if err := c.OSCommand.Remove(fileName); err != nil {
return err
}
if err := c.StageFile(fileName); err != nil {
return err
}
} else if err := c.CheckoutFile("HEAD^", fileName); err != nil {
return err
}
// amend the commit
cmd, err = c.AmendHead()
if cmd != nil {
return errors.New("received unexpected pointer to cmd")
}
if err != nil {
return err
}
// continue
return c.GenericMerge("rebase", "continue")
}
// DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .`
func (c *GitCommand) DiscardAnyUnstagedFileChanges() error {
return c.OSCommand.RunCommand("git checkout -- .")
}
// RemoveUntrackedFiles runs `git clean -fd`
func (c *GitCommand) RemoveUntrackedFiles() error {
return c.OSCommand.RunCommand("git clean -fd")
}
// ResetHardHead runs `git reset --hard HEAD`
func (c *GitCommand) ResetHardHead() error {
return c.OSCommand.RunCommand("git reset --hard HEAD")
}
// ResetSoftHead runs `git reset --soft HEAD`
func (c *GitCommand) ResetSoftHead() error {
return c.OSCommand.RunCommand("git reset --soft HEAD")
}
// DiffCommits show diff between commits
func (c *GitCommand) DiffCommits(sha1, sha2 string) (string, error) {
cmd := fmt.Sprintf("git diff --color %s %s", sha1, sha2)
return c.OSCommand.RunCommandWithOutput(cmd)
}
// CreateFixupCommit creates a commit that fixes up a previous commit
func (c *GitCommand) CreateFixupCommit(sha string) error {
cmd := fmt.Sprintf("git commit --fixup=%s", sha)
return c.OSCommand.RunCommand(cmd)
}
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
func (c *GitCommand) SquashAllAboveFixupCommits(sha string) error {
return c.RunSkipEditorCommand(
fmt.Sprintf(
"git rebase --interactive --autostash --autosquash %s^",
sha,
),
)
}
// StashSaveStagedChanges stashes only the currently staged changes. This takes a few steps
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
func (c *GitCommand) StashSaveStagedChanges(message string) error {
if err := c.OSCommand.RunCommand("git stash --keep-index"); err != nil {
return err
}
if err := c.StashSave(message); err != nil {
return err
}
if err := c.OSCommand.RunCommand("git stash apply stash@{1}"); err != nil {
return err
}
if err := c.OSCommand.PipeCommands("git stash show -p", "git apply -R"); err != nil {
return err
}
if err := c.OSCommand.RunCommand("git stash drop stash@{1}"); err != nil {
return err
}
// if you had staged an untracked file, that will now appear as 'AD' in git status
// meaning it's deleted in your working tree but added in your index. Given that it's
// now safely stashed, we need to remove it.
files := c.GetStatusFiles()
for _, file := range files {
if file.ShortStatus == "AD" {
if err := c.UnStageFile(file.Name, false); err != nil {
return err
}
}
}
return nil
}

View File

@@ -8,6 +8,7 @@ import (
"testing"
"time"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/test"
"github.com/stretchr/testify/assert"
@@ -355,52 +356,72 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
func(cmd string, args ...string) *exec.Cmd {
return exec.Command(
"echo",
"MM file1.txt\nA file3.txt\nAM file2.txt\n?? file4.txt",
"MM file1.txt\nA file3.txt\nAM file2.txt\n?? file4.txt\nUU file5.txt",
)
},
func(files []*File) {
assert.Len(t, files, 4)
assert.Len(t, files, 5)
expected := []*File{
{
Name: "file1.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: true,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "MM file1.txt",
Type: "other",
Name: "file1.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: true,
Deleted: false,
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "MM file1.txt",
Type: "other",
ShortStatus: "MM",
},
{
Name: "file3.txt",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "A file3.txt",
Type: "other",
Name: "file3.txt",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "A file3.txt",
Type: "other",
ShortStatus: "A ",
},
{
Name: "file2.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "AM file2.txt",
Type: "other",
Name: "file2.txt",
HasStagedChanges: true,
HasUnstagedChanges: true,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "AM file2.txt",
Type: "other",
ShortStatus: "AM",
},
{
Name: "file4.txt",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "?? file4.txt",
Type: "other",
Name: "file4.txt",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
HasInlineMergeConflicts: false,
DisplayString: "?? file4.txt",
Type: "other",
ShortStatus: "??",
},
{
Name: "file5.txt",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: true,
Deleted: false,
HasMergeConflicts: true,
HasInlineMergeConflicts: true,
DisplayString: "UU file5.txt",
Type: "other",
ShortStatus: "UU",
},
}
@@ -616,12 +637,12 @@ func TestGitCommandResetToCommit(t *testing.T) {
gitCmd := NewDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"reset", "78976bc"}, args)
assert.EqualValues(t, []string{"reset", "--hard", "78976bc"}, args)
return exec.Command("echo")
}
assert.NoError(t, gitCmd.ResetToCommit("78976bc"))
assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard"))
}
// TestGitCommandNewBranch is a function.
@@ -801,6 +822,7 @@ func TestGitCommandCommit(t *testing.T) {
command func(string, ...string) *exec.Cmd
getGlobalGitConfig func(string) (string, error)
test func(*exec.Cmd, error)
flags string
}
scenarios := []scenario{
@@ -808,7 +830,7 @@ func TestGitCommandCommit(t *testing.T) {
"Commit using gpg",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "bash", cmd)
assert.EqualValues(t, []string{"-c", `git commit -m 'test'`}, args)
assert.EqualValues(t, []string{"-c", `git commit -m 'test'`}, args)
return exec.Command("echo")
},
@@ -819,6 +841,7 @@ func TestGitCommandCommit(t *testing.T) {
assert.NotNil(t, cmd)
assert.Nil(t, err)
},
"",
},
{
"Commit without using gpg",
@@ -835,6 +858,24 @@ func TestGitCommandCommit(t *testing.T) {
assert.Nil(t, cmd)
assert.Nil(t, err)
},
"",
},
{
"Commit with --no-verify flag",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"commit", "--no-verify", "-m", "test"}, args)
return exec.Command("echo")
},
func(string) (string, error) {
return "false", nil
},
func(cmd *exec.Cmd, err error) {
assert.Nil(t, cmd)
assert.Nil(t, err)
},
"--no-verify",
},
{
"Commit without using gpg with an error",
@@ -851,6 +892,7 @@ func TestGitCommandCommit(t *testing.T) {
assert.Nil(t, cmd)
assert.Error(t, err)
},
"",
},
}
@@ -859,7 +901,7 @@ func TestGitCommandCommit(t *testing.T) {
gitCmd := NewDummyGitCommand()
gitCmd.getGlobalGitConfig = s.getGlobalGitConfig
gitCmd.OSCommand.command = s.command
s.test(gitCmd.Commit("test"))
s.test(gitCmd.Commit("test", s.flags))
})
}
}
@@ -1140,8 +1182,8 @@ func TestGitCommandIsInMergeState(t *testing.T) {
}
}
// TestGitCommandRemoveFile is a function.
func TestGitCommandRemoveFile(t *testing.T) {
// TestGitCommandDiscardAllFileChanges is a function.
func TestGitCommandDiscardAllFileChanges(t *testing.T) {
type scenario struct {
testName string
command func() (func(string, ...string) *exec.Cmd, *[][]string)
@@ -1252,7 +1294,7 @@ func TestGitCommandRemoveFile(t *testing.T) {
},
},
{
"Reset and checkout",
"Reset and checkout staged changes",
func() (func(string, ...string) *exec.Cmd, *[][]string) {
cmdsCalled := [][]string{}
return func(cmd string, args ...string) *exec.Cmd {
@@ -1278,6 +1320,33 @@ func TestGitCommandRemoveFile(t *testing.T) {
return nil
},
},
{
"Reset and checkout merge conflicts",
func() (func(string, ...string) *exec.Cmd, *[][]string) {
cmdsCalled := [][]string{}
return func(cmd string, args ...string) *exec.Cmd {
cmdsCalled = append(cmdsCalled, args)
return exec.Command("echo")
}, &cmdsCalled
},
func(cmdsCalled *[][]string, err error) {
assert.NoError(t, err)
assert.Len(t, *cmdsCalled, 2)
assert.EqualValues(t, *cmdsCalled, [][]string{
{"reset", "--", "test"},
{"checkout", "--", "test"},
})
},
&File{
Name: "test",
Tracked: true,
HasMergeConflicts: true,
},
func(string) error {
return nil
},
},
{
"Reset and remove",
func() (func(string, ...string) *exec.Cmd, *[][]string) {
@@ -1337,7 +1406,7 @@ func TestGitCommandRemoveFile(t *testing.T) {
gitCmd := NewDummyGitCommand()
gitCmd.OSCommand.command, cmdsCalled = s.command()
gitCmd.removeFile = s.removeFile
s.test(cmdsCalled, gitCmd.RemoveFile(s.file))
s.test(cmdsCalled, gitCmd.DiscardAllFileChanges(s.file))
})
}
}
@@ -1707,7 +1776,472 @@ func TestGitCommandRebaseBranch(t *testing.T) {
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.RebaseBranch(s.arg))
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.RebaseBranch(s.arg))
})
}
}
// TestGitCommandCheckoutFile is a function.
func TestGitCommandCheckoutFile(t *testing.T) {
type scenario struct {
testName string
commitSha string
fileName string
command func(string, ...string) *exec.Cmd
test func(error)
}
scenarios := []scenario{
{
"typical case",
"11af912",
"test999.txt",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git checkout 11af912 test999.txt",
Replace: "echo",
},
}),
func(err error) {
assert.NoError(t, err)
},
},
{
"returns error if there is one",
"11af912",
"test999.txt",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git checkout 11af912 test999.txt",
Replace: "test",
},
}),
func(err error) {
assert.Error(t, err)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.CheckoutFile(s.commitSha, s.fileName))
})
}
}
// TestGitCommandDiscardOldFileChanges is a function.
func TestGitCommandDiscardOldFileChanges(t *testing.T) {
type scenario struct {
testName string
getLocalGitConfig func(string) (string, error)
commits []*Commit
commitIndex int
fileName string
command func(string, ...string) *exec.Cmd
test func(error)
}
scenarios := []scenario{
{
"returns error when index outside of range of commits",
func(string) (string, error) {
return "", nil
},
[]*Commit{},
0,
"test999.txt",
nil,
func(err error) {
assert.Error(t, err)
},
},
{
"returns error when using gpg",
func(string) (string, error) {
return "true", nil
},
[]*Commit{{Name: "commit", Sha: "123456"}},
0,
"test999.txt",
nil,
func(err error) {
assert.Error(t, err)
},
},
{
"checks out file if it already existed",
func(string) (string, error) {
return "", nil
},
[]*Commit{
{Name: "commit", Sha: "123456"},
{Name: "commit2", Sha: "abcdef"},
},
0,
"test999.txt",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git rebase --interactive --autostash abcdef",
Replace: "echo",
},
{
Expect: "git cat-file -e HEAD^:test999.txt",
Replace: "echo",
},
{
Expect: "git checkout HEAD^ test999.txt",
Replace: "echo",
},
{
Expect: "git commit --amend --no-edit",
Replace: "echo",
},
{
Expect: "git rebase --continue",
Replace: "echo",
},
}),
func(err error) {
assert.NoError(t, err)
},
},
// test for when the file was created within the commit requires a refactor to support proper mocks
// currently we'd need to mock out the os.Remove function and that's gonna introduce tech debt
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
gitCmd.getLocalGitConfig = s.getLocalGitConfig
s.test(gitCmd.DiscardOldFileChanges(s.commits, s.commitIndex, s.fileName))
})
}
}
// TestGitCommandShowCommitFile is a function.
func TestGitCommandShowCommitFile(t *testing.T) {
type scenario struct {
testName string
commitSha string
fileName string
command func(string, ...string) *exec.Cmd
test func(string, error)
}
scenarios := []scenario{
{
"valid case",
"123456",
"hello.txt",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git show --color 123456 -- hello.txt",
Replace: "echo -n hello",
},
}),
func(str string, err error) {
assert.NoError(t, err)
assert.Equal(t, "hello", str)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.ShowCommitFile(s.commitSha, s.fileName))
})
}
}
// TestGitCommandGetCommitFiles is a function.
func TestGitCommandGetCommitFiles(t *testing.T) {
type scenario struct {
testName string
commitSha string
command func(string, ...string) *exec.Cmd
test func([]*CommitFile, error)
}
scenarios := []scenario{
{
"valid case",
"123456",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: "git show --pretty= --name-only 123456",
Replace: "echo 'hello\nworld'",
},
}),
func(commitFiles []*CommitFile, err error) {
assert.NoError(t, err)
assert.Equal(t, []*CommitFile{
{Sha: "123456", Name: "hello", DisplayString: "hello"},
{Sha: "123456", Name: "world", DisplayString: "world"},
}, commitFiles)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.GetCommitFiles(s.commitSha))
})
}
}
// TestGitCommandDiscardUnstagedFileChanges is a function.
func TestGitCommandDiscardUnstagedFileChanges(t *testing.T) {
type scenario struct {
testName string
file *File
command func(string, ...string) *exec.Cmd
test func(error)
}
scenarios := []scenario{
{
"valid case",
&File{Name: "test.txt"},
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: `git checkout -- "test.txt"`,
Replace: "echo",
},
}),
func(err error) {
assert.NoError(t, err)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.DiscardUnstagedFileChanges(s.file))
})
}
}
// TestGitCommandDiscardAnyUnstagedFileChanges is a function.
func TestGitCommandDiscardAnyUnstagedFileChanges(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
test func(error)
}
scenarios := []scenario{
{
"valid case",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: `git checkout -- .`,
Replace: "echo",
},
}),
func(err error) {
assert.NoError(t, err)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.DiscardAnyUnstagedFileChanges())
})
}
}
// TestGitCommandRemoveUntrackedFiles is a function.
func TestGitCommandRemoveUntrackedFiles(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
test func(error)
}
scenarios := []scenario{
{
"valid case",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: `git clean -fd`,
Replace: "echo",
},
}),
func(err error) {
assert.NoError(t, err)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.RemoveUntrackedFiles())
})
}
}
// TestGitCommandResetHardHead is a function.
func TestGitCommandResetHardHead(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
test func(error)
}
scenarios := []scenario{
{
"valid case",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: `git reset --hard HEAD`,
Replace: "echo",
},
}),
func(err error) {
assert.NoError(t, err)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.ResetHardHead())
})
}
}
// TestGitCommandCreateFixupCommit is a function.
func TestGitCommandCreateFixupCommit(t *testing.T) {
type scenario struct {
testName string
sha string
command func(string, ...string) *exec.Cmd
test func(error)
}
scenarios := []scenario{
{
"valid case",
"12345",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
Expect: `git commit --fixup=12345`,
Replace: "echo",
},
}),
func(err error) {
assert.NoError(t, err)
},
},
}
gitCmd := NewDummyGitCommand()
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.OSCommand.command = s.command
s.test(gitCmd.CreateFixupCommit(s.sha))
})
}
}
func TestFindDotGitDir(t *testing.T) {
type scenario struct {
testName string
stat func(string) (os.FileInfo, error)
readFile func(filename string) ([]byte, error)
test func(string, error)
}
scenarios := []scenario{
{
".git is a directory",
func(dotGit string) (os.FileInfo, error) {
assert.Equal(t, ".git", dotGit)
return os.Stat("testdata/a_dir")
},
func(dotGit string) ([]byte, error) {
assert.Fail(t, "readFile should not be called if .git is a directory")
return nil, nil
},
func(gitDir string, err error) {
assert.NoError(t, err)
assert.Equal(t, ".git", gitDir)
},
},
{
".git is a file",
func(dotGit string) (os.FileInfo, error) {
assert.Equal(t, ".git", dotGit)
return os.Stat("testdata/a_file")
},
func(dotGit string) ([]byte, error) {
assert.Equal(t, ".git", dotGit)
return []byte("gitdir: blah\n"), nil
},
func(gitDir string, err error) {
assert.NoError(t, err)
assert.Equal(t, "blah", gitDir)
},
},
{
"os.Stat returns an error",
func(dotGit string) (os.FileInfo, error) {
assert.Equal(t, ".git", dotGit)
return nil, errors.New("error")
},
func(dotGit string) ([]byte, error) {
assert.Fail(t, "readFile should not be called os.Stat returns an error")
return nil, nil
},
func(gitDir string, err error) {
assert.Error(t, err)
},
},
{
"readFile returns an error",
func(dotGit string) (os.FileInfo, error) {
assert.Equal(t, ".git", dotGit)
return os.Stat("testdata/a_file")
},
func(dotGit string) ([]byte, error) {
return nil, errors.New("error")
},
func(gitDir string, err error) {
assert.Error(t, err)
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
s.test(findDotGitDir(s.stat, s.readFile))
})
}
}

View File

@@ -7,6 +7,7 @@ import (
"path/filepath"
"regexp"
"strings"
"sync"
"github.com/go-errors/errors"
@@ -77,8 +78,9 @@ func (c *OSCommand) RunExecutable(cmd *exec.Cmd) error {
// ExecutableFromString takes a string like `git status` and returns an executable command for it
func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
splitCmd := str.ToArgv(commandStr)
c.Log.Info(splitCmd)
return c.command(splitCmd[0], splitCmd[1:]...)
cmd := c.command(splitCmd[0], splitCmd[1:]...)
cmd.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
return cmd
}
// RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper
@@ -145,7 +147,7 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) {
// errors like 'exit status 1' are not very useful so we'll create an error
// from the combined output
if outputString == "" {
return "", errors.Wrap(err, 0)
return "", WrapError(err)
}
return outputString, errors.New(outputString)
}
@@ -200,8 +202,13 @@ func (c *OSCommand) EditFile(filename string) (*exec.Cmd, error) {
}
// PrepareSubProcess iniPrepareSubProcessrocess then tells the Gui to switch to it
// TODO: see if this needs to exist, given that ExecutableFromString does the same things
func (c *OSCommand) PrepareSubProcess(cmdName string, commandArgs ...string) *exec.Cmd {
return c.command(cmdName, commandArgs...)
cmd := c.command(cmdName, commandArgs...)
if cmd != nil {
cmd.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
}
return cmd
}
// Quote wraps a message in platform-specific quotation marks
@@ -224,13 +231,13 @@ func (c *OSCommand) Unquote(message string) string {
func (c *OSCommand) AppendLineToFile(filename, line string) error {
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return errors.Wrap(err, 0)
return WrapError(err)
}
defer f.Close()
_, err = f.WriteString("\n" + line)
if err != nil {
return errors.Wrap(err, 0)
return WrapError(err)
}
return nil
}
@@ -240,25 +247,25 @@ func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
tmpfile, err := ioutil.TempFile("", filename)
if err != nil {
c.Log.Error(err)
return "", errors.Wrap(err, 0)
return "", WrapError(err)
}
if _, err := tmpfile.WriteString(content); err != nil {
c.Log.Error(err)
return "", errors.Wrap(err, 0)
return "", WrapError(err)
}
if err := tmpfile.Close(); err != nil {
c.Log.Error(err)
return "", errors.Wrap(err, 0)
return "", WrapError(err)
}
return tmpfile.Name(), nil
}
// RemoveFile removes a file at the specified path
func (c *OSCommand) RemoveFile(filename string) error {
err := os.Remove(filename)
return errors.Wrap(err, 0)
// Remove removes a file or directory at the specified path
func (c *OSCommand) Remove(filename string) error {
err := os.RemoveAll(filename)
return WrapError(err)
}
// FileExists checks whether a file exists at the specified path
@@ -296,3 +303,68 @@ func (c *OSCommand) GetLazygitPath() string {
}
return filepath.ToSlash(ex)
}
// RunCustomCommand returns the pointer to a custom command
func (c *OSCommand) RunCustomCommand(command string) *exec.Cmd {
return c.PrepareSubProcess(c.Platform.shell, c.Platform.shellArg, command)
}
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
func (c *OSCommand) PipeCommands(commandStrings ...string) error {
cmds := make([]*exec.Cmd, len(commandStrings))
for i, str := range commandStrings {
cmds[i] = c.ExecutableFromString(str)
}
for i := 0; i < len(cmds)-1; i++ {
stdout, err := cmds[i].StdoutPipe()
if err != nil {
return err
}
cmds[i+1].Stdin = stdout
}
// keeping this here in case I adapt this code for some other purpose in the future
// cmds[len(cmds)-1].Stdout = os.Stdout
finalErrors := []string{}
wg := sync.WaitGroup{}
wg.Add(len(cmds))
for _, cmd := range cmds {
currentCmd := cmd
go func() {
stderr, err := currentCmd.StderrPipe()
if err != nil {
c.Log.Error(err)
}
if err := currentCmd.Start(); err != nil {
c.Log.Error(err)
}
if b, err := ioutil.ReadAll(stderr); err == nil {
if len(b) > 0 {
finalErrors = append(finalErrors, string(b))
}
}
if err := currentCmd.Wait(); err != nil {
c.Log.Error(err)
}
wg.Done()
}()
}
wg.Wait()
if len(finalErrors) > 0 {
return errors.New(strings.Join(finalErrors, "\n"))
}
return nil
}

View File

@@ -34,7 +34,7 @@ func getServices() []*Service {
},
{
Name: "bitbucket.org",
PullRequestURL: "https://bitbucket.org/%s/%s/pull-requests/new?t=%s",
PullRequestURL: "https://bitbucket.org/%s/%s/pull-requests/new?source=%s&t=1",
},
{
Name: "gitlab.com",

View File

@@ -64,7 +64,7 @@ func TestCreatePullRequest(t *testing.T) {
}
assert.Equal(t, cmd, "open")
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?t=feature/profile-page"})
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature/profile-page&t=1"})
return exec.Command("echo")
},
func(err error) {
@@ -83,7 +83,7 @@ func TestCreatePullRequest(t *testing.T) {
}
assert.Equal(t, cmd, "open")
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?t=feature/events"})
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature/events&t=1"})
return exec.Command("echo")
},
func(err error) {

View File

@@ -7,7 +7,7 @@ type StashEntry struct {
DisplayString string
}
// GetDisplayStrings returns the dispaly string of branch
// GetDisplayStrings returns the display string of branch
func (s *StashEntry) GetDisplayStrings(isFocused bool) []string {
return []string{s.DisplayString}
}

0
pkg/commands/testdata/a_dir/file vendored Normal file
View File

0
pkg/commands/testdata/a_file vendored Normal file
View File

View File

@@ -196,7 +196,7 @@ func (c *AppConfig) WriteToUserConfig(key, value string) error {
return v.WriteConfig()
}
// SaveAppState marhsalls the AppState struct and writes it to the disk
// SaveAppState marshalls the AppState struct and writes it to the disk
func (c *AppConfig) SaveAppState() error {
marshalledAppState, err := yaml.Marshal(c.AppState)
if err != nil {
@@ -245,9 +245,11 @@ func GetDefaultConfig() []byte {
- blue
commitLength:
show: true
git:
merging:
manualCommit: false
git:
merging:
manualCommit: false
skipHookPrefix: 'WIP'
autoFetch: true
update:
method: prompt # can be: prompt | background | never
days: 14 # how often a update is checked for

View File

@@ -70,7 +70,7 @@ func (b *BranchListBuilder) obtainSafeBranches() []*commands.Branch {
if err != nil {
panic(err)
}
err = bIter.ForEach(func(b *plumbing.Reference) error {
bIter.ForEach(func(b *plumbing.Reference) error {
name := b.Name().Short()
branches = append(branches, &commands.Branch{Name: name})
return nil

View File

@@ -31,16 +31,18 @@ type CommitListBuilder struct {
OSCommand *commands.OSCommand
Tr *i18n.Localizer
CherryPickedCommits []*commands.Commit
DiffEntries []*commands.Commit
}
// NewCommitListBuilder builds a new commit list builder
func NewCommitListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand, osCommand *commands.OSCommand, tr *i18n.Localizer, cherryPickedCommits []*commands.Commit) (*CommitListBuilder, error) {
func NewCommitListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand, osCommand *commands.OSCommand, tr *i18n.Localizer, cherryPickedCommits []*commands.Commit, diffEntries []*commands.Commit) (*CommitListBuilder, error) {
return &CommitListBuilder{
Log: log,
GitCommand: gitCommand,
OSCommand: osCommand,
Tr: tr,
CherryPickedCommits: cherryPickedCommits,
DiffEntries: diffEntries,
}, nil
}
@@ -96,6 +98,14 @@ func (c *CommitListBuilder) GetCommits() ([]*commands.Commit, error) {
return nil, err
}
for _, commit := range commits {
for _, entry := range c.DiffEntries {
if entry.Sha == commit.Sha {
commit.Status = "selected"
}
}
}
return commits, nil
}
@@ -113,7 +123,7 @@ func (c *CommitListBuilder) getRebasingCommits(rebaseMode string) ([]*commands.C
func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, error) {
rewrittenCount := 0
bytesContent, err := ioutil.ReadFile(".git/rebase-apply/rewritten")
bytesContent, err := ioutil.ReadFile(fmt.Sprintf("%s/rebase-apply/rewritten", c.GitCommand.DotGitDir))
if err == nil {
content := string(bytesContent)
rewrittenCount = len(strings.Split(content, "\n"))
@@ -121,7 +131,7 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, erro
// we know we're rebasing, so lets get all the files whose names have numbers
commits := []*commands.Commit{}
err = filepath.Walk(".git/rebase-apply", func(path string, f os.FileInfo, err error) error {
err = filepath.Walk(fmt.Sprintf("%s/rebase-apply", c.GitCommand.DotGitDir), func(path string, f os.FileInfo, err error) error {
if rewrittenCount > 0 {
rewrittenCount--
return nil
@@ -165,9 +175,9 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, erro
// and extracts out the sha and names of commits that we still have to go
// in the rebase:
func (c *CommitListBuilder) getInteractiveRebasingCommits() ([]*commands.Commit, error) {
bytesContent, err := ioutil.ReadFile(".git/rebase-merge/git-rebase-todo")
bytesContent, err := ioutil.ReadFile(fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.GitCommand.DotGitDir))
if err != nil {
c.Log.Info(fmt.Sprintf("error occured reading git-rebase-todo: %s", err.Error()))
c.Log.Info(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
// we assume an error means the file doesn't exist so we just return
return nil, nil
}
@@ -251,10 +261,8 @@ func (c *CommitListBuilder) getMergeBase() (string, error) {
baseBranch = "develop"
}
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git merge-base HEAD %s", baseBranch))
if err != nil {
// swallowing error because it's not a big deal; probably because there are no commits yet
}
// swallowing error because it's not a big deal; probably because there are no commits yet
output, _ := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git merge-base HEAD %s", baseBranch))
return output, nil
}

View File

@@ -34,7 +34,7 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo"))
}
branch := gui.getSelectedBranch()
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, v); err != nil {
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches), v); err != nil {
return err
}
go func() {
@@ -121,15 +121,7 @@ func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
}
branch := gui.getSelectedBranch()
if err := gui.GitCommand.Checkout(branch.Name, false); err != nil {
if err := gui.createErrorPanel(g, err.Error()); err != nil {
return err
}
} else {
gui.State.Panels.Branches.SelectedLine = 0
}
return gui.refreshSidePanels(g)
return gui.handleCheckoutBranch(branch.Name)
}
func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error {
@@ -166,12 +158,45 @@ func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
}, nil)
}
func (gui *Gui) handleCheckoutBranch(branchName string) error {
if err := gui.GitCommand.Checkout(branchName, 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
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(), 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") + branchName); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.GitCommand.Checkout(branchName, 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
}
}
gui.State.Panels.Branches.SelectedLine = 0
return gui.refreshSidePanels(gui.g)
}
func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error {
gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.Checkout(gui.trimmedContent(v), false); err != nil {
return gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(g)
return gui.handleCheckoutBranch(gui.trimmedContent(v))
})
return nil
}

View File

@@ -0,0 +1,106 @@
package gui
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
)
func (gui *Gui) getSelectedCommitFile(g *gocui.Gui) *commands.CommitFile {
selectedLine := gui.State.Panels.CommitFiles.SelectedLine
if selectedLine == -1 {
return nil
}
return gui.State.CommitFiles[selectedLine]
}
func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error {
commitFile := gui.getSelectedCommitFile(g)
if commitFile == nil {
return gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
}
if err := gui.focusPoint(0, gui.State.Panels.CommitFiles.SelectedLine, len(gui.State.CommitFiles), v); err != nil {
return err
}
commitText, err := gui.GitCommand.ShowCommitFile(commitFile.Sha, commitFile.Name)
if err != nil {
return err
}
return gui.renderString(g, "main", commitText)
}
func (gui *Gui) handleCommitFilesNextLine(g *gocui.Gui, v *gocui.View) error {
panelState := gui.State.Panels.CommitFiles
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.CommitFiles), false)
return gui.handleCommitFileSelect(gui.g, v)
}
func (gui *Gui) handleCommitFilesPrevLine(g *gocui.Gui, v *gocui.View) error {
panelState := gui.State.Panels.CommitFiles
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.CommitFiles), true)
return gui.handleCommitFileSelect(gui.g, v)
}
func (gui *Gui) handleSwitchToCommitsPanel(g *gocui.Gui, v *gocui.View) error {
commitsView, err := g.View("commits")
if err != nil {
return err
}
return gui.switchFocus(g, v, commitsView)
}
func (gui *Gui) handleCheckoutCommitFile(g *gocui.Gui, v *gocui.View) error {
file := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLine]
if err := gui.GitCommand.CheckoutFile(file.Sha, file.Name); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
return gui.refreshFiles()
}
func (gui *Gui) handleDiscardOldFileChange(g *gocui.Gui, v *gocui.View) error {
fileName := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLine].Name
return gui.createConfirmationPanel(gui.g, v, gui.Tr.SLocalize("DiscardFileChangesTitle"), gui.Tr.SLocalize("DiscardFileChangesPrompt"), func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
if err := gui.GitCommand.DiscardOldFileChanges(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, fileName); err != nil {
if err := gui.handleGenericMergeCommandResult(err); err != nil {
return err
}
}
return gui.refreshSidePanels(gui.g)
})
}, nil)
}
func (gui *Gui) refreshCommitFilesView() error {
commit := gui.getSelectedCommit(gui.g)
if commit == nil {
return nil
}
files, err := gui.GitCommand.GetCommitFiles(commit.Sha)
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
gui.State.CommitFiles = files
gui.refreshSelectedLine(&gui.State.Panels.CommitFiles.SelectedLine, len(gui.State.CommitFiles))
if err := gui.renderListPanel(gui.getCommitFilesView(), gui.State.CommitFiles); err != nil {
return err
}
return gui.handleCommitFileSelect(gui.g, gui.getCommitFilesView())
}
func (gui *Gui) handleOpenOldCommitFile(g *gocui.Gui, v *gocui.View) error {
file := gui.getSelectedCommitFile(g)
return gui.openFile(file.Name)
}

View File

@@ -11,17 +11,18 @@ import (
// runSyncOrAsyncCommand takes the output of a command that may have returned
// either no error, an error, or a subprocess to execute, and if a subprocess
// needs to be set on the gui object, it does so, and then returns the error
func (gui *Gui) runSyncOrAsyncCommand(sub *exec.Cmd, err error) error {
// the bool returned tells us whether the calling code should continue
func (gui *Gui) runSyncOrAsyncCommand(sub *exec.Cmd, err error) (bool, error) {
if err != nil {
if err != gui.Errors.ErrSubProcess {
return gui.createErrorPanel(gui.g, err.Error())
return false, gui.createErrorPanel(gui.g, err.Error())
}
}
if sub != nil {
gui.SubProcess = sub
return gui.Errors.ErrSubProcess
return false, gui.Errors.ErrSubProcess
}
return nil
return true, nil
}
func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
@@ -29,9 +30,18 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
if message == "" {
return gui.createErrorPanel(g, gui.Tr.SLocalize("CommitWithoutMessageErr"))
}
if err := gui.runSyncOrAsyncCommand(gui.GitCommand.Commit(message)); err != nil {
flags := ""
skipHookPrefix := gui.Config.GetUserConfig().GetString("git.skipHookPrefix")
if skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix) {
flags = "--no-verify"
}
ok, err := gui.runSyncOrAsyncCommand(gui.GitCommand.Commit(message, flags))
if err != nil {
return err
}
if !ok {
return nil
}
v.Clear()
_ = v.SetCursor(0, 0)
@@ -61,33 +71,6 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error {
return gui.renderString(g, "options", message)
}
func (gui *Gui) simpleEditor(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
switch {
case key == gocui.KeyBackspace || key == gocui.KeyBackspace2:
v.EditDelete(true)
case key == gocui.KeyDelete:
v.EditDelete(false)
case key == gocui.KeyArrowDown:
v.MoveCursor(0, 1, false)
case key == gocui.KeyArrowUp:
v.MoveCursor(0, -1, false)
case key == gocui.KeyArrowLeft:
v.MoveCursor(-1, 0, false)
case key == gocui.KeyArrowRight:
v.MoveCursor(1, 0, false)
case key == gocui.KeyTab:
v.EditNewLine()
case key == gocui.KeySpace:
v.EditWrite(' ')
case key == gocui.KeyInsert:
v.Overwrite = !v.Overwrite
default:
v.EditWrite(ch)
}
gui.RenderCommitLength()
}
func (gui *Gui) getBufferLength(view *gocui.View) string {
return " " + strconv.Itoa(strings.Count(view.Buffer(), "")-1) + " "
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strconv"
"github.com/fatih/color"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
@@ -36,9 +37,15 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
}
if err := gui.focusPoint(0, gui.State.Panels.Commits.SelectedLine, v); err != nil {
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 {
return nil
}
commitText, err := gui.GitCommand.Show(commit.Sha)
if err != nil {
return err
@@ -48,7 +55,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 {
builder, err := git.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr, gui.State.CherryPickedCommits)
builder, err := git.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr, gui.State.CherryPickedCommits, gui.State.DiffEntries)
if err != nil {
return err
}
@@ -71,9 +78,12 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
fmt.Fprint(v, list)
gui.refreshStatus(g)
if v == g.CurrentView() {
if g.CurrentView() == v {
gui.handleCommitSelect(g, v)
}
if g.CurrentView() == gui.getCommitFilesView() {
return gui.refreshCommitFilesView()
}
return nil
})
return nil
@@ -115,7 +125,8 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error
if commit == nil {
panic(errors.New(gui.Tr.SLocalize("NoCommitsThisBranch")))
}
if err := gui.GitCommand.ResetToCommit(commit.Sha); err != nil {
if err := gui.GitCommand.ResetToCommit(commit.Sha, "mixed"); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshCommits(g); err != nil {
@@ -437,3 +448,158 @@ func (gui *Gui) HandlePasteCommits(g *gocui.Gui, v *gocui.View) error {
})
}, nil)
}
func (gui *Gui) handleSwitchToCommitFilesPanel(g *gocui.Gui, v *gocui.View) error {
if err := gui.refreshCommitFilesView(); err != nil {
return err
}
return gui.switchFocus(g, v, gui.getCommitFilesView())
}
func (gui *Gui) handleToggleDiffCommit(g *gocui.Gui, v *gocui.View) error {
selectLimit := 2
// get selected commit
commit := gui.getSelectedCommit(g)
if commit == nil {
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
}
// if already selected commit delete
if idx, has := gui.hasCommit(gui.State.DiffEntries, commit.Sha); has {
gui.State.DiffEntries = gui.unchooseCommit(gui.State.DiffEntries, idx)
} else {
if len(gui.State.DiffEntries) == selectLimit {
gui.State.DiffEntries = gui.unchooseCommit(gui.State.DiffEntries, 0)
}
gui.State.DiffEntries = append(gui.State.DiffEntries, commit)
}
gui.setDiffMode()
// if selected two commits, display diff between
if len(gui.State.DiffEntries) == selectLimit {
commitText, err := gui.GitCommand.DiffCommits(gui.State.DiffEntries[0].Sha, gui.State.DiffEntries[1].Sha)
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
return gui.renderString(g, "main", commitText)
}
return nil
}
func (gui *Gui) setDiffMode() {
v := gui.getCommitsView()
if len(gui.State.DiffEntries) != 0 {
gui.State.Panels.Commits.SpecificDiffMode = true
v.Title = gui.Tr.SLocalize("CommitsDiffTitle")
} else {
gui.State.Panels.Commits.SpecificDiffMode = false
v.Title = gui.Tr.SLocalize("CommitsTitle")
}
gui.refreshCommits(gui.g)
}
func (gui *Gui) hasCommit(commits []*commands.Commit, target string) (int, bool) {
for idx, commit := range commits {
if commit.Sha == target {
return idx, true
}
}
return -1, false
}
func (gui *Gui) unchooseCommit(commits []*commands.Commit, i int) []*commands.Commit {
return append(commits[:i], commits[i+1:]...)
}
func (gui *Gui) handleCreateFixupCommit(g *gocui.Gui, v *gocui.View) error {
commit := gui.getSelectedCommit(g)
if commit == nil {
return nil
}
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("CreateFixupCommit"), gui.Tr.TemplateLocalize(
"SureCreateFixupCommit",
Teml{
"commit": commit.Sha,
},
), func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.CreateFixupCommit(commit.Sha); err != nil {
return gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(gui.g)
}, nil)
}
func (gui *Gui) handleSquashAllAboveFixupCommits(g *gocui.Gui, v *gocui.View) error {
commit := gui.getSelectedCommit(g)
if commit == nil {
return nil
}
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("SquashAboveCommits"), gui.Tr.TemplateLocalize(
"SureSquashAboveCommits",
Teml{
"commit": commit.Sha,
},
), func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error {
err := gui.GitCommand.SquashAllAboveFixupCommits(commit.Sha)
return gui.handleGenericMergeCommandResult(err)
})
}, nil)
}
type resetOption struct {
description string
command string
}
// GetDisplayStrings is a function.
func (r *resetOption) GetDisplayStrings(isFocused bool) []string {
return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
}
func (gui *Gui) handleCreateCommitResetMenu(g *gocui.Gui, v *gocui.View) error {
commit := gui.getSelectedCommit(g)
if commit == nil {
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("NoCommitsThisBranch"))
}
strengths := []string{"soft", "mixed", "hard"}
options := make([]*resetOption, len(strengths))
for i, strength := range strengths {
options[i] = &resetOption{
description: fmt.Sprintf("%s reset", strength),
command: fmt.Sprintf("reset --%s %s", strength, commit.Sha),
}
}
handleMenuPress := func(index int) error {
if err := gui.GitCommand.ResetToCommit(commit.Sha, strengths[index]); err != nil {
return err
}
if err := gui.refreshCommits(g); err != nil {
return err
}
if err := gui.refreshFiles(); err != nil {
return err
}
if err := gui.resetOrigin(gui.getCommitsView()); err != nil {
return err
}
gui.State.Panels.Commits.SelectedLine = 0
return gui.handleCommitSelect(g, gui.getCommitsView())
}
return gui.createMenu(fmt.Sprintf("%s %s", gui.Tr.SLocalize("resetTo"), commit.Sha), options, len(options), handleMenuPress)
}

View File

@@ -21,7 +21,11 @@ func (gui *Gui) contextTitleMap() map[string]map[string]string {
}
func (gui *Gui) setMainTitle() error {
currentViewName := gui.g.CurrentView().Name()
currentView := gui.g.CurrentView()
if currentView == nil {
return nil
}
currentViewName := currentView.Name()
var newTitle string
if context, ok := gui.State.Contexts[currentViewName]; ok {
newTitle = gui.contextTitleMap()[currentViewName][context]

View File

@@ -10,6 +10,7 @@ import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
@@ -63,7 +64,7 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View, alreadySelected bo
return gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
}
if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, v); err != nil {
if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, len(gui.State.Files), v); err != nil {
return err
}
@@ -85,6 +86,10 @@ func (gui *Gui) refreshFiles() error {
selectedFile, _ := gui.getSelectedFile(gui.g)
filesView := gui.getFilesView()
if filesView == nil {
// if the filesView hasn't been instantiated yet we just return
return nil
}
if err := gui.refreshStateFiles(); err != nil {
return err
}
@@ -259,35 +264,6 @@ func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error {
return gui.Errors.ErrSubProcess
}
func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g)
if err != nil {
if err == gui.Errors.ErrNoFiles {
return nil
}
return err
}
var deleteVerb string
if file.Tracked {
deleteVerb = gui.Tr.SLocalize("checkout")
} else {
deleteVerb = gui.Tr.SLocalize("delete")
}
message := gui.Tr.TemplateLocalize(
"SureTo",
Teml{
"deleteVerb": deleteVerb,
"fileName": file.Name,
},
)
return gui.createConfirmationPanel(g, v, strings.Title(deleteVerb)+" file", message, func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.RemoveFile(file); err != nil {
return err
}
return gui.refreshFiles()
}, nil)
}
func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g)
if err != nil {
@@ -302,6 +278,22 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
return gui.refreshFiles()
}
func (gui *Gui) handleWIPCommitPress(g *gocui.Gui, filesView *gocui.View) error {
skipHookPreifx := gui.Config.GetUserConfig().GetString("git.skipHookPrefix")
if skipHookPreifx == "" {
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("SkipHookPrefixNotConfigured"))
}
if err := gui.renderString(g, "commitMessage", skipHookPreifx); err != nil {
return err
}
if err := gui.getCommitMessageView().SetCursor(len(skipHookPreifx), 0); err != nil {
return err
}
return gui.handleCommitPress(g, filesView)
}
func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
if len(gui.stagedFiles()) == 0 && gui.State.WorkingTreeState == "normal" {
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit"))
@@ -328,9 +320,13 @@ func (gui *Gui) handleAmendCommitPress(g *gocui.Gui, filesView *gocui.View) erro
question := gui.Tr.SLocalize("SureToAmend")
return gui.createConfirmationPanel(g, filesView, title, question, func(g *gocui.Gui, v *gocui.View) error {
if err := gui.runSyncOrAsyncCommand(gui.GitCommand.AmendHead()); err != nil {
ok, err := gui.runSyncOrAsyncCommand(gui.GitCommand.AmendHead())
if err != nil {
return err
}
if !ok {
return nil
}
return gui.refreshSidePanels(g)
}, nil)
@@ -355,13 +351,14 @@ func (gui *Gui) PrepareSubProcess(g *gocui.Gui, commands ...string) {
}
func (gui *Gui) editFile(filename string) error {
return gui.runSyncOrAsyncCommand(gui.OSCommand.EditFile(filename))
_, err := gui.runSyncOrAsyncCommand(gui.OSCommand.EditFile(filename))
return err
}
func (gui *Gui) handleFileEdit(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g)
if err != nil {
return err
return gui.createErrorPanel(gui.g, err.Error())
}
return gui.editFile(file.Name)
@@ -370,7 +367,7 @@ func (gui *Gui) handleFileEdit(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g)
if err != nil {
return err
return gui.createErrorPanel(gui.g, err.Error())
}
return gui.openFile(file.Name)
}
@@ -454,7 +451,7 @@ func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g)
if err != nil {
if err != gui.Errors.ErrNoFiles {
return err
return gui.createErrorPanel(gui.g, err.Error())
}
return nil
}
@@ -479,15 +476,6 @@ func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
return gui.refreshFiles()
}
func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("ClearFilePanel"), gui.Tr.SLocalize("SureResetHardHead"), func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.ResetAndClean(); err != nil {
gui.createErrorPanel(g, err.Error())
}
return gui.refreshFiles()
}, nil)
}
func (gui *Gui) openFile(filename string) error {
if err := gui.OSCommand.OpenFile(filename); err != nil {
return gui.createErrorPanel(gui.g, err.Error())
@@ -504,15 +492,181 @@ func (gui *Gui) anyFilesWithMergeConflicts() bool {
return false
}
func (gui *Gui) handleSoftReset(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("SoftReset"), gui.Tr.SLocalize("ConfirmSoftReset"), func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.SoftReset("HEAD^"); err != nil {
return gui.createErrorPanel(g, err.Error())
}
type discardOption struct {
handler func(fileName *commands.File) error
description string
}
if err := gui.refreshCommits(gui.g); err != nil {
type discardAllOption struct {
handler func() error
description string
command string
}
// GetDisplayStrings is a function.
func (r *discardOption) GetDisplayStrings(isFocused bool) []string {
return []string{r.description}
}
// GetDisplayStrings is a function.
func (r *discardAllOption) GetDisplayStrings(isFocused bool) []string {
return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
}
func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g)
if err != nil {
if err != gui.Errors.ErrNoFiles {
return err
}
return nil
}
options := []*discardOption{
{
description: gui.Tr.SLocalize("discardAllChanges"),
handler: func(file *commands.File) error {
return gui.GitCommand.DiscardAllFileChanges(file)
},
},
{
description: gui.Tr.SLocalize("cancel"),
handler: func(file *commands.File) error {
return nil
},
},
}
if file.HasStagedChanges && file.HasUnstagedChanges {
discardUnstagedChanges := &discardOption{
description: gui.Tr.SLocalize("discardUnstagedChanges"),
handler: func(file *commands.File) error {
return gui.GitCommand.DiscardUnstagedFileChanges(file)
},
}
options = append(options[:1], append([]*discardOption{discardUnstagedChanges}, options[1:]...)...)
}
handleMenuPress := func(index int) error {
file, err := gui.getSelectedFile(g)
if err != nil {
return err
}
if err := options[index].handler(file); err != nil {
return err
}
return gui.refreshFiles()
}, nil)
}
return gui.createMenu(file.Name, options, len(options), handleMenuPress)
}
func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
options := []*discardAllOption{
{
description: gui.Tr.SLocalize("discardAllChangesToAllFiles"),
command: "reset --hard HEAD && git clean -fd",
handler: func() error {
return gui.GitCommand.ResetAndClean()
},
},
{
description: gui.Tr.SLocalize("discardAnyUnstagedChanges"),
command: "git checkout -- .",
handler: func() error {
return gui.GitCommand.DiscardAnyUnstagedFileChanges()
},
},
{
description: gui.Tr.SLocalize("discardUntrackedFiles"),
command: "git clean -fd",
handler: func() error {
return gui.GitCommand.RemoveUntrackedFiles()
},
},
{
description: gui.Tr.SLocalize("softReset"),
command: "git reset --soft HEAD",
handler: func() error {
return gui.GitCommand.ResetSoftHead()
},
},
{
description: gui.Tr.SLocalize("hardReset"),
command: "git reset --hard HEAD",
handler: func() error {
return gui.GitCommand.ResetHardHead()
},
},
{
description: gui.Tr.SLocalize("cancel"),
handler: func() error {
return nil
},
},
}
handleMenuPress := func(index int) error {
if err := options[index].handler(); err != nil {
return err
}
return gui.refreshFiles()
}
return gui.createMenu("", options, len(options), handleMenuPress)
}
func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error {
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("CustomCommand"), func(g *gocui.Gui, v *gocui.View) error {
command := gui.trimmedContent(v)
gui.SubProcess = gui.OSCommand.RunCustomCommand(command)
return gui.Errors.ErrSubProcess
})
}
type stashOption struct {
description string
handler func() error
}
// GetDisplayStrings is a function.
func (o *stashOption) GetDisplayStrings(isFocused bool) []string {
return []string{o.description}
}
func (gui *Gui) handleCreateStashMenu(g *gocui.Gui, v *gocui.View) error {
options := []*stashOption{
{
description: gui.Tr.SLocalize("stashAllChanges"),
handler: func() error {
return gui.handleStashSave(gui.GitCommand.StashSave)
},
},
{
description: gui.Tr.SLocalize("stashStagedChanges"),
handler: func() error {
return gui.handleStashSave(gui.GitCommand.StashSaveStagedChanges)
},
},
{
description: gui.Tr.SLocalize("cancel"),
handler: func() error {
return nil
},
},
}
handleMenuPress := func(index int) error {
return options[index].handler()
}
return gui.createMenu(gui.Tr.SLocalize("stashOptions"), options, len(options), handleMenuPress)
}
func (gui *Gui) handleStashChanges(g *gocui.Gui, v *gocui.View) error {
return gui.handleStashSave(gui.GitCommand.StashSave)
}

View File

@@ -1,14 +1,15 @@
package gui
import (
"fmt"
"io/ioutil"
"math"
"os"
"sync"
// "io"
// "io/ioutil"
"io/ioutil"
"os"
"os/exec"
"strings"
"time"
@@ -103,7 +104,8 @@ type branchPanelState struct {
}
type commitPanelState struct {
SelectedLine int
SelectedLine int
SpecificDiffMode bool
}
type stashPanelState struct {
@@ -114,14 +116,19 @@ type menuPanelState struct {
SelectedLine int
}
type commitFilesPanelState struct {
SelectedLine int
}
type panelStates struct {
Files *filePanelState
Branches *branchPanelState
Commits *commitPanelState
Stash *stashPanelState
Menu *menuPanelState
Staging *stagingPanelState
Merging *mergingPanelState
Files *filePanelState
Branches *branchPanelState
Commits *commitPanelState
Stash *stashPanelState
Menu *menuPanelState
Staging *stagingPanelState
Merging *mergingPanelState
CommitFiles *commitFilesPanelState
}
type guiState struct {
@@ -129,6 +136,9 @@ type guiState struct {
Branches []*commands.Branch
Commits []*commands.Commit
StashEntries []*commands.StashEntry
CommitFiles []*commands.CommitFile
DiffEntries []*commands.Commit
MenuItemCount int // can't store the actual list because it's of interface{} type
PreviousView string
Platform commands.Platform
Updating bool
@@ -147,13 +157,15 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
Commits: make([]*commands.Commit, 0),
CherryPickedCommits: make([]*commands.Commit, 0),
StashEntries: make([]*commands.StashEntry, 0),
DiffEntries: make([]*commands.Commit, 0),
Platform: *oSCommand.Platform,
Panels: &panelStates{
Files: &filePanelState{SelectedLine: -1},
Branches: &branchPanelState{SelectedLine: 0},
Commits: &commitPanelState{SelectedLine: -1},
Stash: &stashPanelState{SelectedLine: -1},
Menu: &menuPanelState{SelectedLine: 0},
Files: &filePanelState{SelectedLine: -1},
Branches: &branchPanelState{SelectedLine: 0},
Commits: &commitPanelState{SelectedLine: -1},
CommitFiles: &commitFilesPanelState{SelectedLine: -1},
Stash: &stashPanelState{SelectedLine: -1},
Menu: &menuPanelState{SelectedLine: 0},
Merging: &mergingPanelState{
ConflictIndex: 0,
ConflictTop: true,
@@ -213,20 +225,21 @@ func max(a, b int) int {
// getFocusLayout returns a manager function for when view gain and lose focus
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
var focusedView *gocui.View
var previousView *gocui.View
return func(g *gocui.Gui) error {
v := gui.g.CurrentView()
if v != focusedView {
if err := gui.onFocusChange(); err != nil {
newView := gui.g.CurrentView()
if err := gui.onFocusChange(); err != nil {
return err
}
// for now we don't consider losing focus to a popup panel as actually losing focus
if newView != previousView && !gui.isPopupPanel(newView.Name()) {
if err := gui.onFocusLost(previousView, newView); err != nil {
return err
}
if err := gui.onFocusLost(focusedView); err != nil {
if err := gui.onFocus(newView); err != nil {
return err
}
if err := gui.onFocus(v); err != nil {
return err
}
focusedView = v
previousView = newView
}
return nil
}
@@ -240,21 +253,24 @@ func (gui *Gui) onFocusChange() error {
return gui.setMainTitle()
}
func (gui *Gui) onFocusLost(v *gocui.View) error {
func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
if v == nil {
return nil
}
if v.Name() == "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
}
} else if v.Name() == "main" {
// if we have lost focus to a popup panel, that's okay
if gui.popupPanelFocused() {
return nil
// if we have lost focus to a first-class panel, we need to do some cleanup
if err := gui.changeContext("main", "normal"); err != nil {
return err
}
if err := gui.changeContext("main", "normal"); err != nil {
} else if v.Name() == "commitFiles" {
if _, err := gui.g.SetViewOnBottom(v.Name()); err != nil {
return err
}
}
@@ -274,20 +290,75 @@ func (gui *Gui) onFocus(v *gocui.View) error {
func (gui *Gui) layout(g *gocui.Gui) error {
g.Highlight = true
width, height := g.Size()
information := gui.Config.GetVersion()
if gui.g.Mouse {
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.SLocalize("Donate"))
information = donate + " " + information
}
leftSideWidth := width / 3
statusFilesBoundary := 2
filesBranchesBoundary := 2 * height / 5
commitsBranchesBoundary := 3 * height / 5
optionsTop := height - 2
commitsStashBoundary := optionsTop - 3
optionsVersionBoundary := width - max(len(utils.Decolorise(information)), 1)
minimumHeight := 18
minimumHeight := 9
minimumWidth := 10
if height < minimumHeight || width < minimumWidth {
v, err := g.SetView("limit", 0, 0, width-1, height-1, 0)
if err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
v.Wrap = true
_, _ = g.SetViewOnTop("limit")
}
return nil
}
currView := gui.g.CurrentView()
currentCyclebleView := gui.State.PreviousView
if currView != nil {
viewName := currView.Name()
usePreviouseView := true
for _, view := range cyclableViews {
if view == viewName {
currentCyclebleView = viewName
usePreviouseView = false
break
}
}
if usePreviouseView {
currentCyclebleView = gui.State.PreviousView
}
}
usableSpace := height - 7
extraSpace := usableSpace - (usableSpace/3)*3
vHeights := map[string]int{
"status": 3,
"files": (usableSpace / 3) + extraSpace,
"branches": usableSpace / 3,
"commits": usableSpace / 3,
"stash": 3,
"options": 1,
}
if height < 28 {
defaultHeight := 3
if height < 21 {
defaultHeight = 1
}
vHeights = map[string]int{
"status": defaultHeight,
"files": defaultHeight,
"branches": defaultHeight,
"commits": defaultHeight,
"stash": defaultHeight,
"options": defaultHeight,
}
vHeights[currentCyclebleView] = height - defaultHeight*4 - 1
}
optionsVersionBoundary := width - max(len(utils.Decolorise(information)), 1)
leftSideWidth := width / 3
appStatus := gui.statusManager.getStatusString()
appStatusOptionsBoundary := 0
@@ -300,22 +371,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
panelSpacing = 0
}
if height < minimumHeight || width < minimumWidth {
v, err := g.SetView("limit", 0, 0, max(width-1, 2), max(height-1, 2), 0)
if err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
v.Wrap = true
g.SetViewOnTop("limit")
}
return nil
}
_, _ = g.SetViewOnBottom("limit")
g.DeleteView("limit")
v, err := g.SetView("main", leftSideWidth+panelSpacing, 0, width-1, optionsTop, gocui.LEFT)
v, err := g.SetView("main", leftSideWidth+panelSpacing, 0, width-1, height-2, gocui.LEFT)
if err != nil {
if err.Error() != "unknown view" {
return err
@@ -325,7 +384,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
v.FgColor = gocui.ColorWhite
}
if v, err := g.SetView("status", 0, 0, leftSideWidth, statusFilesBoundary, gocui.BOTTOM|gocui.RIGHT); err != nil {
if v, err := g.SetView("status", 0, 0, leftSideWidth, vHeights["status"]-1, gocui.BOTTOM|gocui.RIGHT); err != nil {
if err.Error() != "unknown view" {
return err
}
@@ -333,7 +392,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
v.FgColor = gocui.ColorWhite
}
filesView, err := g.SetView("files", 0, statusFilesBoundary+panelSpacing, leftSideWidth, filesBranchesBoundary, gocui.TOP|gocui.BOTTOM)
filesView, err := g.SetViewBeneath("files", "status", vHeights["files"])
if err != nil {
if err.Error() != "unknown view" {
return err
@@ -343,7 +402,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
v.FgColor = gocui.ColorWhite
}
branchesView, err := g.SetView("branches", 0, filesBranchesBoundary+panelSpacing, leftSideWidth, commitsBranchesBoundary, gocui.TOP|gocui.BOTTOM)
branchesView, err := g.SetViewBeneath("branches", "files", vHeights["branches"])
if err != nil {
if err.Error() != "unknown view" {
return err
@@ -352,7 +411,15 @@ func (gui *Gui) layout(g *gocui.Gui) error {
branchesView.FgColor = gocui.ColorWhite
}
commitsView, err := g.SetView("commits", 0, commitsBranchesBoundary+panelSpacing, leftSideWidth, commitsStashBoundary, gocui.TOP|gocui.BOTTOM)
if v, err := g.SetViewBeneath("commitFiles", "branches", vHeights["commits"]); err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("CommitFiles")
v.FgColor = gocui.ColorWhite
}
commitsView, err := g.SetViewBeneath("commits", "branches", vHeights["commits"])
if err != nil {
if err.Error() != "unknown view" {
return err
@@ -361,7 +428,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
commitsView.FgColor = gocui.ColorWhite
}
stashView, err := g.SetView("stash", 0, commitsStashBoundary+panelSpacing, leftSideWidth, optionsTop, gocui.TOP|gocui.RIGHT)
stashView, err := g.SetViewBeneath("stash", "commits", vHeights["stash"])
if err != nil {
if err.Error() != "unknown view" {
return err
@@ -370,7 +437,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
stashView.FgColor = gocui.ColorWhite
}
if v, err := g.SetView("options", appStatusOptionsBoundary-1, optionsTop, optionsVersionBoundary-1, optionsTop+2, 0); err != nil {
if v, err := g.SetView("options", appStatusOptionsBoundary-1, height-2, optionsVersionBoundary-1, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
@@ -382,7 +449,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
if gui.getCommitMessageView() == nil {
// doesn't matter where this view starts because it will be hidden
if commitMessageView, err := g.SetView("commitMessage", 0, 0, width/2, height/2, 0); err != nil {
if commitMessageView, err := g.SetView("commitMessage", width, height, width*2, height*2, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
@@ -390,13 +457,12 @@ func (gui *Gui) layout(g *gocui.Gui) error {
commitMessageView.Title = gui.Tr.SLocalize("CommitMessage")
commitMessageView.FgColor = gocui.ColorWhite
commitMessageView.Editable = true
commitMessageView.Editor = gocui.EditorFunc(gui.simpleEditor)
}
}
if check, _ := g.View("credentials"); check == nil {
// doesn't matter where this view starts because it will be hidden
if credentialsView, err := g.SetView("credentials", 0, 0, width/2, height/2, 0); err != nil {
if credentialsView, err := g.SetView("credentials", width, height, width*2, height*2, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
@@ -407,11 +473,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername")
credentialsView.FgColor = gocui.ColorWhite
credentialsView.Editable = true
credentialsView.Editor = gocui.EditorFunc(gui.simpleEditor)
}
}
if appStatusView, err := g.SetView("appStatus", -1, optionsTop, width, optionsTop+2, 0); err != nil {
if appStatusView, err := g.SetView("appStatus", -1, height-2, width, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
@@ -423,7 +488,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
}
if v, err := g.SetView("information", optionsVersionBoundary-1, optionsTop, width, optionsTop+2, 0); err != nil {
if v, err := g.SetView("information", optionsVersionBoundary-1, height-2, width, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
@@ -434,47 +499,41 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return err
}
// these are only called once (it's a place to put all the things you want
// to happen on startup after the screen is first rendered)
gui.Updater.CheckForNewUpdate(gui.onBackgroundUpdateCheckFinish, false)
if err := gui.updateRecentRepoList(); err != nil {
// doing this here because it'll only happen once
if err := gui.loadNewRepo(); err != nil {
return err
}
gui.waitForIntro.Done()
if _, err := gui.g.SetCurrentView(filesView.Name()); err != nil {
return err
}
if err := gui.refreshSidePanels(gui.g); err != nil {
return err
}
if err := gui.switchFocus(g, nil, filesView); err != nil {
return err
}
if gui.Config.GetUserConfig().GetString("reporting") == "undetermined" {
if err := gui.promptAnonymousReporting(); err != nil {
return err
}
}
}
listViews := map[*gocui.View]int{
filesView: gui.State.Panels.Files.SelectedLine,
branchesView: gui.State.Panels.Branches.SelectedLine,
commitsView: gui.State.Panels.Commits.SelectedLine,
stashView: gui.State.Panels.Stash.SelectedLine,
if gui.g.CurrentView() == nil {
if _, err := gui.g.SetCurrentView(gui.getFilesView().Name()); err != nil {
return err
}
if err := gui.switchFocus(gui.g, nil, gui.getFilesView()); err != nil {
return err
}
}
type listViewState struct {
selectedLine int
lineCount int
}
listViews := map[*gocui.View]listViewState{
filesView: {selectedLine: gui.State.Panels.Files.SelectedLine, lineCount: len(gui.State.Files)},
branchesView: {selectedLine: gui.State.Panels.Branches.SelectedLine, lineCount: len(gui.State.Branches)},
commitsView: {selectedLine: gui.State.Panels.Commits.SelectedLine, lineCount: len(gui.State.Commits)},
stashView: {selectedLine: gui.State.Panels.Stash.SelectedLine, lineCount: len(gui.State.StashEntries)},
}
// menu view might not exist so we check to be safe
if menuView, err := gui.g.View("menu"); err == nil {
listViews[menuView] = gui.State.Panels.Menu.SelectedLine
listViews[menuView] = listViewState{selectedLine: gui.State.Panels.Menu.SelectedLine, lineCount: gui.State.MenuItemCount}
}
for view, selectedLine := range listViews {
for view, state := range listViews {
// check if the selected line is now out of view and if so refocus it
if err := gui.focusPoint(0, selectedLine, view); err != nil {
if err := gui.focusPoint(0, state.selectedLine, state.lineCount, view); err != nil {
return err
}
}
@@ -486,6 +545,25 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return gui.resizeCurrentPopupPanel(g)
}
func (gui *Gui) loadNewRepo() error {
gui.Updater.CheckForNewUpdate(gui.onBackgroundUpdateCheckFinish, false)
if err := gui.updateRecentRepoList(); err != nil {
return err
}
gui.waitForIntro.Done()
if err := gui.refreshSidePanels(gui.g); err != nil {
return err
}
if gui.Config.GetUserConfig().GetString("reporting") == "undetermined" {
if err := gui.promptAnonymousReporting(); err != nil {
return err
}
}
return nil
}
func (gui *Gui) promptAnonymousReporting() error {
return gui.createConfirmationPanel(gui.g, nil, gui.Tr.SLocalize("AnonymousReportingTitle"), gui.Tr.SLocalize("AnonymousReportingPrompt"), func(g *gocui.Gui, v *gocui.View) error {
gui.waitForIntro.Done()
@@ -541,6 +619,23 @@ func (gui *Gui) goEvery(interval time.Duration, function func() error) {
}()
}
func (gui *Gui) startBackgroundFetch() {
gui.waitForIntro.Wait()
isNew := gui.Config.GetIsNewRepo()
if !isNew {
time.After(60 * time.Second)
}
_, err := gui.fetch(gui.g, gui.g.CurrentView(), false)
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
_ = gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil)
} else {
gui.goEvery(time.Second*60, func() error {
_, err := gui.fetch(gui.g, gui.g.CurrentView(), false)
return err
})
}
}
// Run setup the gui with keybindings and start the mainloop
func (gui *Gui) Run() error {
g, err := gocui.NewGui(gocui.OutputNormal, OverlappingEdges)
@@ -565,22 +660,9 @@ func (gui *Gui) Run() error {
gui.waitForIntro.Add(1)
}
go func() {
gui.waitForIntro.Wait()
isNew := gui.Config.GetIsNewRepo()
if !isNew {
time.After(60 * time.Second)
}
_, err := gui.fetch(g, g.CurrentView(), false)
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
_ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil)
} else {
gui.goEvery(time.Second*60, func() error {
_, err := gui.fetch(gui.g, gui.g.CurrentView(), false)
return err
})
}
}()
if gui.Config.GetUserConfig().GetBool("git.autoFetch") {
go gui.startBackgroundFetch()
}
gui.goEvery(time.Second*10, gui.refreshFiles)
gui.goEvery(time.Millisecond*50, gui.renderAppStatus)
@@ -605,14 +687,9 @@ func (gui *Gui) RunWithSubprocesses() error {
} else if err == gui.Errors.ErrSwitchRepo {
continue
} else if err == gui.Errors.ErrSubProcess {
gui.SubProcess.Stdin = os.Stdin
gui.SubProcess.Stdout = os.Stdout
gui.SubProcess.Stderr = os.Stderr
gui.SubProcess.Run()
gui.SubProcess.Stdout = ioutil.Discard
gui.SubProcess.Stderr = ioutil.Discard
gui.SubProcess.Stdin = nil
gui.SubProcess = nil
if err := gui.runCommand(); err != nil {
return err
}
} else {
return err
}
@@ -621,6 +698,30 @@ func (gui *Gui) RunWithSubprocesses() error {
return nil
}
func (gui *Gui) runCommand() error {
gui.SubProcess.Stdout = os.Stdout
gui.SubProcess.Stderr = os.Stdout
gui.SubProcess.Stdin = os.Stdin
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(gui.SubProcess.Args, " "), color.FgBlue))
if err := gui.SubProcess.Run(); err != nil {
// not handling the error explicitly because usually we're going to see it
// in the output anyway
gui.Log.Error(err)
}
gui.SubProcess.Stdout = ioutil.Discard
gui.SubProcess.Stderr = ioutil.Discard
gui.SubProcess.Stdin = nil
gui.SubProcess = nil
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.SLocalize("pressEnterToReturn"), color.FgGreen))
fmt.Scanln() // wait for enter press
return nil
}
func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error {
if gui.State.Updating {
return gui.createUpdateQuitConfirmation(g, v)

View File

@@ -13,6 +13,7 @@ type Binding struct {
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
Modifier gocui.Modifier
Description string
Alternative string
}
// GetDisplayStrings returns the display string of a file
@@ -28,6 +29,12 @@ func (b *Binding) GetKey() string {
case rune:
key = int(b.Key.(rune))
case gocui.Key:
if b.Key.(gocui.Key) == gocui.KeyCtrlJ {
return "ctrl+j"
}
if b.Key.(gocui.Key) == gocui.KeyCtrlK {
return "ctrl+k"
}
key = int(b.Key.(gocui.Key))
}
@@ -74,14 +81,26 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Key: gocui.KeyEsc,
Modifier: gocui.ModNone,
Handler: gui.quit,
}, {
ViewName: "",
Key: gocui.KeyPgup,
Modifier: gocui.ModNone,
Handler: gui.scrollUpMain,
Alternative: "fn+up",
}, {
ViewName: "",
Key: gocui.KeyPgdn,
Modifier: gocui.ModNone,
Handler: gui.scrollDownMain,
Alternative: "fn+down",
}, {
ViewName: "",
Key: gocui.KeyPgup,
Key: 'K',
Modifier: gocui.ModNone,
Handler: gui.scrollUpMain,
}, {
ViewName: "",
Key: gocui.KeyPgdn,
Key: 'J',
Modifier: gocui.ModNone,
Handler: gui.scrollDownMain,
}, {
@@ -154,6 +173,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleCommitPress,
Description: gui.Tr.SLocalize("CommitChanges"),
},
{
ViewName: "files",
Key: 'w',
Modifier: gocui.ModNone,
Handler: gui.handleWIPCommitPress,
Description: gui.Tr.SLocalize("commitChangesWithoutHook"),
}, {
ViewName: "files",
Key: 'A',
@@ -176,8 +202,8 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "files",
Key: 'd',
Modifier: gocui.ModNone,
Handler: gui.handleFileRemove,
Description: gui.Tr.SLocalize("removeFile"),
Handler: gui.handleCreateDiscardMenu,
Description: gui.Tr.SLocalize("viewDiscardOptions"),
}, {
ViewName: "files",
Key: 'e',
@@ -202,18 +228,18 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleRefreshFiles,
Description: gui.Tr.SLocalize("refreshFiles"),
}, {
ViewName: "files",
Key: 'S',
Modifier: gocui.ModNone,
Handler: gui.handleStashSave,
Description: gui.Tr.SLocalize("stashFiles"),
}, {
ViewName: "files",
Key: 's',
Modifier: gocui.ModNone,
Handler: gui.handleSoftReset,
Description: gui.Tr.SLocalize("softReset"),
Handler: gui.handleStashChanges,
Description: gui.Tr.SLocalize("stashAllChanges"),
}, {
ViewName: "files",
Key: 'S',
Modifier: gocui.ModNone,
Handler: gui.handleCreateStashMenu,
Description: gui.Tr.SLocalize("viewStashOptions"),
}, {
ViewName: "files",
Key: 'a',
@@ -230,8 +256,8 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "files",
Key: 'D',
Modifier: gocui.ModNone,
Handler: gui.handleResetAndClean,
Description: gui.Tr.SLocalize("resetHard"),
Handler: gui.handleCreateResetMenu,
Description: gui.Tr.SLocalize("viewResetOptions"),
}, {
ViewName: "files",
Key: gocui.KeyEnter,
@@ -244,6 +270,12 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleGitFetch,
Description: gui.Tr.SLocalize("fetch"),
}, {
ViewName: "files",
Key: 'X',
Modifier: gocui.ModNone,
Handler: gui.handleCustomCommand,
Description: gui.Tr.SLocalize("executeCustomCommand"),
}, {
ViewName: "branches",
Key: gocui.KeySpace,
@@ -320,7 +352,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "commits",
Key: 'g',
Modifier: gocui.ModNone,
Handler: gui.handleResetToCommit,
Handler: gui.handleCreateCommitResetMenu,
Description: gui.Tr.SLocalize("resetToThisCommit"),
}, {
ViewName: "commits",
@@ -328,6 +360,18 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleCommitFixup,
Description: gui.Tr.SLocalize("fixupCommit"),
}, {
ViewName: "commits",
Key: 'F',
Modifier: gocui.ModNone,
Handler: gui.handleCreateFixupCommit,
Description: gui.Tr.SLocalize("createFixupCommit"),
}, {
ViewName: "commits",
Key: 'S',
Modifier: gocui.ModNone,
Handler: gui.handleSquashAllAboveFixupCommits,
Description: gui.Tr.SLocalize("squashAboveCommits"),
}, {
ViewName: "commits",
Key: 'd',
@@ -336,13 +380,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.SLocalize("deleteCommit"),
}, {
ViewName: "commits",
Key: 'J',
Key: gocui.KeyCtrlJ,
Modifier: gocui.ModNone,
Handler: gui.handleCommitMoveDown,
Description: gui.Tr.SLocalize("moveDownCommit"),
}, {
ViewName: "commits",
Key: 'K',
Key: gocui.KeyCtrlK,
Modifier: gocui.ModNone,
Handler: gui.handleCommitMoveUp,
Description: gui.Tr.SLocalize("moveUpCommit"),
@@ -388,6 +432,18 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.HandlePasteCommits,
Description: gui.Tr.SLocalize("pasteCommits"),
}, {
ViewName: "commits",
Key: gocui.KeyEnter,
Modifier: gocui.ModNone,
Handler: gui.handleSwitchToCommitFilesPanel,
Description: gui.Tr.SLocalize("viewCommitFiles"),
}, {
ViewName: "commits",
Key: gocui.KeySpace,
Modifier: gocui.ModNone,
Handler: gui.handleToggleDiffCommit,
Description: gui.Tr.SLocalize("CommitsDiff"),
}, {
ViewName: "stash",
Key: gocui.KeySpace,
@@ -441,10 +497,35 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Key: gocui.MouseLeft,
Modifier: gocui.ModNone,
Handler: gui.handleDonate,
}, {
ViewName: "commitFiles",
Key: gocui.KeyEsc,
Modifier: gocui.ModNone,
Handler: gui.handleSwitchToCommitsPanel,
Description: gui.Tr.SLocalize("goBack"),
}, {
ViewName: "commitFiles",
Key: 'c',
Modifier: gocui.ModNone,
Handler: gui.handleCheckoutCommitFile,
Description: gui.Tr.SLocalize("checkoutCommitFile"),
}, {
ViewName: "commitFiles",
Key: 'd',
Modifier: gocui.ModNone,
Handler: gui.handleDiscardOldFileChange,
Description: gui.Tr.SLocalize("discardOldFileChange"),
},
{
ViewName: "commitFiles",
Key: 'o',
Modifier: gocui.ModNone,
Handler: gui.handleOpenOldCommitFile,
Description: gui.Tr.SLocalize("openFile"),
},
}
for _, viewName := range []string{"status", "branches", "files", "commits", "stash", "menu"} {
for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} {
bindings = append(bindings, []*Binding{
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
@@ -459,12 +540,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
nextLine func(*gocui.Gui, *gocui.View) error
focus func(*gocui.Gui, *gocui.View) error
}{
"menu": {prevLine: gui.handleMenuPrevLine, nextLine: gui.handleMenuNextLine, focus: gui.handleMenuSelect},
"files": {prevLine: gui.handleFilesPrevLine, nextLine: gui.handleFilesNextLine, focus: gui.handleFilesFocus},
"branches": {prevLine: gui.handleBranchesPrevLine, nextLine: gui.handleBranchesNextLine, focus: gui.handleBranchSelect},
"commits": {prevLine: gui.handleCommitsPrevLine, nextLine: gui.handleCommitsNextLine, focus: gui.handleCommitSelect},
"stash": {prevLine: gui.handleStashPrevLine, nextLine: gui.handleStashNextLine, focus: gui.handleStashEntrySelect},
"status": {focus: gui.handleStatusSelect},
"menu": {prevLine: gui.handleMenuPrevLine, nextLine: gui.handleMenuNextLine, focus: gui.handleMenuSelect},
"files": {prevLine: gui.handleFilesPrevLine, nextLine: gui.handleFilesNextLine, focus: gui.handleFilesFocus},
"branches": {prevLine: gui.handleBranchesPrevLine, nextLine: gui.handleBranchesNextLine, focus: gui.handleBranchSelect},
"commits": {prevLine: gui.handleCommitsPrevLine, nextLine: gui.handleCommitsNextLine, focus: gui.handleCommitSelect},
"stash": {prevLine: gui.handleStashPrevLine, nextLine: gui.handleStashNextLine, focus: gui.handleStashEntrySelect},
"status": {focus: gui.handleStatusSelect},
"commitFiles": {prevLine: gui.handleCommitFilesPrevLine, nextLine: gui.handleCommitFilesNextLine, focus: gui.handleCommitFileSelect},
}
for viewName, functions := range listPanelMap {
@@ -516,12 +598,14 @@ func (gui *Gui) GetContextMap() map[string]map[string][]*Binding {
Modifier: gocui.ModNone,
Handler: gui.scrollDownMain,
Description: gui.Tr.SLocalize("ScrollDown"),
Alternative: "fn+up",
}, {
ViewName: "main",
Key: gocui.MouseWheelUp,
Modifier: gocui.ModNone,
Handler: gui.scrollUpMain,
Description: gui.Tr.SLocalize("ScrollUp"),
Alternative: "fn+down",
},
},
"staging": {

View File

@@ -10,7 +10,7 @@ import (
// list panel functions
func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error {
return gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, v)
return gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, gui.State.MenuItemCount, v)
}
func (gui *Gui) handleMenuNextLine(g *gocui.Gui, v *gocui.View) error {
@@ -51,8 +51,9 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
return gui.returnFocus(g, v)
}
func (gui *Gui) createMenu(title string, items interface{}, handlePress func(int) error) error {
func (gui *Gui) createMenu(title string, items interface{}, itemCount int, handlePress func(int) error) error {
isFocused := gui.g.CurrentView().Name() == "menu"
gui.State.MenuItemCount = itemCount
list, err := utils.RenderList(items, isFocused)
if err != nil {
return err
@@ -81,6 +82,8 @@ func (gui *Gui) createMenu(title string, items interface{}, handlePress func(int
}
for _, key := range []gocui.Key{gocui.KeySpace, gocui.KeyEnter} {
_ = gui.g.DeleteKeybinding("menu", key, gocui.ModNone)
if err := gui.g.SetKeybinding("menu", key, gocui.ModNone, wrappedHandlePress); err != nil {
return err
}

View File

@@ -49,5 +49,5 @@ func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error {
return bindings[index].Handler(g, v)
}
return gui.createMenu(strings.Title(gui.Tr.SLocalize("menu")), bindings, handleMenuPress)
return gui.createMenu(strings.Title(gui.Tr.SLocalize("menu")), bindings, len(bindings), handleMenuPress)
}

View File

@@ -38,7 +38,7 @@ func (gui *Gui) handleCreateRebaseOptionsMenu(g *gocui.Gui, v *gocui.View) error
title = gui.Tr.SLocalize("RebaseOptionsTitle")
}
return gui.createMenu(title, options, handleMenuPress)
return gui.createMenu(title, options, len(options), handleMenuPress)
}
func (gui *Gui) genericMergeCommand(command string) error {

View File

@@ -44,7 +44,7 @@ func (gui *Gui) handleCreateRecentReposMenu(g *gocui.Gui, v *gocui.View) error {
return gui.Errors.ErrSwitchRepo
}
return gui.createMenu(gui.Tr.SLocalize("RecentRepos"), recentRepos, handleMenuPress)
return gui.createMenu(gui.Tr.SLocalize("RecentRepos"), recentRepos, len(recentRepos), handleMenuPress)
}
// updateRecentRepoList registers the fact that we opened lazygit in this repo,

View File

@@ -31,7 +31,7 @@ func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
if stashEntry == nil {
return gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries"))
}
if err := gui.focusPoint(0, gui.State.Panels.Stash.SelectedLine, v); err != nil {
if err := gui.focusPoint(0, gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries), v); err != nil {
return err
}
go func() {
@@ -130,16 +130,15 @@ func (gui *Gui) stashDo(g *gocui.Gui, v *gocui.View, method string) error {
return gui.refreshFiles()
}
func (gui *Gui) handleStashSave(g *gocui.Gui, filesView *gocui.View) error {
func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
if len(gui.trackedFiles()) == 0 && len(gui.stagedFiles()) == 0 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoTrackedStagedFilesStash"))
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("NoTrackedStagedFilesStash"))
}
gui.createPromptPanel(g, filesView, gui.Tr.SLocalize("StashChanges"), func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.StashSave(gui.trimmedContent(v)); err != nil {
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 {
gui.createErrorPanel(g, err.Error())
}
gui.refreshStashEntries(g)
return gui.refreshFiles()
})
return nil
}

View File

@@ -61,7 +61,7 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
[]string{
lazygitTitle(),
"Copyright (c) 2018 Jesse Duffield",
"Keybindings: https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md",
"Keybindings: https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings",
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
"Tutorial: https://youtu.be/VDXvbHZYeKY",
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues",

View File

@@ -31,7 +31,7 @@ func (gui *Gui) GetAttribute(key string) gocui.Attribute {
func (gui *Gui) GetColor(keys []string) gocui.Attribute {
var attribute gocui.Attribute
for _, key := range keys {
attribute = attribute | gui.GetAttribute(key)
attribute |= gui.GetAttribute(key)
}
return attribute
}

View File

@@ -22,6 +22,7 @@ func (gui *Gui) refreshSidePanels(g *gocui.Gui) error {
if err := gui.refreshCommits(g); err != nil {
return err
}
return gui.refreshStashEntries(g)
}
@@ -30,8 +31,13 @@ func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
if v == nil || v.Name() == cyclableViews[len(cyclableViews)-1] {
focusedViewName = cyclableViews[0]
} else {
// if we're in the commitFiles view we'll act like we're in the commits view
viewName := v.Name()
if viewName == "commitFiles" {
viewName = "commits"
}
for i := range cyclableViews {
if v.Name() == cyclableViews[i] {
if viewName == cyclableViews[i] {
focusedViewName = cyclableViews[i+1]
break
}
@@ -39,7 +45,7 @@ func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
message := gui.Tr.TemplateLocalize(
"IssntListOfViews",
Teml{
"name": v.Name(),
"name": viewName,
},
)
gui.Log.Info(message)
@@ -59,8 +65,13 @@ func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error {
if v == nil || v.Name() == cyclableViews[0] {
focusedViewName = cyclableViews[len(cyclableViews)-1]
} else {
// if we're in the commitFiles view we'll act like we're in the commits view
viewName := v.Name()
if viewName == "commitFiles" {
viewName = "commits"
}
for i := range cyclableViews {
if v.Name() == cyclableViews[i] {
if viewName == cyclableViews[i] {
focusedViewName = cyclableViews[i-1] // TODO: make this work properly
break
}
@@ -68,7 +79,7 @@ func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error {
message := gui.Tr.TemplateLocalize(
"IssntListOfViews",
Teml{
"name": v.Name(),
"name": viewName,
},
)
gui.Log.Info(message)
@@ -95,6 +106,8 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
return gui.handleBranchSelect(g, v)
case "commits":
return gui.handleCommitSelect(g, v)
case "commitFiles":
return gui.handleCommitFileSelect(g, v)
case "stash":
return gui.handleStashEntrySelect(g, v)
case "confirmation":
@@ -129,15 +142,10 @@ func (gui *Gui) returnFocus(g *gocui.Gui, v *gocui.View) error {
// pass in oldView = nil if you don't want to be able to return to your old view
// TODO: move some of this logic into our onFocusLost and onFocus hooks
func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
// we assume we'll never want to return focus to a confirmation panel i.e.
// we should never stack confirmation panels
if oldView != nil && oldView.Name() != "confirmation" {
// second class panels should never have focus restored to them because
// once they lose focus they are effectively 'destroyed'
secondClassPanels := []string{"confirmation", "menu"}
if !utils.IncludesString(secondClassPanels, oldView.Name()) {
gui.State.PreviousView = oldView.Name()
}
// we assume we'll never want to return focus to a popup panel i.e.
// we should never stack popup panels
if oldView != nil && !gui.isPopupPanel(oldView.Name()) {
gui.State.PreviousView = oldView.Name()
}
gui.Log.Info("setting highlight to true for view" + newView.Name())
@@ -165,49 +173,37 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
}
func (gui *Gui) resetOrigin(v *gocui.View) error {
if err := v.SetCursor(0, 0); err != nil {
return err
}
_ = v.SetCursor(0, 0)
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, v *gocui.View) error {
if cy < 0 {
func (gui *Gui) focusPoint(cx int, cy int, lineCount int, v *gocui.View) error {
if cy < 0 || cy > lineCount {
return nil
}
ox, oy := v.Origin()
_, height := v.Size()
ly := height - 1
if ly == -1 {
ly = 0
}
// if line is above origin, move origin and set cursor to zero
// if line is below origin + height, move origin and set cursor to max
// otherwise set cursor to value - origin
if ly > v.LinesHeight() {
if err := v.SetCursor(cx, cy); err != nil {
return err
}
if err := v.SetOrigin(ox, 0); err != nil {
return err
}
if ly > lineCount {
_ = v.SetCursor(cx, cy)
_ = v.SetOrigin(ox, 0)
} else if cy < oy {
if err := v.SetCursor(cx, 0); err != nil {
return err
}
if err := v.SetOrigin(ox, cy); err != nil {
return err
}
_ = v.SetCursor(cx, 0)
_ = v.SetOrigin(ox, cy)
} else if cy > oy+ly {
if err := v.SetCursor(cx, ly); err != nil {
return err
}
if err := v.SetOrigin(ox, cy-ly); err != nil {
return err
}
_ = v.SetCursor(cx, ly)
_ = v.SetOrigin(ox, cy-ly)
} else {
if err := v.SetCursor(cx, cy-oy); err != nil {
return err
}
_ = v.SetCursor(cx, cy-oy)
}
return nil
}
@@ -283,6 +279,11 @@ func (gui *Gui) getStashView() *gocui.View {
return v
}
func (gui *Gui) getCommitFilesView() *gocui.View {
v, _ := gui.g.View("commitFiles")
return v
}
func (gui *Gui) trimmedContent(v *gocui.View) string {
return strings.TrimSpace(v.Buffer())
}
@@ -294,7 +295,7 @@ func (gui *Gui) currentViewName() string {
func (gui *Gui) resizeCurrentPopupPanel(g *gocui.Gui) error {
v := g.CurrentView()
if v.Name() == "commitMessage" || v.Name() == "credentials" || v.Name() == "confirmation" {
if gui.isPopupPanel(v.Name()) {
return gui.resizePopupPanel(g, v)
}
return nil
@@ -386,14 +387,10 @@ func (gui *Gui) handleFocusView(g *gocui.Gui, v *gocui.View) error {
return err
}
func (gui *Gui) popupPanelFocused() bool {
viewNames := []string{"commitMessage",
"credentials",
"menu"}
for _, viewName := range viewNames {
if gui.currentViewName() == viewName {
return true
}
}
return false
func (gui *Gui) isPopupPanel(viewName string) bool {
return viewName == "commitMessage" || viewName == "credentials" || viewName == "confirmation" || viewName == "menu"
}
func (gui *Gui) popupPanelFocused() bool {
return gui.isPopupPanel(gui.currentViewName())
}

View File

@@ -28,6 +28,12 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CommitsTitle",
Other: "Commits",
}, &i18n.Message{
ID: "CommitsDiffTitle",
Other: "Commits (specific diff mode)",
}, &i18n.Message{
ID: "CommitsDiff",
Other: "select commit to diff with another commit",
}, &i18n.Message{
ID: "StashTitle",
Other: "Stash",
@@ -79,9 +85,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "execute",
Other: "uitvoeren",
}, &i18n.Message{
ID: "stashFiles",
Other: "stash-bestanden",
}, &i18n.Message{
ID: "open",
Other: "open",
@@ -157,9 +160,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "FileNoMergeCons",
Other: "Dit bestand heeft geen merge conflicten",
}, &i18n.Message{
ID: "SureResetHardHead",
Other: "Weet je het zeker dat je `reset --hard HEAD` en `clean -fd` wil uitvoeren? Het kan dat je hierdoor bestanden verliest",
}, &i18n.Message{
ID: "SureTo",
Other: "Weet je het zeker dat je {{.fileName}} wilt {{.deleteVerb}} (je veranderingen zullen worden verwijderd)",
@@ -340,9 +340,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CantCloseConfirmationPrompt",
Other: "Kon de bevestiging prompt niet sluiten: {{.error}}",
}, &i18n.Message{
ID: "ClearFilePanel",
Other: "maak bestandsvenster leeg",
}, &i18n.Message{
ID: "MergeAborted",
Other: "Merge afgebroken",
@@ -382,9 +379,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "GitconfigParseErr",
Other: `Gogit kon je gitconfig bestand niet goed parsen door de aanwezigheid van losstaande '\' tekens. Het weghalen van deze tekens zou het probleem moeten oplossen. `,
}, &i18n.Message{
ID: "removeFile",
Other: `Verwijder als untracked / uitchecken wordt gevolgd (ga weg)`,
}, &i18n.Message{
ID: "editFile",
Other: `verander bestand`,
@@ -397,9 +391,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "refreshFiles",
Other: `refresh bestanden`,
}, &i18n.Message{
ID: "resetHard",
Other: `harde reset and verwijderen ongevolgde bestanden`,
}, &i18n.Message{
ID: "mergeIntoCurrentBranch",
Other: `merge in met huidige checked out branch`,
@@ -423,7 +414,7 @@ func addDutch(i18nObject *i18n.Bundle) error {
Other: `fetch`,
}, &i18n.Message{
ID: "NoAutomaticGitFetchTitle",
Other: `Geen automatiese git fetch`,
Other: `Geen automatische git fetch`,
}, &i18n.Message{
ID: "NoAutomaticGitFetchBody",
Other: `Lazygit kan niet "git fetch" uitvoeren in een privé repository, gebruik f in het branches paneel om "git fetch" manueel uit te voeren`,
@@ -459,40 +450,34 @@ func addDutch(i18nObject *i18n.Bundle) error {
Other: "Merging",
}, &i18n.Message{
ID: "ConfirmRebase",
Other: "Are you sure you want to rebase {{.checkedOutBranch}} onto {{.selectedBranch}}?",
Other: "Weet je zeker dat je {{.checkedOutBranch}} op {{.selectedBranch}} wil rebasen?",
}, &i18n.Message{
ID: "ConfirmMerge",
Other: "Are you sure you want to merge {{.selectedBranch}} into {{.checkedOutBranch}}?",
Other: "Weet je zeker dat je {{.selectedBranch}} in {{.checkedOutBranch}} wil mergen?",
}, &i18n.Message{
ID: "FwdNoUpstream",
Other: "Cannot fast-forward a branch with no upstream",
Other: "Kan niet de branch vooruitspoelen zonder upstream",
}, &i18n.Message{
ID: "ErrorOccurred",
Other: "An error occurred! Please create an issue at https://github.com/jesseduffield/lazygit/issues",
Other: "Er is iets fout gegaan! Zou je hier een issue aan willen maken: https://github.com/jesseduffield/lazygit/issues",
}, &i18n.Message{
ID: "FwdCommitsToPush",
Other: "Cannot fast-forward a branch with commits to push",
Other: "Je kan niet vooruitspoelen als de branch geen nieuwe commits heeft",
}, &i18n.Message{
ID: "MainTitle",
Other: "Main",
Other: "Hoofd",
}, &i18n.Message{
ID: "NormalTitle",
Other: "Normal",
Other: "Normaal",
}, &i18n.Message{
ID: "softReset",
Other: "soft reset to last commit",
}, &i18n.Message{
ID: "SoftReset",
Other: "Soft reset",
}, &i18n.Message{
ID: "ConfirmSoftReset",
Other: "Are you sure you want to `reset --soft HEAD^`? The changes in your topmost commit will be placed in your working tree",
Other: "zacht reset",
}, &i18n.Message{
ID: "CantRebaseOntoSelf",
Other: "You cannot rebase a branch onto itself",
Other: "Je kan niet een branch rebasen op zichzelf",
}, &i18n.Message{
ID: "SureSquashThisCommit",
Other: "Are you sure you want to squash this commit into the commit below?",
Other: "Weet je zeker dat je deze commit wil samenvoegen met de commit hieronder?",
}, &i18n.Message{
ID: "Squash",
Other: "Squash",
@@ -501,127 +486,130 @@ func addDutch(i18nObject *i18n.Bundle) error {
Other: "pick commit (when mid-rebase)",
}, &i18n.Message{
ID: "revertCommit",
Other: "revert commit",
Other: "commit omgedaan maken",
}, &i18n.Message{
ID: "deleteCommit",
Other: "delete commit",
Other: "verwijder commit",
}, &i18n.Message{
ID: "moveDownCommit",
Other: "move commit down one",
Other: "verplaats commit 1 omlaag",
}, &i18n.Message{
ID: "moveUpCommit",
Other: "move commit up one",
Other: "verplaats commit 1 omhoog",
}, &i18n.Message{
ID: "editCommit",
Other: "edit commit",
Other: "verander commit",
}, &i18n.Message{
ID: "amendToCommit",
Other: "amend commit with staged changes",
Other: "wijzig commit met staged veranderingen",
}, &i18n.Message{
ID: "FoundConflicts",
Other: "Damn, conflicts! To abort press 'esc', otherwise press 'enter'",
Other: "Conflicten!, Om af te breken druk 'esc', anders druk op 'enter'",
}, &i18n.Message{
ID: "FoundConflictsTitle",
Other: "Auto-merge failed",
Other: "Auto-merge mislukt",
}, &i18n.Message{
ID: "Undo",
Other: "undo",
Other: "ongedaan maken",
}, &i18n.Message{
ID: "PickHunk",
Other: "pick hunk",
}, &i18n.Message{
ID: "PickBothHunks",
Other: "pick both hunks",
Other: "pick beide hunks",
}, &i18n.Message{
ID: "ViewMergeRebaseOptions",
Other: "view merge/rebase options",
Other: "bekijk merge/rebase opties",
}, &i18n.Message{
ID: "NotMergingOrRebasing",
Other: "You are currently neither rebasing nor merging",
Other: "Je bent momenteel niet aan het rebasen of mergen",
}, &i18n.Message{
ID: "RecentRepos",
Other: "recent repositories",
}, &i18n.Message{
ID: "MergeOptionsTitle",
Other: "Merge Options",
Other: "Merge Opties",
}, &i18n.Message{
ID: "RebaseOptionsTitle",
Other: "Rebase Options",
Other: "Rebase Opties",
}, &i18n.Message{
ID: "ConflictsResolved",
Other: "all merge conflicts resolved. Continue?",
Other: "alle merge conflicten zijn opgelost. Wilt je verder gaan?",
}, &i18n.Message{
ID: "NoRoom",
Other: "Not enough room",
Other: "Niet genoeg ruimte",
}, &i18n.Message{
ID: "YouAreHere",
Other: "YOU ARE HERE",
Other: "JE BENT HIER",
}, &i18n.Message{
ID: "rewordNotSupported",
Other: "rewording commits while interactively rebasing is not currently supported",
Other: "herformatteren van commits in interactief rebasen is nog niet ondersteund",
}, &i18n.Message{
ID: "cherryPickCopy",
Other: "copy commit (cherry-pick)",
Other: "kopiëer commit (cherry-pick)",
}, &i18n.Message{
ID: "cherryPickCopyRange",
Other: "copy commit range (cherry-pick)",
Other: "kopiëer commit reeks (cherry-pick)",
}, &i18n.Message{
ID: "pasteCommits",
Other: "paste commits (cherry-pick)",
Other: "plak commits (cherry-pick)",
}, &i18n.Message{
ID: "SureCherryPick",
Other: "Are you sure you want to cherry-pick the copied commits onto this branch?",
Other: "Weet je zeker dat je de gekopieerde commits naar deze branch wil cherry-picken?",
}, &i18n.Message{
ID: "CherryPick",
Other: "Cherry-Pick",
}, &i18n.Message{
ID: "CannotRebaseOntoFirstCommit",
Other: "You cannot interactive rebase onto the first commit",
Other: "Je kan niet interactief rebasen naar de eerste commit",
}, &i18n.Message{
ID: "CannotSquashOntoSecondCommit",
Other: "Je kan niet een squash/fixup doen naar de 2de commit",
}, &i18n.Message{
ID: "Donate",
Other: "Donate",
Other: "Doneer",
}, &i18n.Message{
ID: "PrevLine",
Other: "select previous line",
Other: "selecteer de vorige lijn",
}, &i18n.Message{
ID: "NextLine",
Other: "select next line",
Other: "selecteer de volgende lijn",
}, &i18n.Message{
ID: "PrevHunk",
Other: "select previous hunk",
Other: "selecteer de vorige hunk",
}, &i18n.Message{
ID: "NextHunk",
Other: "select next hunk",
Other: "selecteer de volgende hunk",
}, &i18n.Message{
ID: "PrevConflict",
Other: "select previous conflict",
Other: "selecteer voorgaand conflict",
}, &i18n.Message{
ID: "NextConflict",
Other: "select next conflict",
Other: "selecteer volgende conflict",
}, &i18n.Message{
ID: "SelectTop",
Other: "select top hunk",
Other: "selecteer bovenste hunk",
}, &i18n.Message{
ID: "SelectBottom",
Other: "select bottom hunk",
Other: "selecteer onderste hunk",
}, &i18n.Message{
ID: "ScrollDown",
Other: "scroll down",
Other: "scroll omlaag",
}, &i18n.Message{
ID: "ScrollUp",
Other: "scroll up",
Other: "scroll omhoog",
}, &i18n.Message{
ID: "AmendCommitTitle",
Other: "Amend Commit",
Other: "Commit wijzigen",
}, &i18n.Message{
ID: "AmendCommitPrompt",
Other: "Are you sure you want to amend this commit with your staged files?",
Other: "Weet je zeker dat je deze commit wil wijzigen met de vorige staged bestanden?",
}, &i18n.Message{
ID: "DeleteCommitTitle",
Other: "Delete Commit",
Other: "Verwijder Commit",
}, &i18n.Message{
ID: "DeleteCommitPrompt",
Other: "Are you sure you want to delete this commit?",
Other: "Weet je zeker dat je deze commit wil verwijderen?",
}, &i18n.Message{
ID: "SquashingStatus",
Other: "squashing",
@@ -630,19 +618,139 @@ func addDutch(i18nObject *i18n.Bundle) error {
Other: "fixing up",
}, &i18n.Message{
ID: "DeletingStatus",
Other: "deleting",
Other: "verwijderen",
}, &i18n.Message{
ID: "MovingStatus",
Other: "moving",
Other: "verplaatsen",
}, &i18n.Message{
ID: "RebasingStatus",
Other: "rebasing",
}, &i18n.Message{
ID: "AmendingStatus",
Other: "amending",
Other: "wijzigen",
}, &i18n.Message{
ID: "CherryPickingStatus",
Other: "cherry-picking",
}, &i18n.Message{
ID: "CommitFiles",
Other: "Commit bestanden",
}, &i18n.Message{
ID: "viewCommitFiles",
Other: "bekijk gecommite bestanden",
}, &i18n.Message{
ID: "CommitFilesTitle",
Other: "Commit bestanden",
}, &i18n.Message{
ID: "goBack",
Other: "ga terug",
}, &i18n.Message{
ID: "NoCommiteFiles",
Other: "Geen bestanden voor deze commit",
}, &i18n.Message{
ID: "checkoutCommitFile",
Other: "bestand uitchecken",
}, &i18n.Message{
ID: "discardOldFileChange",
Other: "uitsluit deze commit zijn veranderingen aan dit bestand",
}, &i18n.Message{
ID: "DiscardFileChangesTitle",
Other: "uitsluit bestand zijn veranderingen",
}, &i18n.Message{
ID: "DiscardFileChangesPrompt",
Other: "Weet je zeker dat je de wijzigingen van deze commit in dit bestand wilt weggooien? Als dit bestand is gecreëerd in deze commit dan zal dit bestand worden verwijdert",
}, &i18n.Message{
ID: "DisabledForGPG",
Other: "Onderdelen niet beschikbaar voor gebruikers die GPG gebruiken",
}, &i18n.Message{
ID: "CreateRepo",
Other: "Niet in een git repository. Creëer een nieuwe git repository? (y/n): ",
}, &i18n.Message{
ID: "AutoStashTitle",
Other: "Autostash?",
}, &i18n.Message{
ID: "AutoStashPrompt",
Other: "Je moet je veranderingen stashen en poppen om ze over te bregen. Dit automatisch doen? (enter/esc)",
}, &i18n.Message{
ID: "StashPrefix",
Other: "Auto-stashing veranderingen voor ",
}, &i18n.Message{
ID: "viewDiscardOptions",
Other: "bekijk 'veranderingen ongedaan maken' opties",
}, &i18n.Message{
ID: "cancel",
Other: "anuleren",
}, &i18n.Message{
ID: "discardAllChanges",
Other: "negeer alle wijzigingen",
}, &i18n.Message{
ID: "discardUnstagedChanges",
Other: "negeer unstaged wijzigingen",
}, &i18n.Message{
ID: "discardAllChangesToAllFiles",
Other: "verwijder werkende tree",
}, &i18n.Message{
ID: "discardAnyUnstagedChanges",
Other: "discard unstaged changes",
}, &i18n.Message{
ID: "discardUntrackedFiles",
Other: "negeer niet-gevonden bestanden",
}, &i18n.Message{
ID: "viewResetOptions",
Other: `bekijk reset opties`,
}, &i18n.Message{
ID: "hardReset",
Other: "harde reset",
}, &i18n.Message{
ID: "createFixupCommit",
Other: `creëer fixup commit voor deze commit`,
}, &i18n.Message{
ID: "squashAboveCommits",
Other: `squash bovenstaande commits`,
}, &i18n.Message{
ID: "SquashAboveCommits",
Other: `Squash bovenstaande commits`,
}, &i18n.Message{
ID: "SureSquashAboveCommits",
Other: `Weet je zeker dat je alles wil squash/fixup! voor de bovenstaand commits {{.commit}}?`,
}, &i18n.Message{
ID: "CreateFixupCommit",
Other: `Creëer fixup commit`,
}, &i18n.Message{
ID: "SureCreateFixupCommit",
Other: `Weet je zeker dat je een fixup wil maken! commit voor commit {{.commit}}?`,
}, &i18n.Message{
ID: "executeCustomCommand",
Other: "voor aangepast commando uit",
}, &i18n.Message{
ID: "CustomCommand",
Other: "Aangepast commando:",
}, &i18n.Message{
ID: "commitChangesWithoutHook",
Other: "commit veranderingen zonder pre-commit hook",
}, &i18n.Message{
ID: "SkipHookPrefixNotConfigured",
Other: "Je hebt nog niet een commit bericht voorvoegsel ingesteld voor het overslaan van hooks. Set `git.skipHookPrefix = 'WIP'` in je config",
}, &i18n.Message{
ID: "resetTo",
Other: `reset to`,
}, &i18n.Message{
ID: "pressEnterToReturn",
Other: "Press enter to return to lazygit",
}, &i18n.Message{
ID: "viewStashOptions",
Other: "view stash options",
}, &i18n.Message{
ID: "stashAllChanges",
Other: "stash-bestanden",
}, &i18n.Message{
ID: "stashStagedChanges",
Other: "stash staged changes",
}, &i18n.Message{
ID: "stashOptions",
Other: "Stash options",
}, &i18n.Message{
ID: "notARepository",
Other: "Error: must be run inside a git repository",
},
)
}

View File

@@ -36,6 +36,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CommitsTitle",
Other: "Commits",
}, &i18n.Message{
ID: "CommitsDiffTitle",
Other: "Commits (specific diff mode)",
}, &i18n.Message{
ID: "CommitsDiff",
Other: "select commit to diff with another commit",
}, &i18n.Message{
ID: "StashTitle",
Other: "Stash",
@@ -99,12 +105,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "execute",
Other: "execute",
}, &i18n.Message{
ID: "stashFiles",
Other: "stash files",
}, &i18n.Message{
ID: "softReset",
Other: "soft reset to last commit",
}, &i18n.Message{
ID: "open",
Other: "open",
@@ -181,14 +181,8 @@ func addEnglish(i18nObject *i18n.Bundle) error {
ID: "FileNoMergeCons",
Other: "This file has no inline merge conflicts",
}, &i18n.Message{
ID: "SureResetHardHead",
Other: "Are you sure you want to `reset --hard HEAD` and `clean -fd`? You may lose changes",
}, &i18n.Message{
ID: "SoftReset",
Other: "Soft reset",
}, &i18n.Message{
ID: "ConfirmSoftReset",
Other: "Are you sure you want to `reset --soft HEAD^`? The changes in your topmost commit will be placed in your working tree",
ID: "softReset",
Other: "soft reset",
}, &i18n.Message{
ID: "SureTo",
Other: "Are you sure you want to {{.deleteVerb}} {{.fileName}} (you will lose your changes)?",
@@ -405,9 +399,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "NoChangedFiles",
Other: "No changed files",
}, &i18n.Message{
ID: "ClearFilePanel",
Other: "Clear file panel",
}, &i18n.Message{
ID: "MergeAborted",
Other: "Merge aborted",
@@ -447,9 +438,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "GitconfigParseErr",
Other: `Gogit failed to parse your gitconfig file due to the presence of unquoted '\' characters. Removing these should fix the issue.`,
}, &i18n.Message{
ID: "removeFile",
Other: `delete if untracked / checkout if tracked`,
}, &i18n.Message{
ID: "editFile",
Other: `edit file`,
@@ -462,9 +450,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "refreshFiles",
Other: `refresh files`,
}, &i18n.Message{
ID: "resetHard",
Other: `reset hard and remove untracked files`,
}, &i18n.Message{
ID: "mergeIntoCurrentBranch",
Other: `merge into currently checked out branch`,
@@ -521,7 +506,7 @@ func addEnglish(i18nObject *i18n.Bundle) error {
Other: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
}, &i18n.Message{
ID: "FoundConflicts",
Other: "Damn, conflicts! To abort press 'esc', otherwise press 'enter'",
Other: "Conflicts! To abort press 'esc', otherwise press 'enter'",
}, &i18n.Message{
ID: "FoundConflictsTitle",
Other: "Auto-merge failed",
@@ -600,6 +585,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CannotRebaseOntoFirstCommit",
Other: "You cannot interactive rebase onto the first commit",
}, &i18n.Message{
ID: "CannotSquashOntoSecondCommit",
Other: "You cannot squash/fixup onto the second commit",
}, &i18n.Message{
ID: "Donate",
Other: "Donate",
@@ -666,6 +654,126 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CherryPickingStatus",
Other: "cherry-picking",
}, &i18n.Message{
ID: "CommitFiles",
Other: "Commit files",
}, &i18n.Message{
ID: "viewCommitFiles",
Other: "view commit's files",
}, &i18n.Message{
ID: "CommitFilesTitle",
Other: "Commit files",
}, &i18n.Message{
ID: "goBack",
Other: "go back",
}, &i18n.Message{
ID: "NoCommiteFiles",
Other: "No files for this commit",
}, &i18n.Message{
ID: "checkoutCommitFile",
Other: "checkout file",
}, &i18n.Message{
ID: "discardOldFileChange",
Other: "discard this commit's changes to this file",
}, &i18n.Message{
ID: "DiscardFileChangesTitle",
Other: "Discard file changes",
}, &i18n.Message{
ID: "DiscardFileChangesPrompt",
Other: "Are you sure you want to discard this commit's changes to this file? If this file was created in this commit, it will be deleted",
}, &i18n.Message{
ID: "DisabledForGPG",
Other: "Feature not available for users using GPG",
}, &i18n.Message{
ID: "CreateRepo",
Other: "Not in a git repository. Create a new git repository? (y/n): ",
}, &i18n.Message{
ID: "AutoStashTitle",
Other: "Autostash?",
}, &i18n.Message{
ID: "AutoStashPrompt",
Other: "You must stash and pop your changes to bring them across. Do this automatically? (enter/esc)",
}, &i18n.Message{
ID: "StashPrefix",
Other: "Auto-stashing changes for ",
}, &i18n.Message{
ID: "viewDiscardOptions",
Other: "view 'discard changes' options",
}, &i18n.Message{
ID: "cancel",
Other: "cancel",
}, &i18n.Message{
ID: "discardAllChanges",
Other: "discard all changes",
}, &i18n.Message{
ID: "discardUnstagedChanges",
Other: "discard unstaged changes",
}, &i18n.Message{
ID: "discardAllChangesToAllFiles",
Other: "nuke working tree",
}, &i18n.Message{
ID: "discardAnyUnstagedChanges",
Other: "discard unstaged changes",
}, &i18n.Message{
ID: "discardUntrackedFiles",
Other: "discard untracked files",
}, &i18n.Message{
ID: "hardReset",
Other: "hard reset",
}, &i18n.Message{
ID: "viewResetOptions",
Other: `view reset options`,
}, &i18n.Message{
ID: "createFixupCommit",
Other: `create fixup commit for this commit`,
}, &i18n.Message{
ID: "squashAboveCommits",
Other: `squash above commits`,
}, &i18n.Message{
ID: "SquashAboveCommits",
Other: `Squash above commits`,
}, &i18n.Message{
ID: "SureSquashAboveCommits",
Other: `Are you sure you want to squash all fixup! commits above {{.commit}}?`,
}, &i18n.Message{
ID: "CreateFixupCommit",
Other: `Create fixup commit`,
}, &i18n.Message{
ID: "SureCreateFixupCommit",
Other: `Are you sure you want to create a fixup! commit for commit {{.commit}}?`,
}, &i18n.Message{
ID: "executeCustomCommand",
Other: "execute custom command",
}, &i18n.Message{
ID: "CustomCommand",
Other: "Custom Command:",
}, &i18n.Message{
ID: "commitChangesWithoutHook",
Other: "commit changes without pre-commit hook",
}, &i18n.Message{
ID: "SkipHookPrefixNotConfigured",
Other: "You have not configured a commit message prefix for skipping hooks. Set `git.skipHookPrefix = 'WIP'` in your config",
}, &i18n.Message{
ID: "resetTo",
Other: `reset to`,
}, &i18n.Message{
ID: "pressEnterToReturn",
Other: "Press enter to return to lazygit",
}, &i18n.Message{
ID: "viewStashOptions",
Other: "view stash options",
}, &i18n.Message{
ID: "stashAllChanges",
Other: "stash changes",
}, &i18n.Message{
ID: "stashStagedChanges",
Other: "stash staged changes",
}, &i18n.Message{
ID: "stashOptions",
Other: "Stash options",
}, &i18n.Message{
ID: "notARepository",
Other: "Error: must be run inside a git repository",
},
)
}

View File

@@ -87,7 +87,7 @@ func detectLanguage(langDetector func() (string, error)) string {
// setupLocalizer creates a new localizer using given userLang
func setupLocalizer(log *logrus.Entry, userLang string) *Localizer {
// create a i18n bundle that can be used to add translations and other things
i18nBundle := &i18n.Bundle{DefaultLanguage: language.English}
i18nBundle := i18n.NewBundle(language.English)
addBundles(log, i18nBundle)

View File

@@ -26,6 +26,12 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CommitsTitle",
Other: "Commity",
}, &i18n.Message{
ID: "CommitsDiffTitle",
Other: "Commits (specific diff mode)",
}, &i18n.Message{
ID: "CommitsDiff",
Other: "select commit to diff with another commit",
}, &i18n.Message{
ID: "StashTitle",
Other: "Schowek",
@@ -77,9 +83,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "execute",
Other: "wykonaj",
}, &i18n.Message{
ID: "stashFiles",
Other: "przechowaj pliki",
}, &i18n.Message{
ID: "open",
Other: "otwórz",
@@ -146,9 +149,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "FileNoMergeCons",
Other: "Ten plik nie powoduje konfliktów scalania",
}, &i18n.Message{
ID: "SureResetHardHead",
Other: "Jesteś pewny, że chcesz wykonać `reset --hard HEAD` i `clean -fd`? Możesz stracić wprowadzone zmiany",
}, &i18n.Message{
ID: "SureTo",
Other: "Jesteś pewny, że chcesz {{.deleteVerb}} {{.fileName}} (stracisz swoje wprowadzone zmiany)?",
@@ -332,9 +332,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CantCloseConfirmationPrompt",
Other: "Nie można zamknąć monitu potwierdzenia: {{.error}}",
}, &i18n.Message{
ID: "ClearFilePanel",
Other: "Wyczyść panel plików",
}, &i18n.Message{
ID: "MergeAborted",
Other: "Scalanie anulowane",
@@ -371,9 +368,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "AnonymousReportingPrompt",
Other: "Włączyć anonimowe raportowanie błędów w celu pomocy w usprawnianiu lazygita (enter/esc)?",
}, &i18n.Message{
ID: "removeFile",
Other: `usuń jeśli nie śledzony / przełącz jeśli śledzony`,
}, &i18n.Message{
ID: "editFile",
Other: `edytuj plik`,
@@ -386,9 +380,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "refreshFiles",
Other: `odśwież pliki`,
}, &i18n.Message{
ID: "resetHard",
Other: `zresetuj twardo i usuń niepotwierdzone pliki`,
}, &i18n.Message{
ID: "mergeIntoCurrentBranch",
Other: `scal do obecnej gałęzi`,
@@ -466,13 +457,7 @@ func addPolish(i18nObject *i18n.Bundle) error {
Other: "Normal",
}, &i18n.Message{
ID: "softReset",
Other: "soft reset to last commit",
}, &i18n.Message{
ID: "SoftReset",
Other: "Soft reset",
}, &i18n.Message{
ID: "ConfirmSoftReset",
Other: "Are you sure you want to `reset --soft HEAD^`? The changes in your topmost commit will be placed in your working tree",
Other: "soft reset",
}, &i18n.Message{
ID: "SureSquashThisCommit",
Other: "Are you sure you want to squash this commit into the commit below?",
@@ -502,7 +487,7 @@ func addPolish(i18nObject *i18n.Bundle) error {
Other: "amend commit with staged changes",
}, &i18n.Message{
ID: "FoundConflicts",
Other: "Damn, conflicts! To abort press 'esc', otherwise press 'enter'",
Other: "Conflicts! To abort press 'esc', otherwise press 'enter'",
}, &i18n.Message{
ID: "FoundConflictsTitle",
Other: "Auto-merge failed",
@@ -560,6 +545,9 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CannotRebaseOntoFirstCommit",
Other: "You cannot interactive rebase onto the first commit",
}, &i18n.Message{
ID: "CannotSquashOntoSecondCommit",
Other: "You cannot squash/fixup onto the second commit",
}, &i18n.Message{
ID: "Donate",
Other: "Donate",
@@ -626,6 +614,126 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "CherryPickingStatus",
Other: "cherry-picking",
}, &i18n.Message{
ID: "CommitFiles",
Other: "Commit files",
}, &i18n.Message{
ID: "viewCommitFiles",
Other: "view commit's files",
}, &i18n.Message{
ID: "CommitFilesTitle",
Other: "Commit files",
}, &i18n.Message{
ID: "goBack",
Other: "go back",
}, &i18n.Message{
ID: "NoCommiteFiles",
Other: "No files for this commit",
}, &i18n.Message{
ID: "checkoutCommitFile",
Other: "checkout file",
}, &i18n.Message{
ID: "discardOldFileChange",
Other: "discard this commit's changes to this file",
}, &i18n.Message{
ID: "DiscardFileChangesTitle",
Other: "Discard file changes",
}, &i18n.Message{
ID: "DiscardFileChangesPrompt",
Other: "Are you sure you want to discard this commit's changes to this file? If this file was created in this commit, it will be deleted",
}, &i18n.Message{
ID: "DisabledForGPG",
Other: "Feature not available for users using GPG",
}, &i18n.Message{
ID: "CreateRepo",
Other: "Not in a git repository. Create a new git repository? (y/n): ",
}, &i18n.Message{
ID: "AutoStashTitle",
Other: "Autostash?",
}, &i18n.Message{
ID: "AutoStashPrompt",
Other: "You must stash and pop your changes to bring them across. Do this automatically? (enter/esc)",
}, &i18n.Message{
ID: "StashPrefix",
Other: "Auto-stashing changes for ",
}, &i18n.Message{
ID: "viewDiscardOptions",
Other: "view 'discard changes' options",
}, &i18n.Message{
ID: "cancel",
Other: "cancel",
}, &i18n.Message{
ID: "discardAllChanges",
Other: "discard all changes",
}, &i18n.Message{
ID: "discardUnstagedChanges",
Other: "discard unstaged changes",
}, &i18n.Message{
ID: "discardAllChangesToAllFiles",
Other: "nuke working tree",
}, &i18n.Message{
ID: "discardAnyUnstagedChanges",
Other: "discard unstaged changes",
}, &i18n.Message{
ID: "discardUntrackedFiles",
Other: "discard untracked files",
}, &i18n.Message{
ID: "hardReset",
Other: "hard reset",
}, &i18n.Message{
ID: "viewResetOptions",
Other: `view reset options`,
}, &i18n.Message{
ID: "createFixupCommit",
Other: `create fixup commit for this commit`,
}, &i18n.Message{
ID: "squashAboveCommits",
Other: `squash above commits`,
}, &i18n.Message{
ID: "SquashAboveCommits",
Other: `Squash above commits`,
}, &i18n.Message{
ID: "SureSquashAboveCommits",
Other: `Are you sure you want to squash all fixup! commits above {{.commit}}?`,
}, &i18n.Message{
ID: "CreateFixupCommit",
Other: `Create fixup commit`,
}, &i18n.Message{
ID: "SureCreateFixupCommit",
Other: `Are you sure you want to create a fixup! commit for commit {{.commit}}?`,
}, &i18n.Message{
ID: "executeCustomCommand",
Other: "execute custom command",
}, &i18n.Message{
ID: "CustomCommand",
Other: "Custom Command:",
}, &i18n.Message{
ID: "commitChangesWithoutHook",
Other: "commit changes without pre-commit hook",
}, &i18n.Message{
ID: "SkipHookPrefixNotConfigured",
Other: "You have not configured a commit message prefix for skipping hooks. Set `git.skipHookPrefix = 'WIP'` in your config",
}, &i18n.Message{
ID: "resetTo",
Other: `reset to`,
}, &i18n.Message{
ID: "pressEnterToReturn",
Other: "Press enter to return to lazygit",
}, &i18n.Message{
ID: "viewStashOptions",
Other: "view stash options",
}, &i18n.Message{
ID: "stashAllChanges",
Other: "przechowaj pliki",
}, &i18n.Message{
ID: "stashStagedChanges",
Other: "stash staged changes",
}, &i18n.Message{
ID: "stashOptions",
Other: "Stash options",
}, &i18n.Message{
ID: "notARepository",
Other: "Error: must be run inside a git repository",
},
)
}

View File

@@ -33,10 +33,11 @@ func SplitLines(multilineString string) []string {
// WithPadding pads a string as much as you want
func WithPadding(str string, padding int) string {
if padding-len(str) < 0 {
uncoloredStr := Decolorise(str)
if padding < len(uncoloredStr) {
return str
}
return str + strings.Repeat(" ", padding-len(str))
return str + strings.Repeat(" ", padding-len(uncoloredStr))
}
// ColoredString takes a string and a colour attribute and returns a colored

View File

@@ -169,7 +169,7 @@ func TestResolvePlaceholderString(t *testing.T) {
}
for _, s := range scenarios {
assert.EqualValues(t, string(s.expected), ResolvePlaceholderString(s.templateString, s.arguments))
assert.EqualValues(t, s.expected, ResolvePlaceholderString(s.templateString, s.arguments))
}
}

View File

@@ -25,16 +25,21 @@ type bindingSection struct {
}
func main() {
langs := []string{"pl", "nl", "en"}
mConfig, _ := config.NewAppConfig("", "", "", "", "", true)
mApp, _ := app.NewApp(mConfig)
lang := mApp.Tr.GetLanguage()
file, _ := os.Create("Keybindings_" + lang + ".md")
bindingSections := getBindingSections(mApp)
for _, lang := range langs {
os.Setenv("LC_ALL", lang)
mApp, _ := app.NewApp(mConfig)
file, err := os.Create(getProjectRoot() + "/docs/keybindings/Keybindings_" + lang + ".md")
if err != nil {
panic(err)
}
content := formatSections(mApp, bindingSections)
writeString(file, content)
bindingSections := getBindingSections(mApp)
content := formatSections(mApp, bindingSections)
writeString(file, content)
}
}
func writeString(file *os.File, str string) {
@@ -54,6 +59,9 @@ func formatTitle(title string) string {
}
func formatBinding(binding *gui.Binding) string {
if 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", binding.GetKey(), binding.Description)
}
@@ -91,7 +99,7 @@ func getBindingSections(mApp *app.App) []*bindingSection {
}
func addBinding(title string, bindingSections []*bindingSection, binding *gui.Binding) []*bindingSection {
if binding.Description == "" {
if binding.Description == "" && binding.Alternative == "" {
return bindingSections
}
@@ -124,3 +132,11 @@ func formatSections(mApp *app.App, bindingSections []*bindingSection) string {
return content
}
func getProjectRoot() string {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
return strings.Split(dir, "lazygit")[0] + "lazygit"
}

View File

@@ -29,7 +29,7 @@ func main() {
splitVersion := strings.Split(stringVersion, ".")
patch := splitVersion[len(splitVersion)-1]
newPatch, err := strconv.Atoi(patch)
newPatch, _ := strconv.Atoi(patch)
splitVersion[len(splitVersion)-1] = strconv.FormatInt(int64(newPatch)+1, 10)
newVersion := strings.Join(splitVersion, ".")

View File

@@ -3,6 +3,8 @@
set -e
echo "" > coverage.txt
export GOFLAGS=-mod=vendor
use_go_test=false
if command -v gotest; then
use_go_test=true
@@ -10,7 +12,7 @@ fi
for d in $( find ./* -maxdepth 10 ! -path "./vendor*" ! -path "./.git*" ! -path "./scripts*" -type d); do
if ls $d/*.go &> /dev/null; then
args="-v -race -coverprofile=profile.out -covermode=atomic $d"
args="-race -coverprofile=profile.out -covermode=atomic $d"
if [ "$use_go_test" == true ]; then
gotest $args
else

View File

@@ -133,4 +133,23 @@ echo "once upon a time there was a horse" >> file5
git add file5
git commit -m "fourth commit on master"
# git merge develop # should have a merge conflict here
# this is for the autostash feature
git checkout -b base_branch
echo "original1\noriginal2\noriginal3" > file
git add file
git commit -m "file"
git checkout -b other_branch
git checkout base_branch
echo "new1\noriginal2\noriginal3" > file
git add file
git commit -m "file changed"
git checkout other_branch
echo "new2\noriginal2\noriginal3" > file

View File

@@ -0,0 +1,12 @@
[default]
aws_access_key_id = accessKey
aws_secret_access_key = secret
aws_session_token = token
[no_token]
aws_access_key_id = accessKey
aws_secret_access_key = secret
[with_colon]
aws_access_key_id: accessKey
aws_secret_access_key: secret

44
vendor/github.com/cloudfoundry/jibber_jabber/README.md generated vendored Normal file
View File

@@ -0,0 +1,44 @@
# Jibber Jabber [![Build Status](https://travis-ci.org/cloudfoundry/jibber_jabber.svg?branch=master)](https://travis-ci.org/cloudfoundry/jibber_jabber)
Jibber Jabber is a GoLang Library that can be used to detect an operating system's current language.
### OS Support
OSX and Linux via the `LC_ALL` and `LANG` environment variables. These are standard variables that are used in ALL versions of UNIX for language detection.
Windows via [GetUserDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318136.aspx) and [GetSystemDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318122.aspx) system calls. These calls are supported in Windows Vista and up.
# Usage
Add the following line to your go `import`:
```
"github.com/cloudfoundry/jibber_jabber"
```
### DetectIETF
`DetectIETF` will return the current locale as a string. The format of the locale will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code, a DASH, then an [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
```
userLocale, err := jibber_jabber.DetectIETF()
println("Locale:", userLocale)
```
### DetectLanguage
`DetectLanguage` will return the current languge as a string. The format will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code.
```
userLanguage, err := jibber_jabber.DetectLanguage()
println("Language:", userLanguage)
```
### DetectTerritory
`DetectTerritory` will return the current locale territory as a string. The format will be the [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
```
localeTerritory, err := jibber_jabber.DetectTerritory()
println("Territory:", localeTerritory)
```
### Errors
All the Detect commands will return an error if they are unable to read the Locale from the system.
For Windows, additional error information is provided due to the nature of the system call being used.

View File

@@ -2,7 +2,7 @@ ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

View File

@@ -16,7 +16,9 @@
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
@@ -34,80 +36,49 @@ const (
ptrSize = unsafe.Sizeof((*byte)(nil))
)
var (
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
// internal reflect.Value fields. These values are valid before golang
// commit ecccf07e7f9d which changed the format. The are also valid
// after commit 82f48826c6c7 which changed the format again to mirror
// the original format. Code in the init function updates these offsets
// as necessary.
offsetPtr = uintptr(ptrSize)
offsetScalar = uintptr(0)
offsetFlag = uintptr(ptrSize * 2)
type flag uintptr
// flagKindWidth and flagKindShift indicate various bits that the
// reflect package uses internally to track kind information.
//
// flagRO indicates whether or not the value field of a reflect.Value is
// read-only.
//
// flagIndir indicates whether the value field of a reflect.Value is
// the actual data or a pointer to the data.
//
// These values are valid before golang commit 90a7c3c86944 which
// changed their positions. Code in the init function updates these
// flags as necessary.
flagKindWidth = uintptr(5)
flagKindShift = uintptr(flagKindWidth - 1)
flagRO = uintptr(1 << 0)
flagIndir = uintptr(1 << 1)
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
func init() {
// Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Versions
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
// scalar for this purpose which unfortunately came before the flag
// field, so the offset of the flag field is different for those
// versions.
//
// This code constructs a new reflect.Value from a known small integer
// and checks if the size of the reflect.Value struct indicates it has
// the scalar field. When it does, the offsets are updated accordingly.
vv := reflect.ValueOf(0xf00)
if unsafe.Sizeof(vv) == (ptrSize * 4) {
offsetScalar = ptrSize * 2
offsetFlag = ptrSize * 3
}
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Commit 90a7c3c86944 changed the flag positions such that the low
// order bits are the kind. This code extracts the kind from the flags
// field and ensures it's the correct type. When it's not, the flag
// order has been changed to the newer format, so the flags are updated
// accordingly.
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
upfv := *(*uintptr)(upf)
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
flagKindShift = 0
flagRO = 1 << 5
flagIndir = 1 << 6
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
// Commit adf9b30e5594 modified the flags to separate the
// flagRO flag into two bits which specifies whether or not the
// field is embedded. This causes flagIndir to move over a bit
// and means that flagRO is the combination of either of the
// original flagRO bit and the new bit.
//
// This code detects the change by extracting what used to be
// the indirect bit to ensure it's set. When it's not, the flag
// order has been changed to the newer format, so the flags are
// updated accordingly.
if upfv&flagIndir == 0 {
flagRO = 3 << 5
flagIndir = 1 << 7
}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
@@ -119,34 +90,56 @@ func init() {
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
indirects := 1
vt := v.Type()
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
if rvf&flagIndir != 0 {
vt = reflect.PtrTo(v.Type())
indirects++
} else if offsetScalar != 0 {
// The value is in the scalar field when it's not one of the
// reference types.
switch vt.Kind() {
case reflect.Uintptr:
case reflect.Chan:
case reflect.Func:
case reflect.Map:
case reflect.Ptr:
case reflect.UnsafePointer:
default:
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
offsetScalar)
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
pv := reflect.NewAt(vt, upv)
rv = pv
for i := 0; i < indirects; i++ {
rv = rv.Elem()
}
return rv
panic("reflect.Value read-only flag has changed semantics")
}

View File

@@ -16,7 +16,7 @@
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe
// +build js appengine safe disableunsafe !go1.4
package spew

View File

@@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.

View File

@@ -35,16 +35,16 @@ var (
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
)
// dumpState contains information about the state of a dump operation.
@@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound == true:
case nilFound:
d.w.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
d.w.Write(circularBytes)
default:

View File

@@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
// Display dereferenced value.
switch {
case nilFound == true:
case nilFound:
f.fs.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
f.fs.Write(circularShortBytes)
default:

27
vendor/github.com/fatih/color/Gopkg.lock generated vendored Normal file
View File

@@ -0,0 +1,27 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e8a50671c3cb93ea935bf210b1cd20702876b9d9226129be581ef646d1565cdc"
solver-name = "gps-cdcl"
solver-version = 1

30
vendor/github.com/fatih/color/Gopkg.toml generated vendored Normal file
View File

@@ -0,0 +1,30 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/mattn/go-colorable"
version = "0.0.9"
[[constraint]]
name = "github.com/mattn/go-isatty"
version = "0.0.3"

179
vendor/github.com/fatih/color/README.md generated vendored Normal file
View File

@@ -0,0 +1,179 @@
# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color) [![Build Status](https://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
Color lets you use colorized outputs in terms of [ANSI Escape
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
has support for Windows too! The API can be used in several ways, pick one that
suits you.
![Color](https://i.imgur.com/c1JI0lA.png)
## Install
```bash
go get github.com/fatih/color
```
Note that the `vendor` folder is here for stability. Remove the folder if you
already have the dependencies in your GOPATH.
## Examples
### Standard colors
```go
// Print with default helper functions
color.Cyan("Prints text in cyan.")
// A newline will be appended automatically
color.Blue("Prints %s in blue.", "text")
// These are using the default foreground colors
color.Red("We have red")
color.Magenta("And many others ..")
```
### Mix and reuse colors
```go
// Create a new color object
c := color.New(color.FgCyan).Add(color.Underline)
c.Println("Prints cyan text with an underline.")
// Or just add them to New()
d := color.New(color.FgCyan, color.Bold)
d.Printf("This prints bold cyan %s\n", "too!.")
// Mix up foreground and background colors, create new mixes!
red := color.New(color.FgRed)
boldRed := red.Add(color.Bold)
boldRed.Println("This will print text in bold red.")
whiteBackground := red.Add(color.BgWhite)
whiteBackground.Println("Red text with white background.")
```
### Use your own output (io.Writer)
```go
// Use your own io.Writer output
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
blue := color.New(color.FgBlue)
blue.Fprint(writer, "This will print text in blue.")
```
### Custom print functions (PrintFunc)
```go
// Create a custom print function for convenience
red := color.New(color.FgRed).PrintfFunc()
red("Warning")
red("Error: %s", err)
// Mix up multiple attributes
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
notice("Don't forget this...")
```
### Custom fprint functions (FprintFunc)
```go
blue := color.New(FgBlue).FprintfFunc()
blue(myWriter, "important notice: %s", stars)
// Mix up with multiple attributes
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
success(myWriter, "Don't forget this...")
```
### Insert into noncolor strings (SprintFunc)
```go
// Create SprintXxx functions to mix strings with other non-colorized strings:
yellow := color.New(color.FgYellow).SprintFunc()
red := color.New(color.FgRed).SprintFunc()
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
fmt.Printf("This %s rocks!\n", info("package"))
// Use helper functions
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
// Windows supported too! Just don't forget to change the output to color.Output
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
```
### Plug into existing code
```go
// Use handy standard colors
color.Set(color.FgYellow)
fmt.Println("Existing text will now be in yellow")
fmt.Printf("This one %s\n", "too")
color.Unset() // Don't forget to unset
// You can mix up parameters
color.Set(color.FgMagenta, color.Bold)
defer color.Unset() // Use it in your function
fmt.Println("All text will now be bold magenta.")
```
### Disable/Enable color
There might be a case where you want to explicitly disable/enable color output. the
`go-isatty` package will automatically disable color output for non-tty output streams
(for example if the output were piped directly to `less`)
`Color` has support to disable/enable colors both globally and for single color
definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You
can easily disable the color output with:
```go
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
if *flagNoColor {
color.NoColor = true // disables colorized output
}
```
It also has support for single color definitions (local). You can
disable/enable color output on the fly:
```go
c := color.New(color.FgCyan)
c.Println("Prints cyan text")
c.DisableColor()
c.Println("This is printed without any color")
c.EnableColor()
c.Println("This prints again cyan...")
```
## Todo
* Save/Return previous values
* Evaluate fmt.Formatter interface
## Credits
* [Fatih Arslan](https://github.com/fatih)
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
## License
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details

6
vendor/github.com/fsnotify/fsnotify/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,6 @@
# Setup a Global .gitignore for OS and editor generated files:
# https://help.github.com/articles/ignoring-files
# git config --global core.excludesfile ~/.gitignore_global
.vagrant
*.sublime-project

317
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,317 @@
# Changelog
## v1.4.7 / 2018-01-09
* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
* Tests: Fix missing verb on format string (thanks @rchiossi)
* Linux: Fix deadlock in Remove (thanks @aarondl)
* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
* Docs: Moved FAQ into the README (thanks @vahe)
* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
* Docs: replace references to OS X with macOS
## v1.4.2 / 2016-10-10
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
## v1.4.1 / 2016-10-04
* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
## v1.4.0 / 2016-10-01
* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
## v1.3.1 / 2016-06-28
* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
## v1.3.0 / 2016-04-19
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
## v1.2.10 / 2016-03-02
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
## v1.2.9 / 2016-01-13
kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
## v1.2.8 / 2015-12-17
* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
* inotify: fix race in test
* enable race detection for continuous integration (Linux, Mac, Windows)
## v1.2.5 / 2015-10-17
* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
## v1.2.1 / 2015-10-14
* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
## v1.2.0 / 2015-02-08
* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
## v1.1.1 / 2015-02-05
* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
## v1.1.0 / 2014-12-12
* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
* add low-level functions
* only need to store flags on directories
* less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
* done can be an unbuffered channel
* remove calls to os.NewSyscallError
* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
## v1.0.4 / 2014-09-07
* kqueue: add dragonfly to the build tags.
* Rename source code files, rearrange code so exported APIs are at the top.
* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
## v1.0.3 / 2014-08-19
* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
## v1.0.2 / 2014-08-17
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
## v1.0.0 / 2014-08-15
* [API] Remove AddWatch on Windows, use Add.
* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
* Minor updates based on feedback from golint.
## dev / 2014-07-09
* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
## dev / 2014-07-04
* kqueue: fix incorrect mutex used in Close()
* Update example to demonstrate usage of Op.
## dev / 2014-06-28
* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
* Fix for String() method on Event (thanks Alex Brainman)
* Don't build on Plan 9 or Solaris (thanks @4ad)
## dev / 2014-06-21
* Events channel of type Event rather than *Event.
* [internal] use syscall constants directly for inotify and kqueue.
* [internal] kqueue: rename events to kevents and fileEvent to event.
## dev / 2014-06-19
* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
* [internal] remove cookie from Event struct (unused).
* [internal] Event struct has the same definition across every OS.
* [internal] remove internal watch and removeWatch methods.
## dev / 2014-06-12
* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
* [API] Pluralized channel names: Events and Errors.
* [API] Renamed FileEvent struct to Event.
* [API] Op constants replace methods like IsCreate().
## dev / 2014-06-12
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
## dev / 2014-05-23
* [API] Remove current implementation of WatchFlags.
* current implementation doesn't take advantage of OS for efficiency
* provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes
* no tests for the current implementation
* not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
## v0.9.3 / 2014-12-31
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
## v0.9.2 / 2014-08-17
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
## v0.9.1 / 2014-06-12
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
## v0.9.0 / 2014-01-17
* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
## v0.8.12 / 2013-11-13
* [API] Remove FD_SET and friends from Linux adapter
## v0.8.11 / 2013-11-02
* [Doc] Add Changelog [#72][] (thanks @nathany)
* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
## v0.8.10 / 2013-10-19
* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
* [Doc] specify OS-specific limits in README (thanks @debrando)
## v0.8.9 / 2013-09-08
* [Doc] Contributing (thanks @nathany)
* [Doc] update package path in example code [#63][] (thanks @paulhammond)
* [Doc] GoCI badge in README (Linux only) [#60][]
* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
## v0.8.8 / 2013-06-17
* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
## v0.8.7 / 2013-06-03
* [API] Make syscall flags internal
* [Fix] inotify: ignore event changes
* [Fix] race in symlink test [#45][] (reported by @srid)
* [Fix] tests on Windows
* lower case error messages
## v0.8.6 / 2013-05-23
* kqueue: Use EVT_ONLY flag on Darwin
* [Doc] Update README with full example
## v0.8.5 / 2013-05-09
* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
## v0.8.4 / 2013-04-07
* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
## v0.8.3 / 2013-03-13
* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
## v0.8.2 / 2013-02-07
* [Doc] add Authors
* [Fix] fix data races for map access [#29][] (thanks @fsouza)
## v0.8.1 / 2013-01-09
* [Fix] Windows path separators
* [Doc] BSD License
## v0.8.0 / 2012-11-09
* kqueue: directory watching improvements (thanks @vmirage)
* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
## v0.7.4 / 2012-10-09
* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
* [Fix] kqueue: modify after recreation of file
## v0.7.3 / 2012-09-27
* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
* [Fix] kqueue: no longer get duplicate CREATE events
## v0.7.2 / 2012-09-01
* kqueue: events for created directories
## v0.7.1 / 2012-07-14
* [Fix] for renaming files
## v0.7.0 / 2012-07-02
* [Feature] FSNotify flags
* [Fix] inotify: Added file name back to event path
## v0.6.0 / 2012-06-06
* kqueue: watch files after directory created (thanks @tmc)
## v0.5.1 / 2012-05-22
* [Fix] inotify: remove all watches before Close()
## v0.5.0 / 2012-05-03
* [API] kqueue: return errors during watch instead of sending over channel
* kqueue: match symlink behavior on Linux
* inotify: add `DELETE_SELF` (requested by @taralx)
* [Fix] kqueue: handle EINTR (reported by @robfig)
* [Doc] Godoc example [#1][] (thanks @davecheney)
## v0.4.0 / 2012-03-30
* Go 1 released: build with go tool
* [Feature] Windows support using winfsnotify
* Windows does not have attribute change notifications
* Roll attribute notifications into IsModify
## v0.3.0 / 2012-02-19
* kqueue: add files when watch directory
## v0.2.0 / 2011-12-30
* update to latest Go weekly code
## v0.1.0 / 2011-10-19
* kqueue: add watch on file creation to match inotify
* kqueue: create file event
* inotify: ignore `IN_IGNORED` events
* event String()
* linux: common FileEvent functions
* initial commit
[#79]: https://github.com/howeyc/fsnotify/pull/79
[#77]: https://github.com/howeyc/fsnotify/pull/77
[#72]: https://github.com/howeyc/fsnotify/issues/72
[#71]: https://github.com/howeyc/fsnotify/issues/71
[#70]: https://github.com/howeyc/fsnotify/issues/70
[#63]: https://github.com/howeyc/fsnotify/issues/63
[#62]: https://github.com/howeyc/fsnotify/issues/62
[#60]: https://github.com/howeyc/fsnotify/issues/60
[#59]: https://github.com/howeyc/fsnotify/issues/59
[#49]: https://github.com/howeyc/fsnotify/issues/49
[#45]: https://github.com/howeyc/fsnotify/issues/45
[#40]: https://github.com/howeyc/fsnotify/issues/40
[#36]: https://github.com/howeyc/fsnotify/issues/36
[#33]: https://github.com/howeyc/fsnotify/issues/33
[#29]: https://github.com/howeyc/fsnotify/issues/29
[#25]: https://github.com/howeyc/fsnotify/issues/25
[#24]: https://github.com/howeyc/fsnotify/issues/24
[#21]: https://github.com/howeyc/fsnotify/issues/21

77
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,77 @@
# Contributing
## Issues
* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
* Please indicate the platform you are using fsnotify on.
* A code example to reproduce the problem is appreciated.
## Pull Requests
### Contributor License Agreement
fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
Please indicate that you have signed the CLA in your pull request.
### How fsnotify is Developed
* Development is done on feature branches.
* Tests are run on BSD, Linux, macOS and Windows.
* Pull requests are reviewed and [applied to master][am] using [hub][].
* Maintainers may modify or squash commits rather than asking contributors to.
* To issue a new release, the maintainers will:
* Update the CHANGELOG
* Tag a version, which will become available through gopkg.in.
### How to Fork
For smooth sailing, always use the original import path. Installing with `go get` makes this easy.
1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Ensure everything works and the tests pass (see below)
4. Commit your changes (`git commit -am 'Add some feature'`)
Contribute upstream:
1. Fork fsnotify on GitHub
2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
3. Push to the branch (`git push fork my-new-feature`)
4. Create a new Pull Request on GitHub
This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
### Testing
fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
To aid in cross-platform testing there is a Vagrantfile for Linux and BSD.
* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/)
* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder.
* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password)
* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`.
* When you're done, you will want to halt or destroy the Vagrant boxes.
Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory.
Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
### Maintainers
Help maintaining fsnotify is welcome. To be a maintainer:
* Submit a pull request and sign the CLA as above.
* You must be able to run the test suite on Mac, Windows, Linux and BSD.
To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][].
All code changes should be internal pull requests.
Releases are tagged using [Semantic Versioning](http://semver.org/).
[hub]: https://github.com/github/hub
[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs

79
vendor/github.com/fsnotify/fsnotify/README.md generated vendored Normal file
View File

@@ -0,0 +1,79 @@
# File system notifications for Go
[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify)
fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running:
```console
go get -u golang.org/x/sys/...
```
Cross platform: Windows, Linux, BSD and macOS.
|Adapter |OS |Status |
|----------|----------|----------|
|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)|
|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)|
|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
|fanotify |Linux 2.6.37+ | |
|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)|
\* Android and iOS are untested.
Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
## API stability
fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA).
All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number.
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
## Contributing
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
## Example
See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go).
## FAQ
**When a file is moved to another directory is it still being watched?**
No (it shouldn't be, unless you are watching where it was moved to).
**When I watch a directory, are all subdirectories watched as well?**
No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
**Do I have to watch the Error and Event channels in a separate goroutine?**
As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
**Why am I receiving multiple events for the same file on OS X?**
Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
**How many files can be watched at once?**
There are OS-specific limits as to how many watches can be created:
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
[#62]: https://github.com/howeyc/fsnotify/issues/62
[#18]: https://github.com/fsnotify/fsnotify/issues/18
[#11]: https://github.com/fsnotify/fsnotify/issues/11
[#7]: https://github.com/howeyc/fsnotify/issues/7
[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
## Related Projects
* [notify](https://github.com/rjeczalik/notify)
* [fsevents](https://github.com/fsnotify/fsevents)

66
vendor/github.com/go-errors/errors/README.md generated vendored Normal file
View File

@@ -0,0 +1,66 @@
go-errors/errors
================
[![Build Status](https://travis-ci.org/go-errors/errors.svg?branch=master)](https://travis-ci.org/go-errors/errors)
Package errors adds stacktrace support to errors in go.
This is particularly useful when you want to understand the state of execution
when an error was returned unexpectedly.
It provides the type \*Error which implements the standard golang error
interface, so you can use this library interchangably with code that is
expecting a normal error return.
Usage
-----
Full documentation is available on
[godoc](https://godoc.org/github.com/go-errors/errors), but here's a simple
example:
```go
package crashy
import "github.com/go-errors/errors"
var Crashed = errors.Errorf("oh dear")
func Crash() error {
return errors.New(Crashed)
}
```
This can be called as follows:
```go
package main
import (
"crashy"
"fmt"
"github.com/go-errors/errors"
)
func main() {
err := crashy.Crash()
if err != nil {
if errors.Is(err, crashy.Crashed) {
fmt.Println(err.(*errors.Error).ErrorStack())
} else {
panic(err)
}
}
}
```
Meta-fu
-------
This package was original written to allow reporting to
[Bugsnag](https://bugsnag.com/) from
[bugsnag-go](https://github.com/bugsnag/bugsnag-go), but after I found similar
packages by Facebook and Dropbox, it was moved to one canonical location so
everyone can benefit.
This package is licensed under the MIT license, see LICENSE.MIT for details.

89
vendor/github.com/go-errors/errors/cover.out generated vendored Normal file
View File

@@ -0,0 +1,89 @@
mode: set
github.com/go-errors/errors/stackframe.go:27.51,30.25 2 1
github.com/go-errors/errors/stackframe.go:33.2,38.8 3 1
github.com/go-errors/errors/stackframe.go:30.25,32.3 1 0
github.com/go-errors/errors/stackframe.go:43.47,44.31 1 1
github.com/go-errors/errors/stackframe.go:47.2,47.48 1 1
github.com/go-errors/errors/stackframe.go:44.31,46.3 1 1
github.com/go-errors/errors/stackframe.go:52.42,56.16 3 1
github.com/go-errors/errors/stackframe.go:60.2,60.60 1 1
github.com/go-errors/errors/stackframe.go:56.16,58.3 1 0
github.com/go-errors/errors/stackframe.go:64.55,67.16 2 1
github.com/go-errors/errors/stackframe.go:71.2,72.61 2 1
github.com/go-errors/errors/stackframe.go:76.2,76.66 1 1
github.com/go-errors/errors/stackframe.go:67.16,69.3 1 0
github.com/go-errors/errors/stackframe.go:72.61,74.3 1 0
github.com/go-errors/errors/stackframe.go:79.56,91.63 3 1
github.com/go-errors/errors/stackframe.go:95.2,95.53 1 1
github.com/go-errors/errors/stackframe.go:100.2,101.18 2 1
github.com/go-errors/errors/stackframe.go:91.63,94.3 2 1
github.com/go-errors/errors/stackframe.go:95.53,98.3 2 1
github.com/go-errors/errors/error.go:70.32,73.23 2 1
github.com/go-errors/errors/error.go:80.2,85.3 3 1
github.com/go-errors/errors/error.go:74.2,75.10 1 1
github.com/go-errors/errors/error.go:76.2,77.28 1 1
github.com/go-errors/errors/error.go:92.43,95.23 2 1
github.com/go-errors/errors/error.go:104.2,109.3 3 1
github.com/go-errors/errors/error.go:96.2,97.11 1 1
github.com/go-errors/errors/error.go:98.2,99.10 1 1
github.com/go-errors/errors/error.go:100.2,101.28 1 1
github.com/go-errors/errors/error.go:115.39,117.19 1 1
github.com/go-errors/errors/error.go:121.2,121.29 1 1
github.com/go-errors/errors/error.go:125.2,125.43 1 1
github.com/go-errors/errors/error.go:129.2,129.14 1 1
github.com/go-errors/errors/error.go:117.19,119.3 1 1
github.com/go-errors/errors/error.go:121.29,123.3 1 1
github.com/go-errors/errors/error.go:125.43,127.3 1 1
github.com/go-errors/errors/error.go:135.53,137.2 1 1
github.com/go-errors/errors/error.go:140.34,142.2 1 1
github.com/go-errors/errors/error.go:146.34,149.42 2 1
github.com/go-errors/errors/error.go:153.2,153.20 1 1
github.com/go-errors/errors/error.go:149.42,151.3 1 1
github.com/go-errors/errors/error.go:158.39,160.2 1 1
github.com/go-errors/errors/error.go:164.46,165.23 1 1
github.com/go-errors/errors/error.go:173.2,173.19 1 1
github.com/go-errors/errors/error.go:165.23,168.32 2 1
github.com/go-errors/errors/error.go:168.32,170.4 1 1
github.com/go-errors/errors/error.go:177.37,178.42 1 1
github.com/go-errors/errors/error.go:181.2,181.41 1 1
github.com/go-errors/errors/error.go:178.42,180.3 1 1
github.com/go-errors/errors/parse_panic.go:10.39,12.2 1 1
github.com/go-errors/errors/parse_panic.go:16.46,24.34 5 1
github.com/go-errors/errors/parse_panic.go:70.2,70.43 1 1
github.com/go-errors/errors/parse_panic.go:73.2,73.55 1 0
github.com/go-errors/errors/parse_panic.go:24.34,27.23 2 1
github.com/go-errors/errors/parse_panic.go:27.23,28.42 1 1
github.com/go-errors/errors/parse_panic.go:28.42,31.5 2 1
github.com/go-errors/errors/parse_panic.go:31.6,33.5 1 0
github.com/go-errors/errors/parse_panic.go:35.5,35.29 1 1
github.com/go-errors/errors/parse_panic.go:35.29,36.86 1 1
github.com/go-errors/errors/parse_panic.go:36.86,38.5 1 1
github.com/go-errors/errors/parse_panic.go:40.5,40.32 1 1
github.com/go-errors/errors/parse_panic.go:40.32,41.18 1 1
github.com/go-errors/errors/parse_panic.go:45.4,46.46 2 1
github.com/go-errors/errors/parse_panic.go:51.4,53.23 2 1
github.com/go-errors/errors/parse_panic.go:57.4,58.18 2 1
github.com/go-errors/errors/parse_panic.go:62.4,63.17 2 1
github.com/go-errors/errors/parse_panic.go:41.18,43.10 2 1
github.com/go-errors/errors/parse_panic.go:46.46,49.5 2 1
github.com/go-errors/errors/parse_panic.go:53.23,55.5 1 0
github.com/go-errors/errors/parse_panic.go:58.18,60.5 1 0
github.com/go-errors/errors/parse_panic.go:63.17,65.10 2 1
github.com/go-errors/errors/parse_panic.go:70.43,72.3 1 1
github.com/go-errors/errors/parse_panic.go:80.85,82.29 2 1
github.com/go-errors/errors/parse_panic.go:85.2,85.15 1 1
github.com/go-errors/errors/parse_panic.go:88.2,90.63 2 1
github.com/go-errors/errors/parse_panic.go:94.2,94.53 1 1
github.com/go-errors/errors/parse_panic.go:99.2,101.36 2 1
github.com/go-errors/errors/parse_panic.go:105.2,106.15 2 1
github.com/go-errors/errors/parse_panic.go:109.2,112.49 3 1
github.com/go-errors/errors/parse_panic.go:116.2,117.16 2 1
github.com/go-errors/errors/parse_panic.go:121.2,126.8 1 1
github.com/go-errors/errors/parse_panic.go:82.29,84.3 1 0
github.com/go-errors/errors/parse_panic.go:85.15,87.3 1 1
github.com/go-errors/errors/parse_panic.go:90.63,93.3 2 1
github.com/go-errors/errors/parse_panic.go:94.53,97.3 2 1
github.com/go-errors/errors/parse_panic.go:101.36,103.3 1 0
github.com/go-errors/errors/parse_panic.go:106.15,108.3 1 0
github.com/go-errors/errors/parse_panic.go:112.49,114.3 1 1
github.com/go-errors/errors/parse_panic.go:117.16,119.3 1 0

6
vendor/github.com/go-ini/ini/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,6 @@
testdata/conf_out.ini
ini.sublime-project
ini.sublime-workspace
testdata/conf_reflect.ini
.idea
/.vscode

15
vendor/github.com/go-ini/ini/Makefile generated vendored Normal file
View File

@@ -0,0 +1,15 @@
.PHONY: build test bench vet coverage
build: vet bench
test:
go test -v -cover -race
bench:
go test -v -cover -race -test.bench=. -test.benchmem
vet:
go vet
coverage:
go test -coverprofile=c.out && go tool cover -html=c.out && rm c.out

44
vendor/github.com/go-ini/ini/README.md generated vendored Normal file
View File

@@ -0,0 +1,44 @@
INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini) [![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg)](https://sourcegraph.com/github.com/go-ini/ini)
===
![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
Package ini provides INI file read and write functionality in Go.
## Features
- Load from multiple data sources(`[]byte`, file and `io.ReadCloser`) with overwrites.
- Read with recursion values.
- Read with parent-child sections.
- Read with auto-increment key names.
- Read with multiple-line values.
- Read with tons of helper methods.
- Read and convert values to Go types.
- Read and **WRITE** comments of sections and keys.
- Manipulate sections, keys and comments with ease.
- Keep sections and keys in order as you parse and save.
## Installation
To use a tagged revision:
```sh
$ go get gopkg.in/ini.v1
```
To use with latest changes:
```sh
$ go get github.com/go-ini/ini
```
Please add `-u` flag to update in the future.
## Getting Help
- [Getting Started](https://ini.unknwon.io/docs/intro/getting_started)
- [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
## License
This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.

30
vendor/github.com/hashicorp/go-cleanhttp/README.md generated vendored Normal file
View File

@@ -0,0 +1,30 @@
# cleanhttp
Functions for accessing "clean" Go http.Client values
-------------
The Go standard library contains a default `http.Client` called
`http.DefaultClient`. It is a common idiom in Go code to start with
`http.DefaultClient` and tweak it as necessary, and in fact, this is
encouraged; from the `http` package documentation:
> The Client's Transport typically has internal state (cached TCP connections),
so Clients should be reused instead of created as needed. Clients are safe for
concurrent use by multiple goroutines.
Unfortunately, this is a shared value, and it is not uncommon for libraries to
assume that they are free to modify it at will. With enough dependencies, it
can be very easy to encounter strange problems and race conditions due to
manipulation of this shared value across libraries and goroutines (clients are
safe for concurrent use, but writing values to the client struct itself is not
protected).
Making things worse is the fact that a bare `http.Client` will use a default
`http.Transport` called `http.DefaultTransport`, which is another global value
that behaves the same way. So it is not simply enough to replace
`http.DefaultClient` with `&http.Client{}`.
This repository provides some simple functions to get a "clean" `http.Client`
-- one that uses the same default values as the Go standard library, but
returns a client that does not share any state with other clients.

View File

@@ -1 +0,0 @@
../real

10
vendor/github.com/hashicorp/go-safetemp/README.md generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# go-safetemp
[![Godoc](https://godoc.org/github.com/hashcorp/go-safetemp?status.svg)](https://godoc.org/github.com/hashicorp/go-safetemp)
Functions for safely working with temporary directories and files.
## Why?
The Go standard library provides the excellent `ioutil` package for
working with temporary directories and files. This library builds on top
of that to provide safe abstractions above that.

65
vendor/github.com/hashicorp/go-version/README.md generated vendored Normal file
View File

@@ -0,0 +1,65 @@
# Versioning Library for Go
[![Build Status](https://travis-ci.org/hashicorp/go-version.svg?branch=master)](https://travis-ci.org/hashicorp/go-version)
go-version is a library for parsing versions and version constraints,
and verifying versions against a set of constraints. go-version
can sort a collection of versions properly, handles prerelease/beta
versions, can increment versions, etc.
Versions used with go-version must follow [SemVer](http://semver.org/).
## Installation and Usage
Package documentation can be found on
[GoDoc](http://godoc.org/github.com/hashicorp/go-version).
Installation can be done with a normal `go get`:
```
$ go get github.com/hashicorp/go-version
```
#### Version Parsing and Comparison
```go
v1, err := version.NewVersion("1.2")
v2, err := version.NewVersion("1.5+metadata")
// Comparison example. There is also GreaterThan, Equal, and just
// a simple Compare that returns an int allowing easy >=, <=, etc.
if v1.LessThan(v2) {
fmt.Printf("%s is less than %s", v1, v2)
}
```
#### Version Constraints
```go
v1, err := version.NewVersion("1.2")
// Constraints example.
constraints, err := version.NewConstraint(">= 1.0, < 1.4")
if constraints.Check(v1) {
fmt.Printf("%s satisfies constraints %s", v1, constraints)
}
```
#### Version Sorting
```go
versionsRaw := []string{"1.1", "0.7.1", "1.4-beta", "1.4", "2"}
versions := make([]*version.Version, len(versionsRaw))
for i, raw := range versionsRaw {
v, _ := version.NewVersion(raw)
versions[i] = v
}
// After this, the versions are properly sorted
sort.Sort(version.Collection(versions))
```
## Issues and Contributing
If you find an issue with this library, please report an issue. If you'd
like, we welcome any contributions. Fork this library and submit a pull
request.

1
vendor/github.com/hashicorp/go-version/go.mod generated vendored Normal file
View File

@@ -0,0 +1 @@
module github.com/hashicorp/go-version

9
vendor/github.com/hashicorp/hcl/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,9 @@
y.output
# ignore intellij files
.idea
*.iml
*.ipr
*.iws
*.test

18
vendor/github.com/hashicorp/hcl/Makefile generated vendored Normal file
View File

@@ -0,0 +1,18 @@
TEST?=./...
default: test
fmt: generate
go fmt ./...
test: generate
go get -t ./...
go test $(TEST) $(TESTARGS)
generate:
go generate ./...
updatedeps:
go get -u golang.org/x/tools/cmd/stringer
.PHONY: default generate test updatedeps

125
vendor/github.com/hashicorp/hcl/README.md generated vendored Normal file
View File

@@ -0,0 +1,125 @@
# HCL
[![GoDoc](https://godoc.org/github.com/hashicorp/hcl?status.png)](https://godoc.org/github.com/hashicorp/hcl) [![Build Status](https://travis-ci.org/hashicorp/hcl.svg?branch=master)](https://travis-ci.org/hashicorp/hcl)
HCL (HashiCorp Configuration Language) is a configuration language built
by HashiCorp. The goal of HCL is to build a structured configuration language
that is both human and machine friendly for use with command-line tools, but
specifically targeted towards DevOps tools, servers, etc.
HCL is also fully JSON compatible. That is, JSON can be used as completely
valid input to a system expecting HCL. This helps makes systems
interoperable with other systems.
HCL is heavily inspired by
[libucl](https://github.com/vstakhov/libucl),
nginx configuration, and others similar.
## Why?
A common question when viewing HCL is to ask the question: why not
JSON, YAML, etc.?
Prior to HCL, the tools we built at [HashiCorp](http://www.hashicorp.com)
used a variety of configuration languages from full programming languages
such as Ruby to complete data structure languages such as JSON. What we
learned is that some people wanted human-friendly configuration languages
and some people wanted machine-friendly languages.
JSON fits a nice balance in this, but is fairly verbose and most
importantly doesn't support comments. With YAML, we found that beginners
had a really hard time determining what the actual structure was, and
ended up guessing more often than not whether to use a hyphen, colon, etc.
in order to represent some configuration key.
Full programming languages such as Ruby enable complex behavior
a configuration language shouldn't usually allow, and also forces
people to learn some set of Ruby.
Because of this, we decided to create our own configuration language
that is JSON-compatible. Our configuration language (HCL) is designed
to be written and modified by humans. The API for HCL allows JSON
as an input so that it is also machine-friendly (machines can generate
JSON instead of trying to generate HCL).
Our goal with HCL is not to alienate other configuration languages.
It is instead to provide HCL as a specialized language for our tools,
and JSON as the interoperability layer.
## Syntax
For a complete grammar, please see the parser itself. A high-level overview
of the syntax and grammar is listed here.
* Single line comments start with `#` or `//`
* Multi-line comments are wrapped in `/*` and `*/`. Nested block comments
are not allowed. A multi-line comment (also known as a block comment)
terminates at the first `*/` found.
* Values are assigned with the syntax `key = value` (whitespace doesn't
matter). The value can be any primitive: a string, number, boolean,
object, or list.
* Strings are double-quoted and can contain any UTF-8 characters.
Example: `"Hello, World"`
* Multi-line strings start with `<<EOF` at the end of a line, and end
with `EOF` on its own line ([here documents](https://en.wikipedia.org/wiki/Here_document)).
Any text may be used in place of `EOF`. Example:
```
<<FOO
hello
world
FOO
```
* Numbers are assumed to be base 10. If you prefix a number with 0x,
it is treated as a hexadecimal. If it is prefixed with 0, it is
treated as an octal. Numbers can be in scientific notation: "1e10".
* Boolean values: `true`, `false`
* Arrays can be made by wrapping it in `[]`. Example:
`["foo", "bar", 42]`. Arrays can contain primitives,
other arrays, and objects. As an alternative, lists
of objects can be created with repeated blocks, using
this structure:
```hcl
service {
key = "value"
}
service {
key = "value"
}
```
Objects and nested objects are created using the structure shown below:
```
variable "ami" {
description = "the AMI to use"
}
```
This would be equivalent to the following json:
``` json
{
"variable": {
"ami": {
"description": "the AMI to use"
}
}
}
```
## Thanks
Thanks to:
* [@vstakhov](https://github.com/vstakhov) - The original libucl parser
and syntax that HCL was based off of.
* [@fatih](https://github.com/fatih) - The rewritten HCL parser
in pure Go (no goyacc) and support for a printer.

19
vendor/github.com/hashicorp/hcl/appveyor.yml generated vendored Normal file
View File

@@ -0,0 +1,19 @@
version: "build-{branch}-{build}"
image: Visual Studio 2015
clone_folder: c:\gopath\src\github.com\hashicorp\hcl
environment:
GOPATH: c:\gopath
init:
- git config --global core.autocrlf false
install:
- cmd: >-
echo %Path%
go version
go env
go get -t ./...
build_script:
- cmd: go test -v ./...

301
vendor/github.com/jesseduffield/go-getter/README.md generated vendored Normal file
View File

@@ -0,0 +1,301 @@
# go-getter
[![Build Status](http://img.shields.io/travis/hashicorp/go-getter.svg?style=flat-square)][travis]
[![Build status](https://ci.appveyor.com/api/projects/status/ulq3qr43n62croyq/branch/master?svg=true)][appveyor]
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]
[travis]: http://travis-ci.org/hashicorp/go-getter
[godocs]: http://godoc.org/github.com/hashicorp/go-getter
[appveyor]: https://ci.appveyor.com/project/hashicorp/go-getter/branch/master
go-getter is a library for Go (golang) for downloading files or directories
from various sources using a URL as the primary form of input.
The power of this library is being flexible in being able to download
from a number of different sources (file paths, Git, HTTP, Mercurial, etc.)
using a single string as input. This removes the burden of knowing how to
download from a variety of sources from the implementer.
The concept of a _detector_ automatically turns invalid URLs into proper
URLs. For example: "github.com/hashicorp/go-getter" would turn into a
Git URL. Or "./foo" would turn into a file URL. These are extensible.
This library is used by [Terraform](https://terraform.io) for
downloading modules and [Nomad](https://nomadproject.io) for downloading
binaries.
## Installation and Usage
Package documentation can be found on
[GoDoc](http://godoc.org/github.com/hashicorp/go-getter).
Installation can be done with a normal `go get`:
```
$ go get github.com/hashicorp/go-getter
```
go-getter also has a command you can use to test URL strings:
```
$ go install github.com/hashicorp/go-getter/cmd/go-getter
...
$ go-getter github.com/foo/bar ./foo
...
```
The command is useful for verifying URL structures.
## URL Format
go-getter uses a single string URL as input to download from a variety of
protocols. go-getter has various "tricks" with this URL to do certain things.
This section documents the URL format.
### Supported Protocols and Detectors
**Protocols** are used to download files/directories using a specific
mechanism. Example protocols are Git and HTTP.
**Detectors** are used to transform a valid or invalid URL into another
URL if it matches a certain pattern. Example: "github.com/user/repo" is
automatically transformed into a fully valid Git URL. This allows go-getter
to be very user friendly.
go-getter out of the box supports the following protocols. Additional protocols
can be augmented at runtime by implementing the `Getter` interface.
* Local files
* Git
* Mercurial
* HTTP
* Amazon S3
In addition to the above protocols, go-getter has what are called "detectors."
These take a URL and attempt to automatically choose the best protocol for
it, which might involve even changing the protocol. The following detection
is built-in by default:
* File paths such as "./foo" are automatically changed to absolute
file URLs.
* GitHub URLs, such as "github.com/mitchellh/vagrant" are automatically
changed to Git protocol over HTTP.
* BitBucket URLs, such as "bitbucket.org/mitchellh/vagrant" are automatically
changed to a Git or mercurial protocol using the BitBucket API.
### Forced Protocol
In some cases, the protocol to use is ambiguous depending on the source
URL. For example, "http://github.com/mitchellh/vagrant.git" could reference
an HTTP URL or a Git URL. Forced protocol syntax is used to disambiguate this
URL.
Forced protocol can be done by prefixing the URL with the protocol followed
by double colons. For example: `git::http://github.com/mitchellh/vagrant.git`
would download the given HTTP URL using the Git protocol.
Forced protocols will also override any detectors.
In the absense of a forced protocol, detectors may be run on the URL, transforming
the protocol anyways. The above example would've used the Git protocol either
way since the Git detector would've detected it was a GitHub URL.
### Protocol-Specific Options
Each protocol can support protocol-specific options to configure that
protocol. For example, the `git` protocol supports specifying a `ref`
query parameter that tells it what ref to checkout for that Git
repository.
The options are specified as query parameters on the URL (or URL-like string)
given to go-getter. Using the Git example above, the URL below is a valid
input to go-getter:
github.com/hashicorp/go-getter?ref=abcd1234
The protocol-specific options are documented below the URL format
section. But because they are part of the URL, we point it out here so
you know they exist.
### Subdirectories
If you want to download only a specific subdirectory from a downloaded
directory, you can specify a subdirectory after a double-slash `//`.
go-getter will first download the URL specified _before_ the double-slash
(as if you didn't specify a double-slash), but will then copy the
path after the double slash into the target directory.
For example, if you're downloading this GitHub repository, but you only
want to download the `test-fixtures` directory, you can do the following:
```
https://github.com/hashicorp/go-getter.git//test-fixtures
```
If you downloaded this to the `/tmp` directory, then the file
`/tmp/archive.gz` would exist. Notice that this file is in the `test-fixtures`
directory in this repository, but because we specified a subdirectory,
go-getter automatically copied only that directory contents.
Subdirectory paths may contain may also use filesystem glob patterns.
The path must match _exactly one_ entry or go-getter will return an error.
This is useful if you're not sure the exact directory name but it follows
a predictable naming structure.
For example, the following URL would also work:
```
https://github.com/hashicorp/go-getter.git//test-*
```
### Checksumming
For file downloads of any protocol, go-getter can automatically verify
a checksum for you. Note that checksumming only works for downloading files,
not directories, but checksumming will work for any protocol.
To checksum a file, append a `checksum` query parameter to the URL.
The paramter value should be in the format of `type:value`, where
type is "md5", "sha1", "sha256", or "sha512". The "value" should be
the actual checksum value. go-getter will parse out this query parameter
automatically and use it to verify the checksum. An example URL
is shown below:
```
./foo.txt?checksum=md5:b7d96c89d09d9e204f5fedc4d5d55b21
```
The checksum query parameter is never sent to the backend protocol
implementation. It is used at a higher level by go-getter itself.
### Unarchiving
go-getter will automatically unarchive files into a file or directory
based on the extension of the file being requested (over any protocol).
This works for both file and directory downloads.
go-getter looks for an `archive` query parameter to specify the format of
the archive. If this isn't specified, go-getter will use the extension of
the path to see if it appears archived. Unarchiving can be explicitly
disabled by setting the `archive` query parameter to `false`.
The following archive formats are supported:
* `tar.gz` and `tgz`
* `tar.bz2` and `tbz2`
* `tar.xz` and `txz`
* `zip`
* `gz`
* `bz2`
* `xz`
For example, an example URL is shown below:
```
./foo.zip
```
This will automatically be inferred to be a ZIP file and will be extracted.
You can also be explicit about the archive type:
```
./some/other/path?archive=zip
```
And finally, you can disable archiving completely:
```
./some/path?archive=false
```
You can combine unarchiving with the other features of go-getter such
as checksumming. The special `archive` query parameter will be removed
from the URL before going to the final protocol downloader.
## Protocol-Specific Options
This section documents the protocol-specific options that can be specified
for go-getter. These options should be appended to the input as normal query
parameters. Depending on the usage of go-getter, applications may provide
alternate ways of inputting options. For example, [Nomad](https://www.nomadproject.io)
provides a nice options block for specifying options rather than in the URL.
## General (All Protocols)
The options below are available to all protocols:
* `archive` - The archive format to use to unarchive this file, or "" (empty
string) to disable unarchiving. For more details, see the complete section
on archive support above.
* `checksum` - Checksum to verify the downloaded file or archive. See
the entire section on checksumming above for format and more details.
* `filename` - When in file download mode, allows specifying the name of the
downloaded file on disk. Has no effect in directory mode.
### Local Files (`file`)
None
### Git (`git`)
* `ref` - The Git ref to checkout. This is a ref, so it can point to
a commit SHA, a branch name, etc. If it is a named ref such as a branch
name, go-getter will update it to the latest on each get.
* `sshkey` - An SSH private key to use during clones. The provided key must
be a base64-encoded string. For example, to generate a suitable `sshkey`
from a private key file on disk, you would run `base64 -w0 <file>`.
**Note**: Git 2.3+ is required to use this feature.
### Mercurial (`hg`)
* `rev` - The Mercurial revision to checkout.
### HTTP (`http`)
#### Basic Authentication
To use HTTP basic authentication with go-getter, simply prepend `username:password@` to the
hostname in the URL such as `https://Aladdin:OpenSesame@www.example.com/index.html`. All special
characters, including the username and password, must be URL encoded.
### S3 (`s3`)
S3 takes various access configurations in the URL. Note that it will also
read these from standard AWS environment variables if they're set. S3 compliant servers like Minio
are also supported. If the query parameters are present, these take priority.
* `aws_access_key_id` - AWS access key.
* `aws_access_key_secret` - AWS access key secret.
* `aws_access_token` - AWS access token if this is being used.
#### Using IAM Instance Profiles with S3
If you use go-getter and want to use an EC2 IAM Instance Profile to avoid
using credentials, then just omit these and the profile, if available will
be used automatically.
### Using S3 with Minio
If you use go-gitter for Minio support, you must consider the following:
* `aws_access_key_id` (required) - Minio access key.
* `aws_access_key_secret` (required) - Minio access key secret.
* `region` (optional - defaults to us-east-1) - Region identifier to use.
* `version` (optional - defaults to Minio default) - Configuration file format.
#### S3 Bucket Examples
S3 has several addressing schemes used to reference your bucket. These are
listed here: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro
Some examples for these addressing schemes:
- s3::https://s3.amazonaws.com/bucket/foo
- s3::https://s3-eu-west-1.amazonaws.com/bucket/foo
- bucket.s3.amazonaws.com/foo
- bucket.s3-eu-west-1.amazonaws.com/foo/bar
- "s3::http://127.0.0.1:9000/test-bucket/hello.txt?aws_access_key_id=KEYID&aws_access_key_secret=SECRETKEY&region=us-east-2"

16
vendor/github.com/jesseduffield/go-getter/appveyor.yml generated vendored Normal file
View File

@@ -0,0 +1,16 @@
version: "build-{branch}-{build}"
image: Visual Studio 2017
clone_folder: c:\gopath\github.com\hashicorp\go-getter
environment:
GOPATH: c:\gopath
install:
- cmd: >-
echo %Path%
go version
go env
go get -d -v -t ./...
build_script:
- cmd: go test -v ./...

1
vendor/github.com/jesseduffield/gocui/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
*.swp

110
vendor/github.com/jesseduffield/gocui/README.md generated vendored Normal file
View File

@@ -0,0 +1,110 @@
# GOCUI - Go Console User Interface
[![GoDoc](https://godoc.org/github.com/jroimartin/gocui?status.svg)](https://godoc.org/github.com/jroimartin/gocui)
Minimalist Go package aimed at creating Console User Interfaces.
## Features
* Minimalist API.
* Views (the "windows" in the GUI) implement the interface io.ReadWriter.
* Support for overlapping views.
* The GUI can be modified at runtime (concurrent-safe).
* Global and view-level keybindings.
* Mouse support.
* Colored text.
* Customizable edition mode.
* Easy to build reusable widgets, complex layouts...
## Installation
Execute:
```
$ go get github.com/jroimartin/gocui
```
## Documentation
Execute:
```
$ go doc github.com/jroimartin/gocui
```
Or visit [godoc.org](https://godoc.org/github.com/jroimartin/gocui) to read it
online.
## Example
```go
package main
import (
"fmt"
"log"
"github.com/jroimartin/gocui"
)
func main() {
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
log.Panicln(err)
}
defer g.Close()
g.SetManagerFunc(layout)
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln(err)
}
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln(err)
}
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
if err != gocui.ErrUnknownView {
return err
}
fmt.Fprintln(v, "Hello world!")
}
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
```
## Screenshots
![r2cui](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png)
![_examples/demo.go](https://cloud.githubusercontent.com/assets/1223476/5992750/720b84f0-aa36-11e4-88ec-296fa3247b52.png)
![_examples/dynamic.go](https://cloud.githubusercontent.com/assets/1223476/5992751/76ad5cc2-aa36-11e4-8204-6a90269db827.png)
## Projects using gocui
* [komanda-cli](https://github.com/mephux/komanda-cli): IRC Client For Developers.
* [vuls](https://github.com/future-architect/vuls): Agentless vulnerability scanner for Linux/FreeBSD.
* [wuzz](https://github.com/asciimoo/wuzz): Interactive cli tool for HTTP inspection.
* [httplab](https://github.com/gchaincl/httplab): Interactive web server.
* [domainr](https://github.com/MichaelThessel/domainr): Tool that checks the availability of domains based on keywords.
* [gotime](https://github.com/nanohard/gotime): Time tracker for projects and tasks.
* [claws](https://github.com/thehowl/claws): Interactive command line client for testing websockets.
* [terminews](http://github.com/antavelos/terminews): Terminal based RSS reader.
* [diagram](https://github.com/esimov/diagram): Tool to convert ascii arts into hand drawn diagrams.
* [pody](https://github.com/JulienBreux/pody): CLI app to manage Pods in a Kubernetes cluster.
* [kubexp](https://github.com/alitari/kubexp): Kubernetes client.
* [kcli](https://github.com/cswank/kcli): Tool for inspecting kafka topics/partitions/messages.
* [fac](https://github.com/mkchoi212/fac): git merge conflict resolver
* [jsonui](https://github.com/gulyasm/jsonui): Interactive JSON explorer for your terminal.
* [cointop](https://github.com/miguelmota/cointop): Interactive terminal based UI application for tracking cryptocurrencies.
Note: if your project is not listed here, let us know! :)

View File

@@ -33,18 +33,10 @@ var DefaultEditor Editor = EditorFunc(simpleEditor)
// simpleEditor is used as the default gocui editor.
func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
switch {
case ch != 0 && mod == 0:
v.EditWrite(ch)
case key == KeySpace:
v.EditWrite(' ')
case key == KeyBackspace || key == KeyBackspace2:
v.EditDelete(true)
case key == KeyDelete:
v.EditDelete(false)
case key == KeyInsert:
v.Overwrite = !v.Overwrite
case key == KeyEnter:
v.EditNewLine()
case key == KeyArrowDown:
v.MoveCursor(0, 1, false)
case key == KeyArrowUp:
@@ -53,6 +45,20 @@ func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
v.MoveCursor(-1, 0, false)
case key == KeyArrowRight:
v.MoveCursor(1, 0, false)
case key == KeyTab:
v.EditNewLine()
case key == KeySpace:
v.EditWrite(' ')
case key == KeyInsert:
v.Overwrite = !v.Overwrite
case key == KeyCtrlU:
v.EditDeleteToStartOfLine()
case key == KeyCtrlA:
v.EditGotoToStartOfLine()
case key == KeyCtrlE:
v.EditGotoToEndOfLine()
default:
v.EditWrite(ch)
}
}
@@ -63,6 +69,48 @@ func (v *View) EditWrite(ch rune) {
v.moveCursor(w, 0, true)
}
// EditDeleteToStartOfLine is the equivalent of pressing ctrl+U in your terminal, it deletes to the end of the line. Or if you are already at the start of the line, it deletes the newline character
func (v *View) EditDeleteToStartOfLine() {
x, _ := v.Cursor()
if x == 0 {
v.EditDelete(true)
} else {
// delete characters until we are the start of the line
for x > 0 {
v.EditDelete(true)
x, _ = v.Cursor()
}
}
}
// EditGotoToStartOfLine takes you to the start of the current line
func (v *View) EditGotoToStartOfLine() {
x, _ := v.Cursor()
for x > 0 {
v.MoveCursor(-1, 0, false)
x, _ = v.Cursor()
}
}
// EditGotoToEndOfLine takes you to the end of the line
func (v *View) EditGotoToEndOfLine() {
_, y := v.Cursor()
_ = v.SetCursor(0, y+1)
x, newY := v.Cursor()
if newY == y {
// we must be on the last line, so lets move to the very end
prevX := -1
for prevX != x {
prevX = x
v.MoveCursor(1, 0, false)
x, _ = v.Cursor()
}
} else {
// most left so now we're at the end of the original line
v.MoveCursor(-1, 0, false)
}
}
// EditDelete deletes a rune at the cursor position. back determines the
// direction.
func (v *View) EditDelete(back bool) {

View File

@@ -6,6 +6,7 @@ package gocui
import (
standardErrors "errors"
"strings"
"time"
"github.com/go-errors/errors"
@@ -38,18 +39,26 @@ const (
Output216 = OutputMode(termbox.Output216)
)
type tabClickHandler func(int) error
type tabClickBinding struct {
viewName string
handler tabClickHandler
}
// Gui represents the whole User Interface, including the views, layouts
// and keybindings.
type Gui struct {
tbEvents chan termbox.Event
userEvents chan userEvent
views []*View
currentView *View
managers []Manager
keybindings []*keybinding
maxX, maxY int
outputMode OutputMode
stop chan struct{}
tbEvents chan termbox.Event
userEvents chan userEvent
views []*View
currentView *View
managers []Manager
keybindings []*keybinding
tabClickBindings []*tabClickBinding
maxX, maxY int
outputMode OutputMode
stop chan struct{}
// BgColor and FgColor allow to configure the background and foreground
// colors of the GUI.
@@ -84,11 +93,16 @@ type Gui struct {
// NewGui returns a new Gui object with a given output mode.
func NewGui(mode OutputMode, supportOverlaps bool) (*Gui, error) {
if err := termbox.Init(); err != nil {
g := &Gui{}
var err error
if g.maxX, g.maxY, err = g.getTermWindowSize(); err != nil {
return nil, err
}
g := &Gui{}
if err := termbox.Init(); err != nil {
return nil, err
}
g.outputMode = mode
termbox.SetOutputMode(termbox.OutputMode(mode))
@@ -98,8 +112,6 @@ func NewGui(mode OutputMode, supportOverlaps bool) (*Gui, error) {
g.tbEvents = make(chan termbox.Event, 20)
g.userEvents = make(chan userEvent, 20)
g.maxX, g.maxY = termbox.Size()
g.BgColor, g.FgColor = ColorDefault, ColorDefault
g.SelBgColor, g.SelFgColor = ColorDefault, ColorDefault
@@ -129,7 +141,8 @@ func (g *Gui) Size() (x, y int) {
// the given colors.
func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
return errors.New("invalid point")
// swallowing error because it's not that big of a deal
return nil
}
termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor))
return nil
@@ -151,7 +164,7 @@ func (g *Gui) Rune(x, y int) (rune, error) {
// ErrUnknownView is returned, which allows to assert if the View must
// be initialized. It checks if the position is valid.
func (g *Gui) SetView(name string, x0, y0, x1, y1 int, overlaps byte) (*View, error) {
if x0 >= x1 || y0 >= y1 {
if x0 >= x1 {
return nil, errors.New("invalid dimensions")
}
if name == "" {
@@ -175,6 +188,17 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int, overlaps byte) (*View, er
return v, errors.Wrap(ErrUnknownView, 0)
}
// SetViewBeneath sets a view stacked beneath another view
func (g *Gui) SetViewBeneath(name string, aboveViewName string, height int) (*View, error) {
aboveView, err := g.View(aboveViewName)
if err != nil {
return nil, err
}
viewTop := aboveView.y1 + 1
return g.SetView(name, aboveView.x0, viewTop, aboveView.x1, viewTop+height-1, 0)
}
// SetViewOnTop sets the given view on top of the existing ones.
func (g *Gui) SetViewOnTop(name string) (*View, error) {
for i, v := range g.views {
@@ -221,7 +245,11 @@ func (g *Gui) ViewByPosition(x, y int) (*View, error) {
// traverse views in reverse order checking top views first
for i := len(g.views); i > 0; i-- {
v := g.views[i-1]
if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 {
frameOffset := 0
if v.Frame {
frameOffset = 1
}
if x > v.x0-frameOffset && x < v.x1+frameOffset && y > v.y0-frameOffset && y < v.y1+frameOffset {
return v, nil
}
}
@@ -309,6 +337,16 @@ func (g *Gui) DeleteKeybindings(viewname string) {
g.keybindings = s
}
// SetTabClickBinding sets a binding for a tab click event
func (g *Gui) SetTabClickBinding(viewName string, handler tabClickHandler) error {
g.tabClickBindings = append(g.tabClickBindings, &tabClickBinding{
viewName: viewName,
handler: handler,
})
return nil
}
// getKey takes an empty interface with a key and returns the corresponding
// typed Key or rune.
func getKey(key interface{}) (Key, rune, error) {
@@ -360,6 +398,7 @@ func (g *Gui) SetManager(managers ...Manager) {
g.currentView = nil
g.views = nil
g.keybindings = nil
g.tabClickBindings = nil
go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }()
}
@@ -471,15 +510,11 @@ func (g *Gui) flush() error {
}
}
for _, v := range g.views {
if v.y1 < v.y0 {
continue
}
if v.Frame {
var fgColor, bgColor Attribute
if g.Highlight && v == g.currentView {
fgColor = g.SelFgColor
bgColor = g.SelBgColor
} else {
fgColor = g.FgColor
bgColor = g.BgColor
}
fgColor, bgColor := g.viewColors(v)
if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil {
return err
@@ -487,7 +522,7 @@ func (g *Gui) flush() error {
if err := g.drawFrameCorners(v, fgColor, bgColor); err != nil {
return err
}
if v.Title != "" {
if v.Title != "" || len(v.Tabs) > 0 {
if err := g.drawTitle(v, fgColor, bgColor); err != nil {
return err
}
@@ -506,6 +541,13 @@ func (g *Gui) flush() error {
return nil
}
func (g *Gui) viewColors(v *View) (Attribute, Attribute) {
if g.Highlight && v == g.currentView {
return g.SelFgColor, g.SelBgColor
}
return g.FgColor, g.BgColor
}
// drawFrameEdges draws the horizontal and vertical edges of a view.
func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error {
runeH, runeV := '─', '│'
@@ -557,6 +599,18 @@ func corner(v *View, directions byte) rune {
// drawFrameCorners draws the corners of the view.
func (g *Gui) drawFrameCorners(v *View, fgColor, bgColor Attribute) error {
if v.y0 == v.y1 {
if !g.SupportOverlaps && v.x0 >= 0 && v.x1 >= 0 && v.y0 >= 0 && v.x0 < g.maxX && v.x1 < g.maxX && v.y0 < g.maxY {
if err := g.SetRune(v.x0, v.y0, '╶', fgColor, bgColor); err != nil {
return err
}
if err := g.SetRune(v.x1, v.y0, '╴', fgColor, bgColor); err != nil {
return err
}
}
return nil
}
runeTL, runeTR, runeBL, runeBR := '┌', '┐', '└', '┘'
if g.SupportOverlaps {
runeTL = corner(v, BOTTOM|RIGHT)
@@ -589,17 +643,57 @@ func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
return nil
}
for i, ch := range v.Title {
tabs := v.Tabs
separator := " - "
charIndex := 0
currentTabStart := -1
currentTabEnd := -1
if len(tabs) == 0 {
tabs = []string{v.Title}
} else {
for i, tab := range tabs {
if i == v.TabIndex {
currentTabStart = charIndex
currentTabEnd = charIndex + len(tab)
break
}
charIndex += len(tab)
if i < len(tabs)-1 {
charIndex += len(separator)
}
}
}
str := strings.Join(tabs, separator)
for i, ch := range str {
x := v.x0 + i + 2
if x < 0 {
continue
} else if x > v.x1-2 || x >= g.maxX {
break
}
if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil {
currentFgColor := fgColor
currentBgColor := bgColor
// if you are the current view and you have multiple tabs, de-highlight the non-selected tabs
if v == g.currentView && len(v.Tabs) > 0 {
currentFgColor = v.FgColor
currentBgColor = v.BgColor
}
if i >= currentTabStart && i <= currentTabEnd {
currentFgColor = v.SelFgColor
if v != g.currentView {
currentFgColor -= AttrBold
}
currentBgColor = v.SelBgColor
}
if err := g.SetRune(x, v.y0, ch, currentFgColor, currentBgColor); err != nil {
return err
}
}
return nil
}
@@ -610,6 +704,9 @@ func (g *Gui) drawSubtitle(v *View, fgColor, bgColor Attribute) error {
}
start := v.x1 - 5 - len(v.Subtitle)
if start < v.x0 {
return nil
}
for i, ch := range v.Subtitle {
x := start + i
if x >= v.x1 {
@@ -679,6 +776,17 @@ func (g *Gui) onKey(ev *termbox.Event) error {
if err != nil {
break
}
if v.Frame && my == v.y0 {
if len(v.Tabs) > 0 {
tabIndex := v.GetClickedTabIndex(mx - v.x0)
for _, binding := range g.tabClickBindings {
if binding.viewName == v.Name() {
return binding.handler(tabIndex)
}
}
}
}
if err := v.SetCursor(mx-v.x0-1, my-v.y0-1); err != nil {
return err
}
@@ -694,6 +802,8 @@ func (g *Gui) onKey(ev *termbox.Event) error {
// and event. The value of matched is true if there is a match and no errors.
func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) {
var globalKb *keybinding
var matchingParentViewKb *keybinding
for _, kb := range g.keybindings {
if kb.handler == nil {
continue
@@ -704,10 +814,16 @@ func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err err
if kb.matchView(v) {
return g.execKeybinding(v, kb)
}
if kb.viewName == "" && (!v.Editable || kb.ch == 0) {
if kb.matchView(v.ParentView) {
matchingParentViewKb = kb
}
if kb.viewName == "" && ((v != nil && !v.Editable) || (kb.ch == 0 && kb.key != KeyCtrlU && kb.key != KeyCtrlA && kb.key != KeyCtrlE)) {
globalKb = kb
}
}
if matchingParentViewKb != nil {
return g.execKeybinding(v.ParentView, matchingParentViewKb)
}
if globalKb != nil {
return g.execKeybinding(v, globalKb)
}
@@ -724,7 +840,7 @@ func (g *Gui) execKeybinding(v *View, kb *keybinding) (bool, error) {
func (g *Gui) loaderTick() {
go func() {
for range time.Tick(time.Millisecond) {
for range time.Tick(time.Millisecond * 50) {
for _, view := range g.Views() {
if view.HasLoader {
g.userEvents <- userEvent{func(g *Gui) error { return nil }}

61
vendor/github.com/jesseduffield/gocui/gui_notwin.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
// +build !windows
package gocui
import (
"os"
"os/signal"
"syscall"
"unsafe"
"github.com/go-errors/errors"
)
type windowSize struct {
rows uint16
cols uint16
xpixels uint16
ypixels uint16
}
// getTermWindowSize is get terminal window size on linux or unix.
// When gocui run inside the docker contaienr need to check and get the window size.
func (g *Gui) getTermWindowSize() (int, int, error) {
out, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return 0, 0, err
}
defer out.Close()
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGWINCH, syscall.SIGINT)
defer signal.Stop(signalCh)
var sz windowSize
for {
_, _, err = syscall.Syscall(
syscall.SYS_IOCTL,
out.Fd(),
uintptr(syscall.TIOCGWINSZ),
uintptr(unsafe.Pointer(&sz)),
)
// check terminal window size
if sz.cols > 0 && sz.rows > 0 {
return int(sz.cols), int(sz.rows), nil
}
select {
case signal := <-signalCh:
switch signal {
// when the terminal window size is changed
case syscall.SIGWINCH:
continue
// ctrl + c to cancel
case syscall.SIGINT:
return 0, 0, errors.New("There was not enough window space to start the application")
}
}
}
}

10
vendor/github.com/jesseduffield/gocui/gui_win.go generated vendored Normal file
View File

@@ -0,0 +1,10 @@
// +build windows
package gocui
import "github.com/jesseduffield/termbox-go"
func (g *Gui) getTermWindowSize() (int, int, error) {
x, y := termbox.Size()
return x, y, nil
}

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