Compare commits

..

121 Commits

Author SHA1 Message Date
Giorgio Previtera
a1c6619401 \#480 Close popup panels before switching to a side view
Reusing the `onNewPopupPanel` function to close existing popup panels
(if any) before switching to a new side view. Alse closing any
confirmation prompt.
2019-10-27 12:39:08 +11:00
Giorgio Previtera
3524f6baa9 480 - remove duplication by using a decorator
Also use a for loop to append the new keybindings
2019-10-27 12:39:08 +11:00
Giorgio Previtera
ac5cbc1d2c #480 Allow cycling side panels with number keys 2019-10-27 12:39:08 +11:00
mjarkk
a045313e08 Removed the pkg/gui/theme.go file
Moved most functions to the new theme/theme.go
2019-10-20 12:32:57 +11:00
mjarkk
9bd2dc3050 Updated the config.md 2019-10-20 12:32:57 +11:00
mjarkk
02fef3136f Added light theme option to the settings 2019-10-20 12:32:57 +11:00
Dawid Dziurla
8fe0e00cd9 Merge pull request #516 from glvr182/hotfix/path-not-positional
#514 Fix positional flag issue
2019-09-30 21:31:43 +02:00
Glenn Vriesman
f7f19bbc02 Main: Use --path instead of positional
* Also puts a placeholder for the merge-todo argument

Signed-off-by: Glenn Vriesman <glenn.vriesman@gmail.com>
2019-09-30 15:08:20 +02:00
Dawid Dziurla
95ae806e09 Merge pull request #414 from glvr182/feature/dir-as-arg
Provide git directory as argument to Lazygit
2019-09-24 19:45:46 +02:00
Glenn Vriesman
d8a6f173c3 Mod: Added flaggy to vendor directory
Signed-off-by: Glenn Vriesman <glenn.vriesman@gmail.com>
2019-09-24 18:52:52 +02:00
Glenn Vriesman
431f1aa766 Main: Added directory argument
*  Added a positional argument that allows the user to change the dir

Signed-off-by: Glenn Vriesman <glenn.vriesman@gmail.com>
2019-09-24 18:52:46 +02:00
Dawid Dziurla
379dcf0972 UserConfigPath -> UserConfigDir 2019-09-24 19:01:40 +10:00
Dawid Dziurla
0d25d113c9 download updated binary to config dir rather than /tmp 2019-09-24 19:01:40 +10:00
Dawid Dziurla
7c70913e8d Merge pull request #513 from jesseduffield/go.sum
update go.sum again
2019-09-21 18:30:16 +02:00
Dawid Dziurla
1c5858c515 update go.sum again 2019-09-21 18:02:23 +02:00
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
600 changed files with 114684 additions and 26391 deletions

View File

@@ -3,22 +3,11 @@ jobs:
build:
docker:
- 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" }}-v4
- 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" }}-v4
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:831819e8726b6b19e90079bce76d3048e2be3595359ee912d63d560e0db89b97"
name = "github.com/jesseduffield/gocui"
packages = ["."]
pruneopts = "NUT"
revision = "f0f0ab442e6c8e42650a7e38a45122bba2e11f3f"
[[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

@@ -10,7 +10,7 @@ Jira? This is the app for you!
- [Installation](https://github.com/jesseduffield/lazygit#installation)
- [Usage](https://github.com/jesseduffield/lazygit#usage),
[Keybindings](https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings_en.md)
[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)
@@ -21,7 +21,6 @@ Jira? This is the app for you!
### Homebrew
```sh
brew tap jesseduffield/lazygit
brew install lazygit
```
@@ -104,7 +103,7 @@ whichever rc file you're using).
- Basic video tutorial [here](https://youtu.be/VDXvbHZYeKY).
- List of keybindings
[here](/docs/Keybindings_en.md).
[here](/docs/keybindings).
## Cool features

View File

@@ -2,12 +2,13 @@
## Default:
```
```yaml
gui:
# stuff relating to the UI
scrollHeight: 2 # how many lines you scroll by
scrollPastBottom: true # enable scrolling past the bottom
theme:
lightTheme: false # For terminals with a light background
activeBorderColor:
- white
- bold
@@ -21,6 +22,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 +35,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 +58,7 @@
for users of VSCode
```
```yaml
os:
openCommand: 'code -r {{filename}}'
```
@@ -78,6 +81,21 @@ The available attributes are:
- reverse # useful for high-contrast
- underline
## Light terminal theme:
If you have issues with a light terminal theme where you can't read / see the text add these settings
```yaml
gui:
theme:
lightTheme: true
activeBorderColor:
- black
- bold
inactiveBorderColor:
- black
```
## Example Coloring:
![border example](/docs/resources/colored-border-example.png)

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>

93
go.mod
View File

@@ -3,64 +3,67 @@ 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/integrii/flaggy v1.2.2
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-20190305102456-f0f0ab442e6c
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
)

109
go.sum
View File

@@ -1,17 +1,28 @@
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=
@@ -19,9 +30,15 @@ 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=
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 h1:qC+3MHkvfCXb1cA9YDpWZ7np8tPOXZceLrW+xyqOgmk=
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU=
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc h1:wAa9fGALVHfjYxZuXRnmuJG2CnwRpJYOTvY6YdErAh0=
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
@@ -29,24 +46,34 @@ 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/integrii/flaggy v1.2.2 h1:SzL5kyEaW+Cb3RLxGG1ch9FFDLQPB6QuMdYoNu5JIo0=
github.com/integrii/flaggy v1.2.2/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 h1:Nrr/yUxNjXWYK0B3IqcFlYh1ICnesJDB4ogcfOVc5Ns=
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 h1:tbm85YuPi3d1LFAUr6yZyzZ4vR96ygV98rezZZ+ywbg=
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63/go.mod h1:fNqjRf+4XnTo2PrGN1JRb79b/BeoHwP4lU00f39SQY0=
github.com/jesseduffield/gocui v0.0.0-20190305102456-f0f0ab442e6c h1:ZmCZFQukfddcISnY8R5YYMCYJX0mc1Wfmq/MhEQViUE=
github.com/jesseduffield/gocui v0.0.0-20190305102456-f0f0ab442e6c/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=
@@ -63,23 +90,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=
@@ -94,29 +129,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=

43
main.go
View File

@@ -1,7 +1,6 @@
package main
import (
"flag"
"fmt"
"log"
"os"
@@ -9,6 +8,7 @@ import (
"runtime"
"github.com/go-errors/errors"
"github.com/integrii/flaggy"
"github.com/jesseduffield/lazygit/pkg/app"
"github.com/jesseduffield/lazygit/pkg/config"
)
@@ -18,10 +18,6 @@ var (
version = "unversioned"
date string
buildSource = "unknown"
configFlag = flag.Bool("config", false, "Print the current default config")
debuggingFlag = flag.Bool("debug", false, "a boolean")
versionFlag = flag.Bool("v", false, "Print the current version")
)
func projectPath(path string) string {
@@ -30,17 +26,43 @@ func projectPath(path string) string {
}
func main() {
flag.Parse()
if *versionFlag {
flaggy.DefaultParser.ShowVersionWithVersionFlag = false
repoPath := "."
flaggy.String(&repoPath, "p", "path", "Path of git repo")
dump := ""
flaggy.AddPositionalValue(&dump, "gitargs", 1, false, "Todo file")
flaggy.DefaultParser.PositionalFlags[0].Hidden = true
versionFlag := false
flaggy.Bool(&versionFlag, "v", "version", "Print the current version")
debuggingFlag := false
flaggy.Bool(&debuggingFlag, "d", "debug", "Run in debug mode with logging")
configFlag := false
flaggy.Bool(&configFlag, "c", "config", "Print the current default config")
flaggy.Parse()
if versionFlag {
fmt.Printf("commit=%s, build date=%s, build source=%s, version=%s, os=%s, arch=%s\n", commit, date, buildSource, version, runtime.GOOS, runtime.GOARCH)
os.Exit(0)
}
if *configFlag {
if configFlag {
fmt.Printf("%s\n", config.GetDefaultConfig())
os.Exit(0)
}
appConfig, err := config.NewAppConfig("lazygit", version, commit, date, buildSource, *debuggingFlag)
if repoPath != "." {
if err := os.Chdir(repoPath); err != nil {
log.Fatal(err.Error())
}
}
appConfig, err := config.NewAppConfig("lazygit", version, commit, date, buildSource, debuggingFlag)
if err != nil {
log.Fatal(err.Error())
}
@@ -52,6 +74,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

@@ -4,6 +4,8 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -18,7 +20,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 != "" {
@@ -38,7 +40,7 @@ func (b *Branch) GetColor() color.Attribute {
case "hotfix":
return color.FgRed
default:
return color.FgWhite
return theme.DefaultTextColor
}
}

View File

@@ -2,6 +2,7 @@ package commands
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -9,7 +10,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
@@ -22,7 +23,8 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
white := color.New(color.FgWhite)
defaultColor := color.New(theme.DefaultTextColor)
magenta := color.New(color.FgMagenta)
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
@@ -39,8 +41,10 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
shaColor = green
case "rebasing":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = white
shaColor = defaultColor
}
if c.Copied {
@@ -52,5 +56,5 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
}
return []string{shaColor.Sprint(c.Sha), actionString + white.Sprint(c.Name)}
return []string{shaColor.Sprint(c.Sha), actionString + defaultColor.Sprint(c.Name)}
}

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

@@ -13,15 +13,16 @@ import (
// AppConfig contains the base configuration fields required for lazygit.
type AppConfig struct {
Debug bool `long:"debug" env:"DEBUG" default:"false"`
Version string `long:"version" env:"VERSION" default:"unversioned"`
Commit string `long:"commit" env:"COMMIT"`
BuildDate string `long:"build-date" env:"BUILD_DATE"`
Name string `long:"name" env:"NAME" default:"lazygit"`
BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
UserConfig *viper.Viper
AppState *AppState
IsNewRepo bool
Debug bool `long:"debug" env:"DEBUG" default:"false"`
Version string `long:"version" env:"VERSION" default:"unversioned"`
Commit string `long:"commit" env:"COMMIT"`
BuildDate string `long:"build-date" env:"BUILD_DATE"`
Name string `long:"name" env:"NAME" default:"lazygit"`
BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
UserConfig *viper.Viper
UserConfigDir string
AppState *AppState
IsNewRepo bool
}
// AppConfigurer interface allows individual app config structs to inherit Fields
@@ -34,6 +35,7 @@ type AppConfigurer interface {
GetName() string
GetBuildSource() string
GetUserConfig() *viper.Viper
GetUserConfigDir() string
GetAppState() *AppState
WriteToUserConfig(string, string) error
SaveAppState() error
@@ -44,7 +46,7 @@ type AppConfigurer interface {
// NewAppConfig makes a new app config
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool) (*AppConfig, error) {
userConfig, err := LoadConfig("config", true)
userConfig, userConfigPath, err := LoadConfig("config", true)
if err != nil {
return nil, err
}
@@ -54,15 +56,16 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
}
appConfig := &AppConfig{
Name: "lazygit",
Version: version,
Commit: commit,
BuildDate: date,
Debug: debuggingFlag,
BuildSource: buildSource,
UserConfig: userConfig,
AppState: &AppState{},
IsNewRepo: false,
Name: "lazygit",
Version: version,
Commit: commit,
BuildDate: date,
Debug: debuggingFlag,
BuildSource: buildSource,
UserConfig: userConfig,
UserConfigDir: filepath.Dir(userConfigPath),
AppState: &AppState{},
IsNewRepo: false,
}
if err := appConfig.LoadAppState(); err != nil {
@@ -123,6 +126,10 @@ func (c *AppConfig) GetAppState() *AppState {
return c.AppState
}
func (c *AppConfig) GetUserConfigDir() string {
return c.UserConfigDir
}
func newViper(filename string) (*viper.Viper, error) {
v := viper.New()
v.SetConfigType("yaml")
@@ -131,23 +138,24 @@ func newViper(filename string) (*viper.Viper, error) {
}
// LoadConfig gets the user's config
func LoadConfig(filename string, withDefaults bool) (*viper.Viper, error) {
func LoadConfig(filename string, withDefaults bool) (*viper.Viper, string, error) {
v, err := newViper(filename)
if err != nil {
return nil, err
return nil, "", err
}
if withDefaults {
if err = LoadDefaults(v, GetDefaultConfig()); err != nil {
return nil, err
return nil, "", err
}
if err = LoadDefaults(v, GetPlatformDefaultConfig()); err != nil {
return nil, err
return nil, "", err
}
}
if err = LoadAndMergeFile(v, filename+".yml"); err != nil {
return nil, err
configPath, err := LoadAndMergeFile(v, filename+".yml")
if err != nil {
return nil, "", err
}
return v, nil
return v, configPath, nil
}
// LoadDefaults loads in the defaults defined in this file
@@ -173,21 +181,21 @@ func prepareConfigFile(filename string) (string, error) {
// LoadAndMergeFile Loads the config/state file, creating
// the file has an empty one if it does not exist
func LoadAndMergeFile(v *viper.Viper, filename string) error {
func LoadAndMergeFile(v *viper.Viper, filename string) (string, error) {
configPath, err := prepareConfigFile(filename)
if err != nil {
return err
return "", err
}
v.AddConfigPath(filepath.Dir(configPath))
return v.MergeInConfig()
return configPath, v.MergeInConfig()
}
// WriteToUserConfig adds a key/value pair to the user's config and saves it
func (c *AppConfig) WriteToUserConfig(key, value string) error {
// reloading the user config directly (without defaults) so that we're not
// writing any defaults back to the user's config
v, err := LoadConfig("config", false)
v, _, err := LoadConfig("config", false)
if err != nil {
return err
}
@@ -196,7 +204,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 {
@@ -236,6 +244,7 @@ func GetDefaultConfig() []byte {
scrollPastBottom: true
mouseEvents: false # will default to true when the feature is complete
theme:
lightTheme: false
activeBorderColor:
- white
- bold
@@ -245,9 +254,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

@@ -12,6 +12,7 @@ import (
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/theme"
)
func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.View) error) func(*gocui.Gui, *gocui.View) error {
@@ -81,7 +82,7 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
confirmationView.HasLoader = hasLoader
confirmationView.Title = title
confirmationView.Wrap = true
confirmationView.FgColor = gocui.ColorWhite
confirmationView.FgColor = theme.GocuiDefaultTextColor
}
gui.g.Update(func(g *gocui.Gui) error {
return gui.switchFocus(gui.g, currentView, confirmationView)

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"
@@ -23,6 +24,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/updates"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus"
@@ -103,7 +105,8 @@ type branchPanelState struct {
}
type commitPanelState struct {
SelectedLine int
SelectedLine int
SpecificDiffMode bool
}
type stashPanelState struct {
@@ -114,14 +117,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 +137,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 +158,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 +226,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 +254,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 +291,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,103 +372,98 @@ 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)
textColor := theme.GocuiDefaultTextColor
v, err := g.SetView("main", leftSideWidth+panelSpacing, 0, width-1, height-2, gocui.LEFT)
if err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("DiffTitle")
v.Wrap = true
v.FgColor = gocui.ColorWhite
v.FgColor = textColor
}
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
}
v.Title = gui.Tr.SLocalize("StatusTitle")
v.FgColor = gocui.ColorWhite
v.FgColor = textColor
}
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
}
filesView.Highlight = true
filesView.Title = gui.Tr.SLocalize("FilesTitle")
v.FgColor = gocui.ColorWhite
v.FgColor = textColor
}
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
}
branchesView.Title = gui.Tr.SLocalize("BranchesTitle")
branchesView.FgColor = gocui.ColorWhite
branchesView.FgColor = textColor
}
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 = textColor
}
commitsView, err := g.SetViewBeneath("commits", "branches", vHeights["commits"])
if err != nil {
if err.Error() != "unknown view" {
return err
}
commitsView.Title = gui.Tr.SLocalize("CommitsTitle")
commitsView.FgColor = gocui.ColorWhite
commitsView.FgColor = textColor
}
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
}
stashView.Title = gui.Tr.SLocalize("StashTitle")
stashView.FgColor = gocui.ColorWhite
stashView.FgColor = textColor
}
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
}
v.Frame = false
if v.FgColor, err = gui.GetOptionsPanelTextColor(); err != nil {
return err
}
userConfig := gui.Config.GetUserConfig()
v.FgColor = theme.GetColor(userConfig.GetStringSlice("gui.theme.optionsTextColor"))
}
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
}
g.SetViewOnBottom("commitMessage")
commitMessageView.Title = gui.Tr.SLocalize("CommitMessage")
commitMessageView.FgColor = gocui.ColorWhite
commitMessageView.FgColor = textColor
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
}
@@ -405,13 +472,12 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return err
}
credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername")
credentialsView.FgColor = gocui.ColorWhite
credentialsView.FgColor = textColor
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 +489,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 +500,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 +546,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()
@@ -530,6 +609,7 @@ func (gui *Gui) renderGlobalOptions() error {
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
"esc/q": gui.Tr.SLocalize("close"),
"x": gui.Tr.SLocalize("menu"),
"1-5": gui.Tr.SLocalize("jump"),
})
}
@@ -541,6 +621,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)
@@ -555,7 +652,7 @@ func (gui *Gui) Run() error {
gui.g = g // TODO: always use gui.g rather than passing g around everywhere
if err := gui.SetColorScheme(); err != nil {
if err := gui.setColorScheme(); err != nil {
return err
}
@@ -565,22 +662,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 +689,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 +700,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)
@@ -644,3 +747,14 @@ func (gui *Gui) handleDonate(g *gocui.Gui, v *gocui.View) error {
}
return gui.OSCommand.OpenLink("https://donorbox.org/lazygit")
}
// setColorScheme sets the color scheme for the app based on the user config
func (gui *Gui) setColorScheme() error {
userConfig := gui.Config.GetUserConfig()
theme.UpdateTheme(userConfig)
gui.g.FgColor = theme.InactiveBorderColor
gui.g.SelFgColor = theme.ActiveBorderColor
return nil
}

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},
@@ -454,17 +535,23 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
}...)
}
// Appends keybindings to jump to a particular sideView using numbers
for i, viewName := range []string{"status", "files", "branches", "commits", "stash"} {
bindings = append(bindings, &Binding{ViewName: "", Key: rune(i+1) + '0', Modifier: gocui.ModNone, Handler: gui.goToSideView(viewName)})
}
listPanelMap := map[string]struct {
prevLine func(*gocui.Gui, *gocui.View) error
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 +603,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

@@ -4,13 +4,14 @@ import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// 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 +52,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
@@ -61,7 +63,7 @@ func (gui *Gui) createMenu(title string, items interface{}, handlePress func(int
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, false, list)
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
menuView.Title = title
menuView.FgColor = gocui.ColorWhite
menuView.FgColor = theme.GocuiDefaultTextColor
menuView.Clear()
fmt.Fprint(menuView, list)
gui.State.Panels.Menu.SelectedLine = 0
@@ -81,6 +83,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

@@ -14,6 +14,7 @@ import (
"github.com/golang-collections/collections/stack"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -50,7 +51,7 @@ func (gui *Gui) coloredConflictFile(content string, conflicts []commands.Conflic
conflict, remainingConflicts := gui.shiftConflict(conflicts)
var outputBuffer bytes.Buffer
for i, line := range utils.SplitLines(content) {
colourAttr := color.FgWhite
colourAttr := theme.DefaultTextColor
if i == conflict.Start || i == conflict.Middle || i == conflict.End {
colourAttr = color.FgRed
}

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

@@ -1,54 +0,0 @@
package gui
import (
"github.com/jesseduffield/gocui"
)
// GetAttribute gets the gocui color attribute from the string
func (gui *Gui) GetAttribute(key string) gocui.Attribute {
colorMap := map[string]gocui.Attribute{
"default": gocui.ColorDefault,
"black": gocui.ColorBlack,
"red": gocui.ColorRed,
"green": gocui.ColorGreen,
"yellow": gocui.ColorYellow,
"blue": gocui.ColorBlue,
"magenta": gocui.ColorMagenta,
"cyan": gocui.ColorCyan,
"white": gocui.ColorWhite,
"bold": gocui.AttrBold,
"reverse": gocui.AttrReverse,
"underline": gocui.AttrUnderline,
}
value, present := colorMap[key]
if present {
return value
}
return gocui.ColorWhite
}
// GetColor bitwise OR's a list of attributes obtained via the given keys
func (gui *Gui) GetColor(keys []string) gocui.Attribute {
var attribute gocui.Attribute
for _, key := range keys {
attribute = attribute | gui.GetAttribute(key)
}
return attribute
}
// GetOptionsPanelTextColor gets the color of the options panel text
func (gui *Gui) GetOptionsPanelTextColor() (gocui.Attribute, error) {
userConfig := gui.Config.GetUserConfig()
optionsColor := userConfig.GetStringSlice("gui.theme.optionsTextColor")
return gui.GetColor(optionsColor), nil
}
// SetColorScheme sets the color scheme for the app based on the user config
func (gui *Gui) SetColorScheme() error {
userConfig := gui.Config.GetUserConfig()
activeBorderColor := userConfig.GetStringSlice("gui.theme.activeBorderColor")
inactiveBorderColor := userConfig.GetStringSlice("gui.theme.inactiveBorderColor")
gui.g.FgColor = gui.GetColor(inactiveBorderColor)
gui.g.SelFgColor = gui.GetColor(activeBorderColor)
return nil
}

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":
@@ -126,18 +139,39 @@ func (gui *Gui) returnFocus(g *gocui.Gui, v *gocui.View) error {
return gui.switchFocus(g, v, previousView)
}
func (gui *Gui) goToSideView(sideViewName string) func(g *gocui.Gui, v *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
view, err := g.View(sideViewName)
if err != nil {
gui.Log.Error(err)
return nil
}
err = gui.closePopupPanels()
if err != nil {
gui.Log.Error(err)
return nil
}
return gui.switchFocus(g, nil, view)
}
}
func (gui *Gui) closePopupPanels() error {
gui.onNewPopupPanel()
err := gui.closeConfirmationPrompt(gui.g)
if err != nil {
gui.Log.Error(err)
return err
}
return nil
}
// 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 +199,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 +305,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 +321,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 +413,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,142 @@ 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",
}, &i18n.Message{
ID: "jump",
Other: "jump to panel",
},
)
}

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,129 @@ 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",
}, &i18n.Message{
ID: "jump",
Other: "jump to panel",
},
)
}

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,129 @@ 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",
}, &i18n.Message{
ID: "jump",
Other: "jump to panel",
},
)
}

100
pkg/theme/theme.go Normal file
View File

@@ -0,0 +1,100 @@
package theme
import (
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/spf13/viper"
)
var (
// DefaultTextColor is the default text color
DefaultTextColor = color.FgWhite
// GocuiDefaultTextColor does the same as DefaultTextColor but this one only colors gocui default text colors
GocuiDefaultTextColor gocui.Attribute
// ActiveBorderColor is the border color of the active frame
ActiveBorderColor gocui.Attribute
// InactiveBorderColor is the border color of the inactive active frames
InactiveBorderColor gocui.Attribute
)
// UpdateTheme updates all theme variables
func UpdateTheme(userConfig *viper.Viper) {
ActiveBorderColor = getColor(userConfig.GetStringSlice("gui.theme.activeBorderColor"))
InactiveBorderColor = getColor(userConfig.GetStringSlice("gui.theme.inactiveBorderColor"))
isLightTheme := userConfig.GetBool("gui.theme.lightTheme")
if isLightTheme {
DefaultTextColor = color.FgBlack
GocuiDefaultTextColor = gocui.ColorBlack
} else {
DefaultTextColor = color.FgWhite
GocuiDefaultTextColor = gocui.ColorWhite
}
}
// getAttribute gets the gocui color attribute from the string
func getAttribute(key string) gocui.Attribute {
colorMap := map[string]gocui.Attribute{
"default": gocui.ColorDefault,
"black": gocui.ColorBlack,
"red": gocui.ColorRed,
"green": gocui.ColorGreen,
"yellow": gocui.ColorYellow,
"blue": gocui.ColorBlue,
"magenta": gocui.ColorMagenta,
"cyan": gocui.ColorCyan,
"white": gocui.ColorWhite,
"bold": gocui.AttrBold,
"reverse": gocui.AttrReverse,
"underline": gocui.AttrUnderline,
}
value, present := colorMap[key]
if present {
return value
}
return gocui.ColorWhite
}
// getColor bitwise OR's a list of attributes obtained via the given keys
func getColor(keys []string) gocui.Attribute {
var attribute gocui.Attribute
for _, key := range keys {
attribute |= getAttribute(key)
}
return attribute
}
// GetAttribute gets the gocui color attribute from the string
func GetAttribute(key string) gocui.Attribute {
colorMap := map[string]gocui.Attribute{
"default": gocui.ColorDefault,
"black": gocui.ColorBlack,
"red": gocui.ColorRed,
"green": gocui.ColorGreen,
"yellow": gocui.ColorYellow,
"blue": gocui.ColorBlue,
"magenta": gocui.ColorMagenta,
"cyan": gocui.ColorCyan,
"white": gocui.ColorWhite,
"bold": gocui.AttrBold,
"reverse": gocui.AttrReverse,
"underline": gocui.AttrUnderline,
}
value, present := colorMap[key]
if present {
return value
}
return gocui.ColorWhite
}
// GetColor bitwise OR's a list of attributes obtained via the given keys
func GetColor(keys []string) gocui.Attribute {
var attribute gocui.Attribute
for _, key := range keys {
attribute |= GetAttribute(key)
}
return attribute
}

View File

@@ -3,7 +3,6 @@ package updates
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
@@ -16,7 +15,7 @@ import (
"github.com/kardianos/osext"
getter "github.com/jesseduffield/go-getter"
"github.com/jesseduffield/go-getter"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/i18n"
@@ -261,15 +260,11 @@ func (u *Updater) downloadAndInstall(rawUrl string) error {
}
g := new(getter.HttpGetter)
tempDir, err := ioutil.TempDir("", "lazygit")
if err != nil {
return err
}
defer os.RemoveAll(tempDir)
u.Log.Info("Temp directory is " + tempDir)
configDir := u.Config.GetUserConfigDir()
u.Log.Info("Download directory is " + configDir)
// Get it!
if err := g.Get(tempDir, url); err != nil {
if err := g.Get(configDir, url); err != nil {
return err
}
@@ -284,7 +279,7 @@ func (u *Updater) downloadAndInstall(rawUrl string) error {
u.Log.Info("Binary name is " + binaryName)
// Verify the main file exists
tempPath := filepath.Join(tempDir, binaryName)
tempPath := filepath.Join(configDir, binaryName)
u.Log.Info("Temp path to binary is " + tempPath)
if _, err := os.Stat(tempPath); err != nil {
return err

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 ./...

14
vendor/github.com/integrii/flaggy/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,14 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

24
vendor/github.com/integrii/flaggy/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>

252
vendor/github.com/integrii/flaggy/README.md generated vendored Normal file
View File

@@ -0,0 +1,252 @@
<p align="center">
<img src="/logo.png" />
<br />
<a href="https://goreportcard.com/report/github.com/integrii/flaggy"><img src="https://goreportcard.com/badge/github.com/integrii/flaggy"></a>
<a href="https://travis-ci.org/integrii/flaggy"><img src="https://travis-ci.org/integrii/flaggy.svg?branch=master"></a>
<a href="http://godoc.org/github.com/integrii/flaggy"><img src="https://camo.githubusercontent.com/d48cccd1ce67ddf8ba7fc356ec1087f3f7aa6d12/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f6c696c65696f2f6c696c653f7374617475732e737667"></a>
<a href="http://unlicense.org/"><img src="https://img.shields.io/badge/license-Unlicense-blue.svg"></a>
<a href="https://cover.run/go?repo=github.com%2Fintegrii%2Fflaggy&tag=golang-1.10"><img src="https://cover.run/go/github.com/integrii/flaggy.svg?style=flat&tag=golang-1.10"></a>
<a href="https://github.com/avelino/awesome-go"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg"></a>
</p>
Sensible and _fast_ command-line flag parsing with excellent support for **subcommands** and **positional values**. Flags can be at any position. Flaggy has no required project or package layout like [Cobra requires](https://github.com/spf13/cobra/issues/641), and **no external dependencies**!
Check out the [godoc](http://godoc.org/github.com/integrii/flaggy), [examples directory](https://github.com/integrii/flaggy/tree/master/examples), and [examples in this readme](https://github.com/integrii/flaggy#super-simple-example) to get started quickly. You can also read the Flaggy introduction post with helpful examples [on my weblog](https://ericgreer.info/post/a-better-flags-package-for-go/).
# Installation
`go get -u github.com/integrii/flaggy`
# Key Features
- Very easy to use ([see examples below](https://github.com/integrii/flaggy#super-simple-example))
- 35 different flag types supported
- Any flag can be at any position
- Pretty and readable help output by default
- Positional subcommands
- Positional parameters
- Suggested subcommands when a subcommand is typo'd
- Nested subcommands
- Both global and subcommand specific flags
- Both global and subcommand specific positional parameters
- Customizable help templates for both the global command and subcommands
- Customizable appended/prepended help messages for both the global command and subcommands
- Simple function that displays help followed by a custom message string
- Flags and subcommands may have both a short and long name
- Unlimited trailing arguments after a `--`
- Flags can use a single dash or double dash (`--flag`, `-flag`, `-f`, `--f`)
- Flags can have `=` assignment operators, or use a space (`--flag=value`, `--flag value`)
- Flags support single quote globs with spaces (`--flag 'this is all one value'`)
- Flags of slice types can be passed multiple times (`-f one -f two -f three`)
- Optional but default version output with `--version`
- Optional but default help output with `-h` or `--help`
- Optional but default help output when any invalid or unknown parameter is passed
- It's _fast_. All flag and subcommand parsing takes less than `1ms` in most programs.
# Example Help Output
```
testCommand - Description goes here. Get more information at http://flaggy.
This is a prepend for help
Usage:
testCommand [subcommandA|subcommandB|subcommandC] [testPositionalA] [testPositionalB]
Positional Variables:
testPositionalA - (Required) Test positional A does some things with a positional value. (default: defaultValue)
testPositionalB - Test positional B does some less than serious things with a positional value.
Subcommands:
subcommandA (a) - Subcommand A is a command that does stuff
subcommandB (b) - Subcommand B is a command that does other stuff
subcommandC (c) - Subcommand C is a command that does SERIOUS stuff
Flags:
--version Displays the program version string.
-h --help Displays help with available flag, subcommand, and positional value parameters.
-s --stringFlag This is a test string flag that does some stringy string stuff.
-i --intFlg This is a test int flag that does some interesting int stuff. (default: 5)
-b --boolFlag This is a test bool flag that does some booly bool stuff. (default: true)
-d --durationFlag This is a test duration flag that does some untimely stuff. (default: 1h23s)
This is an append for help
This is a help add-on message
```
# Super Simple Example
`./yourApp -f test`
```go
// Declare variables and their defaults
var stringFlag = "defaultValue"
// Add a flag
flaggy.String(&stringFlag, "f", "flag", "A test string flag")
// Parse the flag
flaggy.Parse()
// Use the flag
print(stringFlag)
```
# Example with Subcommand
`./yourApp subcommandExample -f test`
```go
// Declare variables and their defaults
var stringFlag = "defaultValue"
// Create the subcommand
subcommand := flaggy.NewSubcommand("subcommandExample")
// Add a flag to the subcommand
subcommand.String(&stringFlag, "f", "flag", "A test string flag")
// Add the subcommand to the parser at position 1
flaggy.AttachSubcommand(subcommand, 1)
// Parse the subcommand and all flags
flaggy.Parse()
// Use the flag
print(stringFlag)
```
# Example with Nested Subcommands, Various Flags and Trailing Arguments
`./yourApp subcommandExample --flag=5 nestedSubcommand -t test -y -- trailingArg`
```go
// Declare variables and their defaults
var stringFlagF = "defaultValueF"
var intFlagT = 3
var boolFlagB bool
// Create the subcommands
subcommandExample := flaggy.NewSubcommand("subcommandExample")
nestedSubcommand := flaggy.NewSubcommand("nestedSubcommand")
// Add a flag to both subcommands
subcommandExample.String(&stringFlagF, "t", "testFlag", "A test string flag")
nestedSubcommand.Int(&intFlagT, "f", "flag", "A test int flag")
// add a global bool flag for fun
flaggy.Bool(&boolFlagB, "y", "yes", "A sample boolean flag")
// attach the nested subcommand to the parent subcommand at position 1
subcommandExample.AttachSubcommand(nestedSubcommand, 1)
// attach the base subcommand to the parser at position 1
flaggy.AttachSubcommand(subcommandExample, 1)
// Parse everything, then use the flags and trailing arguments
flaggy.Parse()
print(stringFlagF)
print(intFlagT)
print(boolFlagB)
print(flaggy.TrailingArguments[0])
```
# Supported Flag Types:
- string
- []string
- bool
- []bool
- time.Duration
- []time.Duration
- float32
- []float32
- float64
- []float64
- uint
- uint64
- []uint64
- uint32
- []uint32
- uint16
- []uint16
- uint8
- []uint8
- []byte
- int
- []int
- int64
- []int64
- int32
- []int32
- int16
- []int16
- int8
- []int8
- net.IP
- []net.IP
- net.HardwareAddr
- []net.HardwareAddr
- net.IPMask
- []net.IPMask
# Recommended Program Structure
Best practice when using flaggy includes setting your program's name, description, and version (at build time).
```go
package main
import "github.com/integrii/flaggy"
// make a variable for the version which will be set at build time
var version = "unknown"
// keep subcommands as globals so you can easily check if they were used later on
var mySubcommand *flaggy.Subcommand
func init() {
// Set your program's name and description. These appear in help output.
flaggy.SetName("Test Program")
flaggy.SetDescription("A little example program")
// you can disable various things by changing bools on the default parser
// (or your own parser if you have created one)
flaggy.DefaultParser.ShowHelpOnUnexpected = false
// you can set a help prepend or append on the default parser
flaggy.DefaultParser.AdditionalHelpPrepend = "http://github.com/integrii/flaggy"
// create any subcommands and set their parameters
mySubcommand = flaggy.NewSubcommand("mySubcommand")
mySubcommand.Description = "My great subcommand!"
// set the version and parse all inputs into variables
flaggy.SetVersion(version)
flaggy.Parse()
}
func main(){
if mySubcommand.Used {
...
}
}
```
Then, you can use the following build command to set the `version` variable in the above program at build time.
```bash
# build your app and set the version string
$ go build -ldflags='-X main.version=1.0.3-a3db3'
$ ./yourApp version
Version: 1.0.3-a3db3
$ ./yourApp --help
Test Program - A little example program
http://github.com/integrii/flaggy
```
# Contributions
Please feel free to open an issue if you find any bugs or see any features that make sense. Pull requests will be reviewed and accepted if they make sense, but it is always wise to submit a proposal issue before any major changes.

29
vendor/github.com/integrii/flaggy/argumentParser.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package flaggy
// setValueForParsers sets the value for a specified key in the
// specified parsers (which normally include a Parser and Subcommand).
// The return values represent the key being set, and any errors
// returned when setting the key, such as failures to convert the string
// into the appropriate flag value. We stop assigning values as soon
// as we find a parser that accepts it.
func setValueForParsers(key string, value string, parsers ...ArgumentParser) (bool, error) {
var valueWasSet bool
for _, p := range parsers {
valueWasSet, err := p.SetValueForKey(key, value)
if err != nil {
return valueWasSet, err
}
if valueWasSet {
break
}
}
return valueWasSet, nil
}
// ArgumentParser represents a parser or subcommand
type ArgumentParser interface {
SetValueForKey(key string, value string) (bool, error)
}

622
vendor/github.com/integrii/flaggy/flag.go generated vendored Normal file
View File

@@ -0,0 +1,622 @@
package flaggy
import (
"errors"
"fmt"
"net"
"reflect"
"strconv"
"strings"
"time"
)
// Flag holds the base methods for all flag types
type Flag struct {
ShortName string
LongName string
Description string
rawValue string // the value as a string before being parsed
Hidden bool // indicates this flag should be hidden from help and suggestions
AssignmentVar interface{}
defaultValue string // the value (as a string), that was set by default before any parsing and assignment
parsed bool // indicates that this flag has already been parsed
}
// HasName indicates that this flag's short or long name matches the
// supplied name string
func (f *Flag) HasName(name string) bool {
name = strings.TrimSpace(name)
if f.ShortName == name || f.LongName == name {
return true
}
return false
}
// identifyAndAssignValue identifies the type of the incoming value
// and assigns it to the AssignmentVar pointer's target value. If
// the value is a type that needs parsing, that is performed as well.
func (f *Flag) identifyAndAssignValue(value string) error {
var err error
// Only parse this flag default value once. This keeps us from
// overwriting the default value in help output
if !f.parsed {
f.parsed = true
// parse the default value as a string and remember it for help output
f.defaultValue, err = f.returnAssignmentVarValueAsString()
if err != nil {
return err
}
}
debugPrint("attempting to assign value", value, "to flag", f.LongName)
f.rawValue = value // remember the raw value
// depending on the type of the assignment variable, we convert the
// incoming string and assign it. We only use pointers to variables
// in flagy. No returning vars by value.
switch f.AssignmentVar.(type) {
case *string:
v, _ := (f.AssignmentVar).(*string)
*v = value
case *[]string:
v := f.AssignmentVar.(*[]string)
splitString := strings.Split(value, ",")
new := append(*v, splitString...)
*v = new
case *bool:
v, err := strconv.ParseBool(value)
if err != nil {
return err
}
a, _ := (f.AssignmentVar).(*bool)
*a = v
case *[]bool:
// parse the incoming bool
b, err := strconv.ParseBool(value)
if err != nil {
return err
}
// cast the assignment var
existing := f.AssignmentVar.(*[]bool)
// deref the assignment var and append to it
v := append(*existing, b)
// pointer the new value and assign it
a, _ := (f.AssignmentVar).(*[]bool)
*a = v
case *time.Duration:
v, err := time.ParseDuration(value)
if err != nil {
return err
}
a, _ := (f.AssignmentVar).(*time.Duration)
*a = v
case *[]time.Duration:
t, err := time.ParseDuration(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]time.Duration)
// deref the assignment var and append to it
v := append(*existing, t)
// pointer the new value and assign it
a, _ := (f.AssignmentVar).(*[]time.Duration)
*a = v
case *float32:
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return err
}
float := float32(v)
a, _ := (f.AssignmentVar).(*float32)
*a = float
case *[]float32:
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return err
}
float := float32(v)
existing := f.AssignmentVar.(*[]float32)
new := append(*existing, float)
*existing = new
case *float64:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
a, _ := (f.AssignmentVar).(*float64)
*a = v
case *[]float64:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]float64)
new := append(*existing, v)
*existing = new
case *int:
v, err := strconv.Atoi(value)
if err != nil {
return err
}
e := f.AssignmentVar.(*int)
*e = v
case *[]int:
v, err := strconv.Atoi(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]int)
new := append(*existing, v)
*existing = new
case *uint:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*uint)
*existing = uint(v)
case *[]uint:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint)
new := append(*existing, uint(v))
*existing = new
case *uint64:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*uint64)
*existing = v
case *[]uint64:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint64)
new := append(*existing, v)
*existing = new
case *uint32:
v, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
existing := f.AssignmentVar.(*uint32)
*existing = uint32(v)
case *[]uint32:
v, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint32)
new := append(*existing, uint32(v))
*existing = new
case *uint16:
v, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return err
}
val := uint16(v)
existing := f.AssignmentVar.(*uint16)
*existing = val
case *[]uint16:
v, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint16)
new := append(*existing, uint16(v))
*existing = new
case *uint8:
v, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return err
}
val := uint8(v)
existing := f.AssignmentVar.(*uint8)
*existing = val
case *[]uint8:
var newSlice []uint8
v, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return err
}
newV := uint8(v)
existing := f.AssignmentVar.(*[]uint8)
newSlice = append(*existing, newV)
*existing = newSlice
case *int64:
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*int64)
*existing = v
case *[]int64:
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int64)
newSlice := append(*existingSlice, v)
*existingSlice = newSlice
case *int32:
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
converted := int32(v)
existing := f.AssignmentVar.(*int32)
*existing = converted
case *[]int32:
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int32)
newSlice := append(*existingSlice, int32(v))
*existingSlice = newSlice
case *int16:
v, err := strconv.ParseInt(value, 10, 16)
if err != nil {
return err
}
converted := int16(v)
existing := f.AssignmentVar.(*int16)
*existing = converted
case *[]int16:
v, err := strconv.ParseInt(value, 10, 16)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int16)
newSlice := append(*existingSlice, int16(v))
*existingSlice = newSlice
case *int8:
v, err := strconv.ParseInt(value, 10, 8)
if err != nil {
return err
}
converted := int8(v)
existing := f.AssignmentVar.(*int8)
*existing = converted
case *[]int8:
v, err := strconv.ParseInt(value, 10, 8)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int8)
newSlice := append(*existingSlice, int8(v))
*existingSlice = newSlice
case *net.IP:
v := net.ParseIP(value)
existing := f.AssignmentVar.(*net.IP)
*existing = v
case *[]net.IP:
v := net.ParseIP(value)
existing := f.AssignmentVar.(*[]net.IP)
new := append(*existing, v)
*existing = new
case *net.HardwareAddr:
v, err := net.ParseMAC(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*net.HardwareAddr)
*existing = v
case *[]net.HardwareAddr:
v, err := net.ParseMAC(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]net.HardwareAddr)
new := append(*existing, v)
*existing = new
case *net.IPMask:
v := net.IPMask(net.ParseIP(value).To4())
existing := f.AssignmentVar.(*net.IPMask)
*existing = v
case *[]net.IPMask:
v := net.IPMask(net.ParseIP(value).To4())
existing := f.AssignmentVar.(*[]net.IPMask)
new := append(*existing, v)
*existing = new
default:
return errors.New("Unknown flag assignmentVar supplied in flag " + f.LongName + " " + f.ShortName)
}
return err
}
const argIsPositional = "positional" // subcommand or positional value
const argIsFlagWithSpace = "flagWithSpace" // -f path or --file path
const argIsFlagWithValue = "flagWithValue" // -f=path or --file=path
const argIsFinal = "final" // the final argument only '--'
// determineArgType determines if the specified arg is a flag with space
// separated value, a flag with a connected value, or neither (positional)
func determineArgType(arg string) string {
// if the arg is --, then its the final arg
if arg == "--" {
return argIsFinal
}
// if it has the prefix --, then its a long flag
if strings.HasPrefix(arg, "--") {
// if it contains an equals, it is a joined value
if strings.Contains(arg, "=") {
return argIsFlagWithValue
}
return argIsFlagWithSpace
}
// if it has the prefix -, then its a short flag
if strings.HasPrefix(arg, "-") {
// if it contains an equals, it is a joined value
if strings.Contains(arg, "=") {
return argIsFlagWithValue
}
return argIsFlagWithSpace
}
return argIsPositional
}
// parseArgWithValue parses a key=value concatenated argument into a key and
// value
func parseArgWithValue(arg string) (key string, value string) {
// remove up to two minuses from start of flag
arg = strings.TrimPrefix(arg, "-")
arg = strings.TrimPrefix(arg, "-")
// debugPrint("parseArgWithValue parsing", arg)
// break at the equals
args := strings.SplitN(arg, "=", 2)
// if its a bool arg, with no explicit value, we return a blank
if len(args) == 1 {
return args[0], ""
}
// if its a key and value pair, we return those
if len(args) == 2 {
// debugPrint("parseArgWithValue parsed", args[0], args[1])
return args[0], args[1]
}
fmt.Println("Warning: attempted to parseArgWithValue but did not have correct parameter count.", arg, "->", args)
return "", ""
}
// parseFlagToName parses a flag with space value down to a key name:
// --path -> path
// -p -> p
func parseFlagToName(arg string) string {
// remove minus from start
arg = strings.TrimLeft(arg, "-")
arg = strings.TrimLeft(arg, "-")
return arg
}
// flagIsBool determines if the flag is a bool within the specified parser
// and subcommand's context
func flagIsBool(sc *Subcommand, p *Parser, key string) bool {
for _, f := range append(sc.Flags, p.Flags...) {
if f.HasName(key) {
_, isBool := f.AssignmentVar.(*bool)
_, isBoolSlice := f.AssignmentVar.(*[]bool)
if isBool || isBoolSlice {
return true
}
}
}
// by default, the answer is false
return false
}
// returnAssignmentVarValueAsString returns the value of the flag's
// assignment variable as a string. This is used to display the
// default value of flags before they are assigned (like when help is output).
func (f *Flag) returnAssignmentVarValueAsString() (string, error) {
debugPrint("returning current value of assignment var of flag", f.LongName)
var err error
// depending on the type of the assignment variable, we convert the
// incoming string and assign it. We only use pointers to variables
// in flagy. No returning vars by value.
switch f.AssignmentVar.(type) {
case *string:
v, _ := (f.AssignmentVar).(*string)
return *v, err
case *[]string:
v := f.AssignmentVar.(*[]string)
return strings.Join(*v, ","), err
case *bool:
a, _ := (f.AssignmentVar).(*bool)
return strconv.FormatBool(*a), err
case *[]bool:
value := f.AssignmentVar.(*[]bool)
var ss []string
for _, b := range *value {
ss = append(ss, strconv.FormatBool(b))
}
return strings.Join(ss, ","), err
case *time.Duration:
a := f.AssignmentVar.(*time.Duration)
return (*a).String(), err
case *[]time.Duration:
tds := f.AssignmentVar.(*[]time.Duration)
var asSlice []string
for _, td := range *tds {
asSlice = append(asSlice, td.String())
}
return strings.Join(asSlice, ","), err
case *float32:
a := f.AssignmentVar.(*float32)
return strconv.FormatFloat(float64(*a), 'f', 2, 32), err
case *[]float32:
v := f.AssignmentVar.(*[]float32)
var strSlice []string
for _, f := range *v {
formatted := strconv.FormatFloat(float64(f), 'f', 2, 32)
strSlice = append(strSlice, formatted)
}
return strings.Join(strSlice, ","), err
case *float64:
a := f.AssignmentVar.(*float64)
return strconv.FormatFloat(float64(*a), 'f', 2, 64), err
case *[]float64:
v := f.AssignmentVar.(*[]float64)
var strSlice []string
for _, f := range *v {
formatted := strconv.FormatFloat(float64(f), 'f', 2, 64)
strSlice = append(strSlice, formatted)
}
return strings.Join(strSlice, ","), err
case *int:
a := f.AssignmentVar.(*int)
return strconv.Itoa(*a), err
case *[]int:
val := f.AssignmentVar.(*[]int)
var strSlice []string
for _, i := range *val {
str := strconv.Itoa(i)
strSlice = append(strSlice, str)
}
return strings.Join(strSlice, ","), err
case *uint:
v := f.AssignmentVar.(*uint)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint:
values := f.AssignmentVar.(*[]uint)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *uint64:
v := f.AssignmentVar.(*uint64)
return strconv.FormatUint(*v, 10), err
case *[]uint64:
values := f.AssignmentVar.(*[]uint64)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(i, 10))
}
return strings.Join(strVars, ","), err
case *uint32:
v := f.AssignmentVar.(*uint32)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint32:
values := f.AssignmentVar.(*[]uint32)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *uint16:
v := f.AssignmentVar.(*uint16)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint16:
values := f.AssignmentVar.(*[]uint16)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *uint8:
v := f.AssignmentVar.(*uint8)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint8:
values := f.AssignmentVar.(*[]uint8)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *int64:
v := f.AssignmentVar.(*int64)
return strconv.FormatInt(int64(*v), 10), err
case *[]int64:
values := f.AssignmentVar.(*[]int64)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(i, 10))
}
return strings.Join(strVars, ","), err
case *int32:
v := f.AssignmentVar.(*int32)
return strconv.FormatInt(int64(*v), 10), err
case *[]int32:
values := f.AssignmentVar.(*[]int32)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(int64(i), 10))
}
return strings.Join(strVars, ","), err
case *int16:
v := f.AssignmentVar.(*int16)
return strconv.FormatInt(int64(*v), 10), err
case *[]int16:
values := f.AssignmentVar.(*[]int16)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(int64(i), 10))
}
return strings.Join(strVars, ","), err
case *int8:
v := f.AssignmentVar.(*int8)
return strconv.FormatInt(int64(*v), 10), err
case *[]int8:
values := f.AssignmentVar.(*[]int8)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(int64(i), 10))
}
return strings.Join(strVars, ","), err
case *net.IP:
val := f.AssignmentVar.(*net.IP)
return val.String(), err
case *[]net.IP:
val := f.AssignmentVar.(*[]net.IP)
var strSlice []string
for _, ip := range *val {
strSlice = append(strSlice, ip.String())
}
return strings.Join(strSlice, ","), err
case *net.HardwareAddr:
val := f.AssignmentVar.(*net.HardwareAddr)
return val.String(), err
case *[]net.HardwareAddr:
val := f.AssignmentVar.(*[]net.HardwareAddr)
var strSlice []string
for _, mac := range *val {
strSlice = append(strSlice, mac.String())
}
return strings.Join(strSlice, ","), err
case *net.IPMask:
val := f.AssignmentVar.(*net.IPMask)
return val.String(), err
case *[]net.IPMask:
val := f.AssignmentVar.(*[]net.IPMask)
var strSlice []string
for _, m := range *val {
strSlice = append(strSlice, m.String())
}
return strings.Join(strSlice, ","), err
default:
return "", errors.New("Unknown flag assignmentVar found in flag " + f.LongName + " " + f.ShortName + ". Type not supported: " + reflect.TypeOf(f.AssignmentVar).String())
}
}

3
vendor/github.com/integrii/flaggy/go.mod generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module github.com/integrii/flaggy
go 1.12

23
vendor/github.com/integrii/flaggy/help.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
package flaggy
// defaultHelpTemplate is the help template used by default
// {{if (or (or (gt (len .StringFlags) 0) (gt (len .IntFlags) 0)) (gt (len .BoolFlags) 0))}}
// {{if (or (gt (len .StringFlags) 0) (gt (len .BoolFlags) 0))}}
const defaultHelpTemplate = `{{.CommandName}}{{if .Description}} - {{.Description}}{{end}}{{if .PrependMessage}}
{{.PrependMessage}}{{end}}
{{if .UsageString}}
Usage:
{{.UsageString}}{{end}}{{if .Positionals}}
Positional Variables: {{range .Positionals}}
{{.Name}}{{if .Required}} (Required){{end}}{{if .Description}} - {{.Description}}{{end}}{{if .DefaultValue}} (default: {{.DefaultValue}}){{end}}{{end}}{{end}}{{if .Subcommands}}
Subcommands: {{range .Subcommands}}
{{.LongName}}{{if .ShortName}} ({{.ShortName}}){{end}}{{if .Position}}{{if gt .Position 1}} (position {{.Position}}){{end}}{{end}}{{if .Description}} - {{.Description}}{{end}}{{end}}
{{end}}{{if (gt (len .Flags) 0)}}
Flags: {{if .Flags}}{{range .Flags}}
{{if .ShortName}}-{{.ShortName}} {{else}} {{end}}{{if .LongName}}--{{.LongName}} {{end}}{{if .Description}} {{.Description}}{{if .DefaultValue}} (default: {{.DefaultValue}}){{end}}{{end}}{{end}}{{end}}
{{end}}{{if .AppendMessage}}{{.AppendMessage}}
{{end}}{{if .Message}}
{{.Message}}{{end}}
`

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