mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 21:46:49 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
555996642d | ||
|
|
aad4269128 | ||
|
|
b3fbe8453f | ||
|
|
ffac0e6420 | ||
|
|
7371765d47 | ||
|
|
7f75808499 | ||
|
|
778cb14dc4 | ||
|
|
00c679a3f0 | ||
|
|
d9ec6f9890 | ||
|
|
9858bfe4c3 | ||
|
|
feada164d0 | ||
|
|
a1706ef2ee | ||
|
|
1bd3aa9708 | ||
|
|
d0aaf94068 | ||
|
|
8054d85782 | ||
|
|
31a8be5f32 | ||
|
|
ca04655a09 | ||
|
|
4281cc2884 | ||
|
|
8013f18177 | ||
|
|
c965333756 | ||
|
|
54408a912c | ||
|
|
3839719154 | ||
|
|
e8c4bf20f6 | ||
|
|
fbadbdd771 | ||
|
|
c6aee678c0 | ||
|
|
c4c27262f2 | ||
|
|
a253b0b897 | ||
|
|
5ca47c3f47 | ||
|
|
456c593c72 | ||
|
|
3b264806ef | ||
|
|
92e75d4602 | ||
|
|
7add598235 | ||
|
|
2853aba951 | ||
|
|
8cc444182f | ||
|
|
2f50cbf2b8 | ||
|
|
417cb97dc6 | ||
|
|
fb5d25c9e9 | ||
|
|
30d79bfa2c | ||
|
|
4d9d2b134f | ||
|
|
6cb0a14f33 | ||
|
|
5a624764a8 | ||
|
|
b541987007 | ||
|
|
2d4801c39d | ||
|
|
7fe54e889d | ||
|
|
f6a9c727fa | ||
|
|
9067c3be3e |
@@ -0,0 +1,35 @@
|
|||||||
|
# This is an example goreleaser.yaml file with some sane defaults.
|
||||||
|
# Make sure to check the documentation at http://goreleaser.com
|
||||||
|
builds:
|
||||||
|
-
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
main: .
|
||||||
|
goos:
|
||||||
|
- freebsd
|
||||||
|
- windows
|
||||||
|
- darwin
|
||||||
|
- linux
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
archive:
|
||||||
|
replacements:
|
||||||
|
darwin: Darwin
|
||||||
|
linux: Linux
|
||||||
|
windows: Windows
|
||||||
|
386: i386
|
||||||
|
amd64: x86_64
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ .Tag }}-next"
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- '^docs:'
|
||||||
|
- '^test:'
|
||||||
|
env_files:
|
||||||
|
github_token: ./github_token
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
env:
|
|
||||||
- DEP_VERSION="0.5.0"
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.x
|
|
||||||
env: LATEST=true
|
|
||||||
- go: 1.7
|
|
||||||
- go: tip
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
before_install:
|
|
||||||
# Download the binary to bin folder in $GOPATH
|
|
||||||
# - curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-386 -o $GOPATH/bin/dep
|
|
||||||
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
|
||||||
- ls
|
|
||||||
- ls */*
|
|
||||||
# Make the binary executable
|
|
||||||
- chmod +x $GOPATH/bin/dep
|
|
||||||
- ls $GOPATH/bin/
|
|
||||||
- dep ensure
|
|
||||||
- go get github.com/mitchellh/gox
|
|
||||||
install:
|
|
||||||
-
|
|
||||||
script:
|
|
||||||
# - go get -v ./...
|
|
||||||
# - diff -u <(echo -n) <(gofmt -d .) # can't make gofmt ignore vendor directory
|
|
||||||
# - go vet $(go list ./... | grep -v /vendor/)
|
|
||||||
- if [ "${LATEST}" = "true" ]; then gox -os="linux darwin windows" -arch="amd64" -ldflags "-X main.Rev=`git rev-parse --short HEAD`" -verbose ./...; fi
|
|
||||||
- ls
|
|
||||||
- ls */*
|
|
||||||
deploy:
|
|
||||||
provider: releases
|
|
||||||
skip_cleanup: true
|
|
||||||
api_key:
|
|
||||||
secure: TnB8I+swjicHuGTXk3ncm1Aaa12eIJqWV/Lhcnbb01i39p6+fyn3vDMdWPcejt3R8gcJqv4wyP8UQVO9G1qkLppt6V/qAuY5x6nX0MgEa3t+8JLJnGYHZYsuIgan/ecAmeu5+6dgUhr9Oq6zQOEv/O88NsALzMlqnEQNXI8XSoScfhkiVDIp3zWov0vBizCdThnNgTx9zRpJVoqxmhWvgt+me2+fOhSx1Y+3ZA2gE7zq8IFAbxp36d0rsR5lKqmTuF+YsF9iQ7Ar+xCjbRunLsZx+VwGqGfpS/qS7EwsEqBI0vEO76eFJkwEsIzOvJiFNhBDUu3upquBFMT4uzxRxH3eV+J4mZtu29UDLdvKI5Q730Lk9AgmH4now+RmP08M0SEXJa+AnHeuBv2u1iU5bu+sI6CORVQzKQwOph9AABDjSZ54wrXIpYEeIW2sz8nx+hiG6QL1mqfM/l+55BR69u3vxKYMryQBxPuzhZCTOqqI4uahlb6GIUNZJ9vGZeIA9HFJq3ymW8cdrpYzhKf3Nx9jK+Yb81h5/AHq9iChXEC63VPCDXXGRllh2UYWNYCaAdtk+ekpLR8299e4CaEregy6g5U2S3/xrBKl87miu1uJ/fquXoxGdSU+JcmsmXZ26sGIU2TCYdNjSfIgpOyfMmB4JNtKHqWRHA9Fe42CRpA=
|
|
||||||
file:
|
|
||||||
- lazygit_windows_amd64.exe
|
|
||||||
- lazygit_darwin_amd64
|
|
||||||
- lazygit_linux_amd64
|
|
||||||
on:
|
|
||||||
repo: jesseduffield/lazygit
|
|
||||||
tags: true
|
|
||||||
condition: $LATEST = true
|
|
||||||
Generated
+24
-15
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
|
digest = "1:ade392a843b2035effb4b4a2efa2c3bab3eb29b992e98bacf9c898b0ecb54e45"
|
||||||
name = "github.com/fatih/color"
|
name = "github.com/fatih/color"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
|
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
|
||||||
version = "v1.7.0"
|
version = "v1.7.0"
|
||||||
|
|
||||||
@@ -14,55 +14,63 @@
|
|||||||
digest = "1:4a8ed9b8cf22bd03bee5d74179fa06a282e4a73b6de949f7a865ff56cd2537e0"
|
digest = "1:4a8ed9b8cf22bd03bee5d74179fa06a282e4a73b6de949f7a865ff56cd2537e0"
|
||||||
name = "github.com/golang-collections/collections"
|
name = "github.com/golang-collections/collections"
|
||||||
packages = ["stack"]
|
packages = ["stack"]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "604e922904d35e97f98a774db7881f049cd8d970"
|
revision = "604e922904d35e97f98a774db7881f049cd8d970"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:f086cf183e423bf1926f4de05f47bf57132fe5db9c99e464f733ce925280fc81"
|
digest = "1:82b3bbc50ba7b6065b0229ebf0fc990a76e47410acd510294e435aeb3523293e"
|
||||||
name = "github.com/jesseduffield/gocui"
|
name = "github.com/jesseduffield/gocui"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "3c923f53ac9952af649af04a067405843558d56f"
|
revision = "3c923f53ac9952af649af04a067405843558d56f"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
digest = "1:08c231ec84231a7e23d67e4b58f975e1423695a32467a362ee55a803f9de8061"
|
||||||
name = "github.com/mattn/go-colorable"
|
name = "github.com/mattn/go-colorable"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||||
version = "v0.0.9"
|
version = "v0.0.9"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
|
digest = "1:bc4f7eec3b7be8c6cb1f0af6c1e3333d5bb71072951aaaae2f05067b0803f287"
|
||||||
name = "github.com/mattn/go-isatty"
|
name = "github.com/mattn/go-isatty"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||||
version = "v0.0.3"
|
version = "v0.0.3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:e2d1d410fb367567c2b53ed9e2d719d3c1f0891397bb2fa49afd747cfbf1e8e4"
|
digest = "1:cb591533458f6eb6e2c1065ff3eac6b50263d7847deb23fc9f79b25bc608970e"
|
||||||
name = "github.com/mattn/go-runewidth"
|
name = "github.com/mattn/go-runewidth"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
|
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
|
||||||
version = "v0.0.2"
|
version = "v0.0.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:c9b6e36dbd23f8403a04493376916ca5dad8c01b2da5ae0a05e6a468eb0b6f24"
|
digest = "1:34d9354c2c5d916c05864327553047df59fc10e86ff1f408e4136eba0a25a5ec"
|
||||||
name = "github.com/nsf/termbox-go"
|
name = "github.com/nsf/termbox-go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "5c94acc5e6eb520f1bcd183974e01171cc4c23b3"
|
revision = "5c94acc5e6eb520f1bcd183974e01171cc4c23b3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:cd5ffc5bda4e0296ab3e4de90dbb415259c78e45e7fab13694b14cde8ab74541"
|
||||||
|
name = "github.com/tcnksm/go-gitconfig"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "d154598bacbf4501c095a309753c5d4af66caa81"
|
||||||
|
version = "v0.1.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:9ec6ad2659635ba0974dd7e55bf84233523eb4e7535c9a2fddaefc4cc62a3eac"
|
digest = "1:4d8a79fbc6fa348fc94afa4235947c5196b7900ed71b94aa5fcbc7e273d150e1"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix"]
|
packages = ["unix"]
|
||||||
pruneopts = "UT"
|
pruneopts = "NUT"
|
||||||
revision = "0ffbfd41fbef8ffcf9b62b0b0aa3a5873ed7a4fe"
|
revision = "0ffbfd41fbef8ffcf9b62b0b0aa3a5873ed7a4fe"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
@@ -72,6 +80,7 @@
|
|||||||
"github.com/fatih/color",
|
"github.com/fatih/color",
|
||||||
"github.com/golang-collections/collections/stack",
|
"github.com/golang-collections/collections/stack",
|
||||||
"github.com/jesseduffield/gocui",
|
"github.com/jesseduffield/gocui",
|
||||||
|
"github.com/tcnksm/go-gitconfig",
|
||||||
]
|
]
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|||||||
@@ -9,16 +9,20 @@ Are YOU tired of typing every git command directly into the terminal, but you're
|
|||||||

|

|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
### Via binary release
|
||||||
|
You can download a binary release [here](https://github.com/jesseduffield/lazygit/releases)
|
||||||
|
You may need to grant execute permission via `chmod +x ./<filename>`.
|
||||||
|
You can move this file to your local /bin directory and rename to `lazygit` so that you can run it anywhere
|
||||||
|
(Easier-install coming soon)
|
||||||
|
|
||||||
|
### Via Go
|
||||||
In a terminal call this command:
|
In a terminal call this command:
|
||||||
`go get github.com/jesseduffield/lazygit`
|
`go get github.com/jesseduffield/lazygit`
|
||||||
(if you don't have Go installed, you can follow the installation guide [here](https://golang.org/doc/install).
|
(if you don't have Go installed, you can follow the installation guide [here](https://golang.org/doc/install).
|
||||||
|
|
||||||
Then just call `lazygit` in your terminal inside a git repository.
|
|
||||||
|
|
||||||
If you want, you can also add an alias for this with `echo "alias lg='lazygit'" >> ~/.zshrc` (or whichever rc file you're using).
|
|
||||||
|
|
||||||
Please note:
|
Please note:
|
||||||
If you get an error claiming that lazygit cannot be found or is not defined, you may need to add `~/go/bin` to your $PATH (MacOS/Linux), or `%HOME%\go\bin` (Windows)
|
If you get an error claiming that lazygit cannot be found or is not defined, you may need to add `~/go/bin` to your $PATH (MacOS/Linux), or `%HOME%\go\bin` (Windows). Not to be mistaked for `C:\Go\bin` (which is for Go's own binaries, not apps like Lazygit)
|
||||||
|
|
||||||
### Ubuntu
|
### Ubuntu
|
||||||
Packages for Ubuntu 16.04 and up are available via Launchpad PPA.
|
Packages for Ubuntu 16.04 and up are available via Launchpad PPA.
|
||||||
@@ -31,6 +35,13 @@ sudo apt-get update
|
|||||||
sudo apt-get install lazygit
|
sudo apt-get install lazygit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Call `lazygit` in your terminal inside a git repository.
|
||||||
|
If you want, you can also add an alias for this with `echo "alias lg='lazygit'" >> ~/.zshrc` (or whichever rc file you're using).
|
||||||
|
Basic tutorial [Here](https://www.youtube.com/watch?v=VDXvbHZYeKY)
|
||||||
|
[Keybindings](https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md)
|
||||||
|
|
||||||
## Cool features
|
## Cool features
|
||||||
- Adding files easily
|
- Adding files easily
|
||||||
- Resolving merge conflicts
|
- Resolving merge conflicts
|
||||||
@@ -45,14 +56,12 @@ sudo apt-get install lazygit
|
|||||||
### Viewing commit diffs
|
### Viewing commit diffs
|
||||||

|

|
||||||
|
|
||||||
## Docs
|
|
||||||
[Keybindings](https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md)
|
|
||||||
|
|
||||||
## Milestones
|
## Milestones
|
||||||
- [ ] Easy Installation (homebrew, release binaries)
|
- [ ] Easy Installation (homebrew, release binaries)
|
||||||
- [ ] Configurable Keybindings
|
- [ ] Configurable Keybindings
|
||||||
- [ ] Configurable Color Themes
|
- [ ] Configurable Color Themes
|
||||||
- [ ] Spawning Subprocesses (help needed - have a look at https://github.com/jesseduffield/lazygit/pull/18)
|
- [ ] Spawning Subprocesses (help needed - have a look at https://github.com/jesseduffield/lazygit/pull/18)
|
||||||
|
- [ ] Maintainability
|
||||||
- [ ] Performance
|
- [ ] Performance
|
||||||
- [ ] i18n
|
- [ ] i18n
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -13,7 +13,9 @@
|
|||||||
space: toggle staged
|
space: toggle staged
|
||||||
c: commit changes
|
c: commit changes
|
||||||
shift+S: stash files
|
shift+S: stash files
|
||||||
o: open (osx only)
|
t: add patched (i.e. pick chunks of a file to add)
|
||||||
|
o: open
|
||||||
|
e: edit
|
||||||
s: open in sublime (requires 'subl' command)
|
s: open in sublime (requires 'subl' command)
|
||||||
v: open in vscode (requires 'code' command)
|
v: open in vscode (requires 'code' command)
|
||||||
i: add to .gitignore
|
i: add to .gitignore
|
||||||
|
|||||||
+27
-6
@@ -63,6 +63,21 @@ func handleFilePress(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return handleFileSelect(g, v)
|
return handleFileSelect(g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleAddPatch(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
file, err := getSelectedFile(g)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrNoFiles {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !file.HasUnstagedChanges {
|
||||||
|
return createErrorPanel(g, "File has no unstaged changes to add")
|
||||||
|
}
|
||||||
|
gitAddPatch(g, file.Name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func getSelectedFile(g *gocui.Gui) (GitFile, error) {
|
func getSelectedFile(g *gocui.Gui) (GitFile, error) {
|
||||||
if len(state.GitFiles) == 0 {
|
if len(state.GitFiles) == 0 {
|
||||||
return GitFile{}, ErrNoFiles
|
return GitFile{}, ErrNoFiles
|
||||||
@@ -115,12 +130,12 @@ func renderfilesOptions(g *gocui.Gui, gitFile *GitFile) error {
|
|||||||
"S": "stash files",
|
"S": "stash files",
|
||||||
"c": "commit changes",
|
"c": "commit changes",
|
||||||
"o": "open",
|
"o": "open",
|
||||||
"s": "sublime",
|
|
||||||
"v": "vscode",
|
|
||||||
"i": "ignore",
|
"i": "ignore",
|
||||||
"d": "delete",
|
"d": "delete",
|
||||||
"space": "toggle staged",
|
"space": "toggle staged",
|
||||||
"R": "refresh",
|
"R": "refresh",
|
||||||
|
"t": "add patch",
|
||||||
|
"e": "edit",
|
||||||
}
|
}
|
||||||
if state.HasMergeConflicts {
|
if state.HasMergeConflicts {
|
||||||
optionsMap["a"] = "abort merge"
|
optionsMap["a"] = "abort merge"
|
||||||
@@ -163,7 +178,7 @@ func handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
|||||||
if message == "" {
|
if message == "" {
|
||||||
return createErrorPanel(g, "You cannot commit without a commit message")
|
return createErrorPanel(g, "You cannot commit without a commit message")
|
||||||
}
|
}
|
||||||
if output, err := gitCommit(message); err != nil {
|
if output, err := gitCommit(g, message); err != nil {
|
||||||
return createErrorPanel(g, output)
|
return createErrorPanel(g, output)
|
||||||
}
|
}
|
||||||
refreshFiles(g)
|
refreshFiles(g)
|
||||||
@@ -172,7 +187,7 @@ func handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func genericFileOpen(g *gocui.Gui, v *gocui.View, open func(string) (string, error)) error {
|
func genericFileOpen(g *gocui.Gui, v *gocui.View, open func(*gocui.Gui, string) (string, error)) error {
|
||||||
file, err := getSelectedFile(g)
|
file, err := getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != ErrNoFiles {
|
if err != ErrNoFiles {
|
||||||
@@ -180,18 +195,24 @@ func genericFileOpen(g *gocui.Gui, v *gocui.View, open func(string) (string, err
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if output, err := open(file.Name); err != nil {
|
if _, err := open(g, file.Name); err != nil {
|
||||||
return createErrorPanel(g, output)
|
return createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleFileEdit(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
return genericFileOpen(g, v, editFile)
|
||||||
|
}
|
||||||
|
|
||||||
func handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
func handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
||||||
return genericFileOpen(g, v, openFile)
|
return genericFileOpen(g, v, openFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSublimeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
func handleSublimeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
||||||
return genericFileOpen(g, v, sublimeOpenFile)
|
return genericFileOpen(g, v, sublimeOpenFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleVsCodeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
func handleVsCodeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
||||||
return genericFileOpen(g, v, vsCodeOpenFile)
|
return genericFileOpen(g, v, vsCodeOpenFile)
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-7
@@ -13,11 +13,16 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
gitconfig "github.com/tcnksm/go-gitconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrNoCheckedOutBranch : When we have no checked out branch
|
// ErrNoCheckedOutBranch : When we have no checked out branch
|
||||||
ErrNoCheckedOutBranch = errors.New("No currently checked out branch")
|
ErrNoCheckedOutBranch = errors.New("No currently checked out branch")
|
||||||
|
|
||||||
|
// ErrNoOpenCommand : When we don't know which command to use to open a file
|
||||||
|
ErrNoOpenCommand = errors.New("Unsure what command to use to open this file")
|
||||||
)
|
)
|
||||||
|
|
||||||
// GitFile : A staged/unstaged file
|
// GitFile : A staged/unstaged file
|
||||||
@@ -263,23 +268,73 @@ func runCommand(command string) (string, error) {
|
|||||||
commandStartTime := time.Now()
|
commandStartTime := time.Now()
|
||||||
commandLog(command)
|
commandLog(command)
|
||||||
splitCmd := strings.Split(command, " ")
|
splitCmd := strings.Split(command, " ")
|
||||||
|
devLog(splitCmd)
|
||||||
cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).CombinedOutput()
|
cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).CombinedOutput()
|
||||||
devLog("run command time: ", time.Now().Sub(commandStartTime))
|
devLog("run command time: ", time.Now().Sub(commandStartTime))
|
||||||
return sanitisedCommandOutput(cmdOut, err)
|
return sanitisedCommandOutput(cmdOut, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func openFile(filename string) (string, error) {
|
func vsCodeOpenFile(g *gocui.Gui, filename string) (string, error) {
|
||||||
return runCommand("open " + filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
func vsCodeOpenFile(filename string) (string, error) {
|
|
||||||
return runCommand("code -r " + filename)
|
return runCommand("code -r " + filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sublimeOpenFile(filename string) (string, error) {
|
func sublimeOpenFile(g *gocui.Gui, filename string) (string, error) {
|
||||||
return runCommand("subl " + filename)
|
return runCommand("subl " + filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openFile(g *gocui.Gui, filename string) (string, error) {
|
||||||
|
cmdName, cmdTrail, err := getOpenCommand()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return runCommand(cmdName + " " + filename + cmdTrail)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOpenCommand() (string, string, error) {
|
||||||
|
//NextStep open equivalents: xdg-open (linux), cygstart (cygwin), open (OSX)
|
||||||
|
trailMap := map[string]string{
|
||||||
|
"xdg-open": " &>/dev/null &",
|
||||||
|
"cygstart": "",
|
||||||
|
"open": "",
|
||||||
|
}
|
||||||
|
for name, trail := range trailMap {
|
||||||
|
if out, _ := runCommand("which " + name); out != "exit status 1" {
|
||||||
|
return name, trail, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", "", ErrNoOpenCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func gitAddPatch(g *gocui.Gui, filename string) {
|
||||||
|
runSubProcess(g, "git", "add", "-p", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func editFile(g *gocui.Gui, filename string) (string, error) {
|
||||||
|
editor, _ := gitconfig.Global("core.editor")
|
||||||
|
if editor == "" {
|
||||||
|
editor = os.Getenv("VISUAL")
|
||||||
|
}
|
||||||
|
if editor == "" {
|
||||||
|
editor = os.Getenv("EDITOR")
|
||||||
|
}
|
||||||
|
if editor == "" {
|
||||||
|
return "", createErrorPanel(g, "No editor defined in $VISUAL, $EDITOR, or git config.")
|
||||||
|
}
|
||||||
|
runSubProcess(g, editor, filename)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSubProcess(g *gocui.Gui, cmdName string, commandArgs ...string) {
|
||||||
|
subprocess = exec.Command(cmdName, commandArgs...)
|
||||||
|
subprocess.Stdin = os.Stdin
|
||||||
|
subprocess.Stdout = os.Stdout
|
||||||
|
subprocess.Stderr = os.Stderr
|
||||||
|
|
||||||
|
g.Update(func(g *gocui.Gui) error {
|
||||||
|
return ErrSubprocess
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func getBranchGraph(branch string, baseBranch string) (string, error) {
|
func getBranchGraph(branch string, baseBranch string) (string, error) {
|
||||||
return runCommand("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 " + branch)
|
return runCommand("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 " + branch)
|
||||||
|
|
||||||
@@ -403,7 +458,12 @@ func removeFile(file GitFile) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func gitCommit(message string) (string, error) {
|
func gitCommit(g *gocui.Gui, message string) (string, error) {
|
||||||
|
gpgsign, _ := gitconfig.Global("commit.gpgsign")
|
||||||
|
if gpgsign != "" {
|
||||||
|
runSubProcess(g, "bash", "-c", "git commit -m \""+message+"\"")
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
return runDirectCommand("git commit -m \"" + message + "\"")
|
return runDirectCommand("git commit -m \"" + message + "\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
TnB8I+swjicHuGTXk3ncm1Aaa12eIJqWV/Lhcnbb01i39p6+fyn3vDMdWPcejt3R8gcJqv4wyP8UQVO9G1qkLppt6V/qAuY5x6nX0MgEa3t+8JLJnGYHZYsuIgan/ecAmeu5+6dgUhr9Oq6zQOEv/O88NsALzMlqnEQNXI8XSoScfhkiVDIp3zWov0vBizCdThnNgTx9zRpJVoqxmhWvgt+me2+fOhSx1Y+3ZA2gE7zq8IFAbxp36d0rsR5lKqmTuF+YsF9iQ7Ar+xCjbRunLsZx+VwGqGfpS/qS7EwsEqBI0vEO76eFJkwEsIzOvJiFNhBDUu3upquBFMT4uzxRxH3eV+J4mZtu29UDLdvKI5Q730Lk9AgmH4now+RmP08M0SEXJa+AnHeuBv2u1iU5bu+sI6CORVQzKQwOph9AABDjSZ54wrXIpYEeIW2sz8nx+hiG6QL1mqfM/l+55BR69u3vxKYMryQBxPuzhZCTOqqI4uahlb6GIUNZJ9vGZeIA9HFJq3ymW8cdrpYzhKf3Nx9jK+Yb81h5/AHq9iChXEC63VPCDXXGRllh2UYWNYCaAdtk+ekpLR8299e4CaEregy6g5U2S3/xrBKl87miu1uJ/fquXoxGdSU+JcmsmXZ26sGIU2TCYdNjSfIgpOyfMmB4JNtKHqWRHA9Fe42CRpA=
|
||||||
@@ -5,8 +5,6 @@ import (
|
|||||||
// "io"
|
// "io"
|
||||||
// "io/ioutil"
|
// "io/ioutil"
|
||||||
|
|
||||||
"log"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -71,75 +69,9 @@ func handleRefresh(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return refreshSidePanels(g)
|
return refreshSidePanels(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
|
|
||||||
// is only handled if the given view has focus, or handled globally if the view
|
|
||||||
// is ""
|
|
||||||
type Binding struct {
|
|
||||||
ViewName string
|
|
||||||
Handler func(*gocui.Gui, *gocui.View) error
|
|
||||||
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
|
|
||||||
Modifier gocui.Modifier
|
|
||||||
}
|
|
||||||
|
|
||||||
func keybindings(g *gocui.Gui) error {
|
|
||||||
bindings := []Binding{
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: previousView},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: nextView},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: nextView},
|
|
||||||
Binding{ViewName: "", Key: 'q', Modifier: gocui.ModNone, Handler: quit},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyCtrlC, Modifier: gocui.ModNone, Handler: quit},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: cursorDown},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: cursorUp},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyPgup, Modifier: gocui.ModNone, Handler: scrollUpMain},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyPgdn, Modifier: gocui.ModNone, Handler: scrollDownMain},
|
|
||||||
Binding{ViewName: "", Key: 'P', Modifier: gocui.ModNone, Handler: pushFiles},
|
|
||||||
Binding{ViewName: "", Key: 'p', Modifier: gocui.ModNone, Handler: pullFiles},
|
|
||||||
Binding{ViewName: "", Key: 'R', Modifier: gocui.ModNone, Handler: handleRefresh},
|
|
||||||
Binding{ViewName: "files", Key: 'c', Modifier: gocui.ModNone, Handler: handleCommitPress},
|
|
||||||
Binding{ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleFilePress},
|
|
||||||
Binding{ViewName: "files", Key: 'd', Modifier: gocui.ModNone, Handler: handleFileRemove},
|
|
||||||
Binding{ViewName: "files", Key: 'm', Modifier: gocui.ModNone, Handler: handleSwitchToMerge},
|
|
||||||
Binding{ViewName: "files", Key: 'o', Modifier: gocui.ModNone, Handler: handleFileOpen},
|
|
||||||
Binding{ViewName: "files", Key: 's', Modifier: gocui.ModNone, Handler: handleSublimeFileOpen},
|
|
||||||
Binding{ViewName: "files", Key: 'v', Modifier: gocui.ModNone, Handler: handleVsCodeFileOpen},
|
|
||||||
Binding{ViewName: "files", Key: 'i', Modifier: gocui.ModNone, Handler: handleIgnoreFile},
|
|
||||||
Binding{ViewName: "files", Key: 'r', Modifier: gocui.ModNone, Handler: handleRefreshFiles},
|
|
||||||
Binding{ViewName: "files", Key: 'S', Modifier: gocui.ModNone, Handler: handleStashSave},
|
|
||||||
Binding{ViewName: "files", Key: 'a', Modifier: gocui.ModNone, Handler: handleAbortMerge},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: handleSelectTop},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: handleSelectBottom},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleEscapeMerge},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handlePickHunk},
|
|
||||||
Binding{ViewName: "main", Key: 'b', Modifier: gocui.ModNone, Handler: handlePickBothHunks},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: handleSelectPrevConflict},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: handleSelectNextConflict},
|
|
||||||
Binding{ViewName: "main", Key: 'z', Modifier: gocui.ModNone, Handler: handlePopFileSnapshot},
|
|
||||||
Binding{ViewName: "branches", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleBranchPress},
|
|
||||||
Binding{ViewName: "branches", Key: 'c', Modifier: gocui.ModNone, Handler: handleCheckoutByName},
|
|
||||||
Binding{ViewName: "branches", Key: 'F', Modifier: gocui.ModNone, Handler: handleForceCheckout},
|
|
||||||
Binding{ViewName: "branches", Key: 'n', Modifier: gocui.ModNone, Handler: handleNewBranch},
|
|
||||||
Binding{ViewName: "branches", Key: 'm', Modifier: gocui.ModNone, Handler: handleMerge},
|
|
||||||
Binding{ViewName: "commits", Key: 's', Modifier: gocui.ModNone, Handler: handleCommitSquashDown},
|
|
||||||
Binding{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: handleRenameCommit},
|
|
||||||
Binding{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: handleResetToCommit},
|
|
||||||
Binding{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleStashApply},
|
|
||||||
Binding{ViewName: "stash", Key: 'k', Modifier: gocui.ModNone, Handler: handleStashPop},
|
|
||||||
Binding{ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: handleStashDrop},
|
|
||||||
}
|
|
||||||
for _, binding := range bindings {
|
|
||||||
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func layout(g *gocui.Gui) error {
|
func layout(g *gocui.Gui) error {
|
||||||
g.Highlight = true
|
g.Highlight = true
|
||||||
g.SelFgColor = gocui.ColorWhite | gocui.AttrBold
|
g.SelFgColor = gocui.ColorWhite | gocui.AttrBold
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
g.FgColor = gocui.ColorBlack
|
|
||||||
}
|
|
||||||
width, height := g.Size()
|
width, height := g.Size()
|
||||||
leftSideWidth := width / 3
|
leftSideWidth := width / 3
|
||||||
statusFilesBoundary := 2
|
statusFilesBoundary := 2
|
||||||
@@ -225,14 +157,23 @@ func layout(g *gocui.Gui) error {
|
|||||||
v.FgColor = gocui.ColorWhite
|
v.FgColor = gocui.ColorWhite
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := g.SetView("options", -1, optionsTop, width, optionsTop+2, 0); err != nil {
|
if v, err := g.SetView("options", -1, optionsTop, width-len(version)-2, optionsTop+2, 0); err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.BgColor = gocui.ColorDefault
|
v.BgColor = gocui.ColorDefault
|
||||||
v.FgColor = gocui.ColorBlue
|
v.FgColor = gocui.ColorBlue
|
||||||
v.Frame = false
|
v.Frame = false
|
||||||
v.Title = "Options"
|
}
|
||||||
|
|
||||||
|
if v, err := g.SetView("version", width-len(version)-1, optionsTop, width, optionsTop+2, 0); err != nil {
|
||||||
|
if err != gocui.ErrUnknownView {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.BgColor = gocui.ColorDefault
|
||||||
|
v.FgColor = gocui.ColorGreen
|
||||||
|
v.Frame = false
|
||||||
|
renderString(g, "version", version)
|
||||||
|
|
||||||
// these are only called once
|
// these are only called once
|
||||||
handleFileSelect(g, filesView)
|
handleFileSelect(g, filesView)
|
||||||
@@ -261,10 +202,10 @@ func updateLoader(g *gocui.Gui) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run() {
|
func run() (err error) {
|
||||||
g, err := gocui.NewGui(gocui.OutputNormal, OverlappingEdges)
|
g, err := gocui.NewGui(gocui.OutputNormal, OverlappingEdges)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicln(err)
|
return
|
||||||
}
|
}
|
||||||
defer g.Close()
|
defer g.Close()
|
||||||
|
|
||||||
@@ -283,13 +224,12 @@ func run() {
|
|||||||
|
|
||||||
g.SetManagerFunc(layout)
|
g.SetManagerFunc(layout)
|
||||||
|
|
||||||
if err := keybindings(g); err != nil {
|
if err = keybindings(g); err != nil {
|
||||||
log.Panicln(err)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
err = g.MainLoop()
|
||||||
log.Panicln(err)
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func quit(g *gocui.Gui, v *gocui.View) error {
|
func quit(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/jesseduffield/gocui"
|
||||||
|
|
||||||
|
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
|
||||||
|
// is only handled if the given view has focus, or handled globally if the view
|
||||||
|
// is ""
|
||||||
|
type Binding struct {
|
||||||
|
ViewName string
|
||||||
|
Handler func(*gocui.Gui, *gocui.View) error
|
||||||
|
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
|
||||||
|
Modifier gocui.Modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func keybindings(g *gocui.Gui) error {
|
||||||
|
bindings := []Binding{
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: previousView},
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: nextView},
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: nextView},
|
||||||
|
Binding{ViewName: "", Key: 'q', Modifier: gocui.ModNone, Handler: quit},
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyCtrlC, Modifier: gocui.ModNone, Handler: quit},
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: cursorDown},
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: cursorUp},
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyPgup, Modifier: gocui.ModNone, Handler: scrollUpMain},
|
||||||
|
Binding{ViewName: "", Key: gocui.KeyPgdn, Modifier: gocui.ModNone, Handler: scrollDownMain},
|
||||||
|
Binding{ViewName: "", Key: 'P', Modifier: gocui.ModNone, Handler: pushFiles},
|
||||||
|
Binding{ViewName: "", Key: 'p', Modifier: gocui.ModNone, Handler: pullFiles},
|
||||||
|
Binding{ViewName: "", Key: 'R', Modifier: gocui.ModNone, Handler: handleRefresh},
|
||||||
|
Binding{ViewName: "files", Key: 'c', Modifier: gocui.ModNone, Handler: handleCommitPress},
|
||||||
|
Binding{ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleFilePress},
|
||||||
|
Binding{ViewName: "files", Key: 'd', Modifier: gocui.ModNone, Handler: handleFileRemove},
|
||||||
|
Binding{ViewName: "files", Key: 'm', Modifier: gocui.ModNone, Handler: handleSwitchToMerge},
|
||||||
|
Binding{ViewName: "files", Key: 'e', Modifier: gocui.ModNone, Handler: handleFileEdit},
|
||||||
|
Binding{ViewName: "files", Key: 'o', Modifier: gocui.ModNone, Handler: handleFileOpen},
|
||||||
|
Binding{ViewName: "files", Key: 's', Modifier: gocui.ModNone, Handler: handleSublimeFileOpen},
|
||||||
|
Binding{ViewName: "files", Key: 'v', Modifier: gocui.ModNone, Handler: handleVsCodeFileOpen},
|
||||||
|
Binding{ViewName: "files", Key: 'i', Modifier: gocui.ModNone, Handler: handleIgnoreFile},
|
||||||
|
Binding{ViewName: "files", Key: 'r', Modifier: gocui.ModNone, Handler: handleRefreshFiles},
|
||||||
|
Binding{ViewName: "files", Key: 'S', Modifier: gocui.ModNone, Handler: handleStashSave},
|
||||||
|
Binding{ViewName: "files", Key: 'a', Modifier: gocui.ModNone, Handler: handleAbortMerge},
|
||||||
|
Binding{ViewName: "files", Key: 't', Modifier: gocui.ModNone, Handler: handleAddPatch},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: handleSelectTop},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: handleSelectBottom},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleEscapeMerge},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handlePickHunk},
|
||||||
|
Binding{ViewName: "main", Key: 'b', Modifier: gocui.ModNone, Handler: handlePickBothHunks},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: handleSelectPrevConflict},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: handleSelectNextConflict},
|
||||||
|
Binding{ViewName: "main", Key: 'z', Modifier: gocui.ModNone, Handler: handlePopFileSnapshot},
|
||||||
|
Binding{ViewName: "branches", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleBranchPress},
|
||||||
|
Binding{ViewName: "branches", Key: 'c', Modifier: gocui.ModNone, Handler: handleCheckoutByName},
|
||||||
|
Binding{ViewName: "branches", Key: 'F', Modifier: gocui.ModNone, Handler: handleForceCheckout},
|
||||||
|
Binding{ViewName: "branches", Key: 'n', Modifier: gocui.ModNone, Handler: handleNewBranch},
|
||||||
|
Binding{ViewName: "branches", Key: 'm', Modifier: gocui.ModNone, Handler: handleMerge},
|
||||||
|
Binding{ViewName: "commits", Key: 's', Modifier: gocui.ModNone, Handler: handleCommitSquashDown},
|
||||||
|
Binding{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: handleRenameCommit},
|
||||||
|
Binding{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: handleResetToCommit},
|
||||||
|
Binding{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleStashApply},
|
||||||
|
Binding{ViewName: "stash", Key: 'k', Modifier: gocui.ModNone, Handler: handleStashPop},
|
||||||
|
Binding{ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: handleStashDrop},
|
||||||
|
}
|
||||||
|
for _, binding := range bindings {
|
||||||
|
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,19 +1,31 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrSubProcess is raised when we are running a subprocess
|
||||||
var (
|
var (
|
||||||
startTime time.Time
|
ErrSubprocess = errors.New("running subprocess")
|
||||||
debugging bool
|
subprocess *exec.Cmd
|
||||||
|
startTime time.Time
|
||||||
|
|
||||||
|
commit string
|
||||||
|
version = "unversioned"
|
||||||
|
|
||||||
|
date string
|
||||||
|
debuggingFlag = flag.Bool("debug", false, "a boolean")
|
||||||
|
versionFlag = flag.Bool("v", false, "Print the current version")
|
||||||
)
|
)
|
||||||
|
|
||||||
func homeDirectory() string {
|
func homeDirectory() string {
|
||||||
@@ -37,7 +49,7 @@ func commandLog(objects ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func localLog(colour color.Attribute, path string, objects ...interface{}) {
|
func localLog(colour color.Attribute, path string, objects ...interface{}) {
|
||||||
if !debugging {
|
if !*debuggingFlag {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f, _ := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
|
f, _ := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
@@ -58,12 +70,24 @@ func navigateToRepoRootDirectory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
debuggingPointer := flag.Bool("debug", false, "a boolean")
|
|
||||||
flag.Parse()
|
|
||||||
debugging = *debuggingPointer
|
|
||||||
devLog("\n\n\n\n\n\n\n\n\n\n")
|
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
|
devLog("\n\n\n\n\n\n\n\n\n\n")
|
||||||
|
flag.Parse()
|
||||||
|
if *versionFlag {
|
||||||
|
fmt.Printf("commit=%s, build date=%s, version=%s", commit, date, version)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
verifyInGitRepo()
|
verifyInGitRepo()
|
||||||
navigateToRepoRootDirectory()
|
navigateToRepoRootDirectory()
|
||||||
run()
|
for {
|
||||||
|
if err := run(); err != nil {
|
||||||
|
if err == gocui.ErrQuit {
|
||||||
|
break
|
||||||
|
} else if err == ErrSubprocess {
|
||||||
|
subprocess.Run()
|
||||||
|
} else {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# For testing subprocesses that require input
|
||||||
|
# Ask the user for login details
|
||||||
|
read -p 'Username: ' user
|
||||||
|
read -sp 'Password: ' pass
|
||||||
|
echo
|
||||||
|
echo Hello $user
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2014 tcnksm
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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 OR COPYRIGHT HOLDERS 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.
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
// Package gitconfig enables you to use `~/.gitconfig` values in Golang.
|
||||||
|
//
|
||||||
|
// For a full guide visit http://github.com/tcnksm/go-gitconfig
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/tcnksm/go-gitconfig"
|
||||||
|
// "fmt"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// user, err := gitconfig.Global("user.name")
|
||||||
|
// if err == nil {
|
||||||
|
// fmt.Println(user)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
package gitconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Entire extracts configuration value from `$HOME/.gitconfig` file ,
|
||||||
|
// `$GIT_CONFIG`, /etc/gitconfig or include.path files.
|
||||||
|
func Entire(key string) (string, error) {
|
||||||
|
return execGitConfig(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global extracts configuration value from `$HOME/.gitconfig` file or `$GIT_CONFIG`.
|
||||||
|
func Global(key string) (string, error) {
|
||||||
|
return execGitConfig("--global", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local extracts configuration value from current project repository.
|
||||||
|
func Local(key string) (string, error) {
|
||||||
|
return execGitConfig("--local", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GithubUser extracts github.user name from `Entire gitconfig`
|
||||||
|
// This is same as Entire("github.user")
|
||||||
|
func GithubUser() (string, error) {
|
||||||
|
return Entire("github.user")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Username extracts git user name from `Entire gitconfig`.
|
||||||
|
// This is same as Entire("user.name")
|
||||||
|
func Username() (string, error) {
|
||||||
|
return Entire("user.name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email extracts git user email from `$HOME/.gitconfig` file or `$GIT_CONFIG`.
|
||||||
|
// This is same as Global("user.email")
|
||||||
|
func Email() (string, error) {
|
||||||
|
return Entire("user.email")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginURL extract remote origin url from current project repository.
|
||||||
|
// This is same as Local("remote.origin.url")
|
||||||
|
func OriginURL() (string, error) {
|
||||||
|
return Local("remote.origin.url")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository extract repository name of current project repository.
|
||||||
|
func Repository() (string, error) {
|
||||||
|
url, err := OriginURL()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := retrieveRepoName(url)
|
||||||
|
return repo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Github extracts github token from `Entire gitconfig`.
|
||||||
|
// This is same as Entire("github.token")
|
||||||
|
func GithubToken() (string, error) {
|
||||||
|
return Entire("github.token")
|
||||||
|
}
|
||||||
|
|
||||||
|
func execGitConfig(args ...string) (string, error) {
|
||||||
|
gitArgs := append([]string{"config", "--get", "--null"}, args...)
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd := exec.Command("git", gitArgs...)
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = ioutil.Discard
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
||||||
|
if waitStatus.ExitStatus() == 1 {
|
||||||
|
return "", fmt.Errorf("the key `%s` is not found", args[len(args)-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimRight(stdout.String(), "\000"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var RepoNameRegexp = regexp.MustCompile(`.+/([^/]+)(\.git)?$`)
|
||||||
|
|
||||||
|
func retrieveRepoName(url string) string {
|
||||||
|
matched := RepoNameRegexp.FindStringSubmatch(url)
|
||||||
|
return strings.TrimSuffix(matched[1], ".git")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user