1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-25 12:24:47 +02:00

Merge pull request #13 from jesseduffield/master

Updated to latest master
This commit is contained in:
Mark Kopenga 2018-10-17 20:22:41 +02:00 committed by GitHub
commit 1d733f3adc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1822 additions and 509 deletions

View File

@ -3,7 +3,6 @@ jobs:
build: build:
docker: docker:
- image: circleci/golang:1.11 - image: circleci/golang:1.11
working_directory: /go/src/github.com/jesseduffield/lazygit working_directory: /go/src/github.com/jesseduffield/lazygit
steps: steps:
- checkout - checkout
@ -29,7 +28,7 @@ jobs:
fi fi
- restore_cache: - restore_cache:
keys: keys:
- pkg-cache-{{ checksum "Gopkg.lock" }}-v2 - pkg-cache-{{ checksum "Gopkg.lock" }}-v3
- run: - run:
name: Run tests name: Run tests
command: | command: |
@ -44,7 +43,7 @@ jobs:
command: | command: |
bash <(curl -s https://codecov.io/bash) bash <(curl -s https://codecov.io/bash)
- save_cache: - save_cache:
key: pkg-cache-{{ checksum "Gopkg.lock" }}-v2 key: pkg-cache-{{ checksum "Gopkg.lock" }}-v3
paths: paths:
- ~/.cache/go-build - ~/.cache/go-build

20
Gopkg.lock generated
View File

@ -189,11 +189,19 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:71e6c15797951d3fabaa944d70253e36a6cee96bf54ca0bc43ca3de3b4814bbb" digest = "1:66bb9b4a5abb704642fccba52a84a7f7feef2d9623f87b700e52a6695044723f"
name = "github.com/jesseduffield/gocui" name = "github.com/jesseduffield/gocui"
packages = ["."] packages = ["."]
pruneopts = "NUT" pruneopts = "NUT"
revision = "2cb6e95bbbf850bb32cc1799e07d08ff0f144746" revision = "03e26ff3f1de2c1bc2205113c3aba661312eee00"
[[projects]]
branch = "master"
digest = "1:3ab130f65766f5b7cc944d557df31c6a007ec017151705ec1e1b8719f2689021"
name = "github.com/jesseduffield/termbox-go"
packages = ["."]
pruneopts = "NUT"
revision = "1e272ff78dcb4c448870f464fda1cdcf2bf0b3dd"
[[projects]] [[projects]]
digest = "1:ac6d01547ec4f7f673311b4663909269bfb8249952de3279799289467837c3cc" digest = "1:ac6d01547ec4f7f673311b4663909269bfb8249952de3279799289467837c3cc"
@ -294,14 +302,6 @@
revision = "a16b91a3ba80db3a2301c70d1d302d42251c9079" revision = "a16b91a3ba80db3a2301c70d1d302d42251c9079"
version = "v2.0.0-beta.5" version = "v2.0.0-beta.5"
[[projects]]
branch = "master"
digest = "1:34d9354c2c5d916c05864327553047df59fc10e86ff1f408e4136eba0a25a5ec"
name = "github.com/nsf/termbox-go"
packages = ["."]
pruneopts = "NUT"
revision = "5c94acc5e6eb520f1bcd183974e01171cc4c23b3"
[[projects]] [[projects]]
digest = "1:cf254277d898b713195cc6b4a3fac8bf738b9f1121625df27843b52b267eec6c" digest = "1:cf254277d898b713195cc6b4a3fac8bf738b9f1121625df27843b52b267eec6c"
name = "github.com/pelletier/go-buffruneio" name = "github.com/pelletier/go-buffruneio"

View File

@ -121,6 +121,11 @@ For contributor discussion about things not better discussed here in the repo, j
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazygit/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M) [![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazygit/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M)
## Donate
If you would like to support the development of lazygit, please donate
[![Donate](https://d1iczxrky3cnb2.cloudfront.net/button-medium-blue.png)](https://donorbox.org/lazygit)
## Work in progress ## Work in progress
This is still a work in progress so there's still bugs to iron out and as this This is still a work in progress so there's still bugs to iron out and as this
is my first project in Go the code could no doubt use an increase in quality, is my first project in Go the code could no doubt use an increase in quality,

4
go.mod
View File

@ -18,7 +18,8 @@ require (
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63
github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8 github.com/jesseduffield/gocui v0.0.0-20180921065632-03e26ff3f1de
github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55 github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55
@ -31,7 +32,6 @@ require (
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699
github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80 github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80
github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb
github.com/pelletier/go-buffruneio v0.2.0 github.com/pelletier/go-buffruneio v0.2.0
github.com/pelletier/go-toml v1.2.0 github.com/pelletier/go-toml v1.2.0
github.com/pkg/errors v0.8.0 github.com/pkg/errors v0.8.0

54
go.sum
View File

@ -1,38 +1,22 @@
github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.0/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 h1:STLvc6RrpycslC1NRtTvt/YSgDkIGCTrB9K9vE5R2oQ=
github.com/aws/aws-sdk-go v1.15.21/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= 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 h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= 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 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo= github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 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 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ini/ini v1.38.2 h1:6Hl/z3p3iFkA0dlDfzYxuFuUGD+kaweypF6btsR2/Q4= github.com/go-ini/ini v1.38.2 h1:6Hl/z3p3iFkA0dlDfzYxuFuUGD+kaweypF6btsR2/Q4=
github.com/go-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 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 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= 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.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/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 h1:URgjUo+bs1KwatoNbwG0uCO4dHN4r1jsp4a5AGgHRjo=
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 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:MFPzqpPED05pFyGjNPJEC2sXM6EHTzFyvX+0s0JoZ48=
@ -45,18 +29,16 @@ github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGE
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= 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 h1:qio0y/sQdhbHRA3cmgczo04MaSV2zw+n46G1owvgWIk=
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331/go.mod h1:BT+PgT529opmb6mcUY+Fg0IwVRRmwqFyavEMU17GnBg= github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331/go.mod h1:BT+PgT529opmb6mcUY+Fg0IwVRRmwqFyavEMU17GnBg=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 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:Nrr/yUxNjXWYK0B3IqcFlYh1ICnesJDB4ogcfOVc5Ns=
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63/go.mod h1:fNqjRf+4XnTo2PrGN1JRb79b/BeoHwP4lU00f39SQY0= github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63/go.mod h1:fNqjRf+4XnTo2PrGN1JRb79b/BeoHwP4lU00f39SQY0=
github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8 h1:YMYnpIu5HUJfx/yfIwnZhFrgWTg51FQWtvZi+PMzQm8= github.com/jesseduffield/gocui v0.0.0-20180919095827-4fca348422d8 h1:XxX+IqNOFDh1PnU4eZDzUomoKbuKCvwyEm5an/IxLQU=
github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw= github.com/jesseduffield/gocui v0.0.0-20180919095827-4fca348422d8/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
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 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+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 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= 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 h1:S38dC4mEwxdw/U41+97VWdbun8mTcTjwg5Ujfg8QPME=
@ -77,22 +59,16 @@ 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/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 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI=
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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 v0.0.0-20180803040939-a16b91a3ba80/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4=
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5 h1:/TjjTS4kg7vC+05gD0LE4+97f/+PRFICnK/7wJPk7kE= github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5 h1:/TjjTS4kg7vC+05gD0LE4+97f/+PRFICnK/7wJPk7kE=
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU= github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU=
github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb h1:YahEjAGkJtCrkqgVHhX6n8ZX+CZ3hDRL9fjLYugLfSs=
github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA= 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-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 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 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 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.0/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/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 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@ -100,10 +76,6 @@ github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= 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 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf h1:6V1qxN6Usn4jy8unvggSJz/NC790tefw8Zdy6OZS5co=
github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I= github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I=
github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
@ -118,7 +90,6 @@ 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/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 h1:2BEDr8r0I0b8h/fOqwtxCEiq2HJu8n2JGZJQFGXWLjg=
github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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 h1:jysxIKov/4GJ33wI2aRvuIK7yBwB28E5almlgDLPRpM=
github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea/go.mod h1:Ffmqrj3nXIMIjeA4uW3Qjj0Ud9eDoTG0fu4JxyAr/tE= github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea/go.mod h1:Ffmqrj3nXIMIjeA4uW3Qjj0Ud9eDoTG0fu4JxyAr/tE=
@ -132,31 +103,16 @@ golang.org/x/crypto v0.0.0-20180808211826-de0752318171 h1:vYogbvSFj2YXcjQxFHu/rA
golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0 h1:8H8QZJ30plJyIVj60H3lr8TZGIq2Fh3Cyrs/ZNg1foU= 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/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.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
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/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/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/ini.v1 v1.38.2 h1:dGcbywv4RufeGeiMycPT/plKB5FtmLKLnWKwBiLhUA4=
gopkg.in/ini.v1 v1.38.2/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 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-billy.v4 v4.2.0/go.mod h1:ZHSF0JP+7oD97194otDUCD7Ofbk63+xFcfWP5bT6h+Q=
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs=
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/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 h1:+wM2BGgQ1znCKBexOB4OrGVSDw8mtKNUSq3wqxZhi/k=
gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo= 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 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=

View File

@ -77,11 +77,11 @@ func Setup(config config.AppConfigurer) (*App, error) {
app.Tr = i18n.NewLocalizer(app.Log) app.Tr = i18n.NewLocalizer(app.Log)
app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr) app.Updater, err = updates.NewUpdater(app.Log, config, app.OSCommand, app.Tr)
if err != nil { if err != nil {
return app, err return app, err
} }
app.Updater, err = updates.NewUpdater(app.Log, config, app.OSCommand, app.Tr) app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr)
if err != nil { if err != nil {
return app, err return app, err
} }

View File

@ -14,9 +14,9 @@ type Branch struct {
Recency string Recency string
} }
// GetDisplayString returns the dispaly string of branch // GetDisplayStrings returns the dispaly string of branch
func (b *Branch) GetDisplayString() string { func (b *Branch) GetDisplayStrings() []string {
return utils.WithPadding(b.Recency, 4) + utils.ColoredString(b.Name, b.GetColor()) return []string{b.Recency, utils.ColoredString(b.Name, b.GetColor())}
} }
// GetColor branch color // GetColor branch color

30
pkg/commands/commit.go Normal file
View File

@ -0,0 +1,30 @@
package commands
import (
"github.com/fatih/color"
)
// Commit : A git commit
type Commit struct {
Sha string
Name string
Pushed bool
Merged bool
DisplayString string
}
func (c *Commit) GetDisplayStrings() []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgGreen)
green := color.New(color.FgYellow)
white := color.New(color.FgWhite)
shaColor := yellow
if c.Pushed {
shaColor = red
} else if !c.Merged {
shaColor = green
}
return []string{shaColor.Sprint(c.Sha), white.Sprint(c.Name)}
}

36
pkg/commands/file.go Normal file
View File

@ -0,0 +1,36 @@
package commands
import "github.com/fatih/color"
// File : A file from git status
// duplicating this for now
type File struct {
Name string
HasStagedChanges bool
HasUnstagedChanges bool
Tracked bool
Deleted bool
HasMergeConflicts bool
DisplayString string
Type string // one of 'file', 'directory', and 'other'
}
// GetDisplayStrings returns the display string of a file
func (f *File) GetDisplayStrings() []string {
// potentially inefficient to be instantiating these color
// objects with each render
red := color.New(color.FgRed)
green := color.New(color.FgGreen)
if !f.Tracked && !f.HasStagedChanges {
return []string{red.Sprint(f.DisplayString)}
}
output := green.Sprint(f.DisplayString[0:1])
output += red.Sprint(f.DisplayString[1:3])
if f.HasUnstagedChanges {
output += red.Sprint(f.Name)
} else {
output += green.Sprint(f.Name)
}
return []string{output}
}

View File

@ -65,6 +65,7 @@ type GitCommand struct {
Tr *i18n.Localizer Tr *i18n.Localizer
getGlobalGitConfig func(string) (string, error) getGlobalGitConfig func(string) (string, error)
getLocalGitConfig func(string) (string, error) getLocalGitConfig func(string) (string, error)
removeFile func(string) error
} }
// NewGitCommand it runs git commands // NewGitCommand it runs git commands
@ -100,21 +101,22 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer)
Repo: repo, Repo: repo,
getGlobalGitConfig: gitconfig.Global, getGlobalGitConfig: gitconfig.Global,
getLocalGitConfig: gitconfig.Local, getLocalGitConfig: gitconfig.Local,
removeFile: os.RemoveAll,
}, nil }, nil
} }
// GetStashEntries stash entryies // GetStashEntries stash entryies
func (c *GitCommand) GetStashEntries() []StashEntry { func (c *GitCommand) GetStashEntries() []*StashEntry {
rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'") rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'")
stashEntries := []StashEntry{} stashEntries := []*StashEntry{}
for i, line := range utils.SplitLines(rawString) { for i, line := range utils.SplitLines(rawString) {
stashEntries = append(stashEntries, stashEntryFromLine(line, i)) stashEntries = append(stashEntries, stashEntryFromLine(line, i))
} }
return stashEntries return stashEntries
} }
func stashEntryFromLine(line string, index int) StashEntry { func stashEntryFromLine(line string, index int) *StashEntry {
return StashEntry{ return &StashEntry{
Name: line, Name: line,
Index: index, Index: index,
DisplayString: line, DisplayString: line,
@ -127,10 +129,10 @@ func (c *GitCommand) GetStashEntryDiff(index int) (string, error) {
} }
// GetStatusFiles git status files // GetStatusFiles git status files
func (c *GitCommand) GetStatusFiles() []File { func (c *GitCommand) GetStatusFiles() []*File {
statusOutput, _ := c.GitStatus() statusOutput, _ := c.GitStatus()
statusStrings := utils.SplitLines(statusOutput) statusStrings := utils.SplitLines(statusOutput)
files := []File{} files := []*File{}
for _, statusString := range statusStrings { for _, statusString := range statusStrings {
change := statusString[0:2] change := statusString[0:2]
@ -140,7 +142,7 @@ func (c *GitCommand) GetStatusFiles() []File {
_, untracked := map[string]bool{"??": true, "A ": true, "AM": true}[change] _, untracked := map[string]bool{"??": true, "A ": true, "AM": true}[change]
_, hasNoStagedChanges := map[string]bool{" ": true, "U": true, "?": true}[stagedChange] _, hasNoStagedChanges := map[string]bool{" ": true, "U": true, "?": true}[stagedChange]
file := File{ file := &File{
Name: filename, Name: filename,
DisplayString: statusString, DisplayString: statusString,
HasStagedChanges: !hasNoStagedChanges, HasStagedChanges: !hasNoStagedChanges,
@ -168,7 +170,7 @@ func (c *GitCommand) StashSave(message string) error {
} }
// MergeStatusFiles merge status files // MergeStatusFiles merge status files
func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File { func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []*File) []*File {
if len(oldFiles) == 0 { if len(oldFiles) == 0 {
return newFiles return newFiles
} }
@ -176,7 +178,7 @@ func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File {
appendedIndexes := []int{} appendedIndexes := []int{}
// retain position of files we already could see // retain position of files we already could see
result := []File{} result := []*File{}
for _, oldFile := range oldFiles { for _, oldFile := range oldFiles {
for newIndex, newFile := range newFiles { for newIndex, newFile := range newFiles {
if oldFile.Name == newFile.Name { if oldFile.Name == newFile.Name {
@ -219,11 +221,11 @@ func (c *GitCommand) ResetHard() error {
// UpstreamDifferenceCount checks how many pushables/pullables there are for the // UpstreamDifferenceCount checks how many pushables/pullables there are for the
// current branch // current branch
func (c *GitCommand) UpstreamDifferenceCount() (string, string) { func (c *GitCommand) UpstreamDifferenceCount() (string, string) {
pushableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --count") pushableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --count")
if err != nil { if err != nil {
return "?", "?" return "?", "?"
} }
pullableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list head..@{u} --count") pullableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list HEAD..@{u} --count")
if err != nil { if err != nil {
return "?", "?" return "?", "?"
} }
@ -231,13 +233,18 @@ func (c *GitCommand) UpstreamDifferenceCount() (string, string) {
} }
// GetCommitsToPush Returns the sha's of the commits that have not yet been pushed // GetCommitsToPush Returns the sha's of the commits that have not yet been pushed
// to the remote branch of the current branch // to the remote branch of the current branch, a map is returned to ease look up
func (c *GitCommand) GetCommitsToPush() []string { func (c *GitCommand) GetCommitsToPush() map[string]bool {
pushables, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit") pushables := map[string]bool{}
o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --abbrev-commit")
if err != nil { if err != nil {
return []string{} return pushables
} }
return utils.SplitLines(pushables) for _, p := range utils.SplitLines(o) {
pushables[p] = true
}
return pushables
} }
// RenameCommit renames the topmost commit with the given name // RenameCommit renames the topmost commit with the given name
@ -260,6 +267,14 @@ func (c *GitCommand) NewBranch(name string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -b %s", name)) return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -b %s", name))
} }
func (c *GitCommand) CurrentBranchName() (string, error) {
output, err := c.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD")
if err != nil {
return "", err
}
return utils.TrimTrailingNewline(output), nil
}
// DeleteBranch delete branch // DeleteBranch delete branch
func (c *GitCommand) DeleteBranch(branch string, force bool) error { func (c *GitCommand) DeleteBranch(branch string, force bool) error {
command := "git branch -d" command := "git branch -d"
@ -299,8 +314,12 @@ func (c *GitCommand) usingGpg() bool {
} }
// Commit commits to git // Commit commits to git
func (c *GitCommand) Commit(message string) (*exec.Cmd, error) { func (c *GitCommand) Commit(message string, amend bool) (*exec.Cmd, error) {
command := fmt.Sprintf("git commit -m %s", c.OSCommand.Quote(message)) amendParam := ""
if amend {
amendParam = " --amend"
}
command := fmt.Sprintf("git commit%s -m %s", amendParam, c.OSCommand.Quote(message))
if c.usingGpg() { if c.usingGpg() {
return c.OSCommand.PrepareSubProcess(c.OSCommand.Platform.shell, c.OSCommand.Platform.shellArg, command), nil return c.OSCommand.PrepareSubProcess(c.OSCommand.Platform.shell, c.OSCommand.Platform.shellArg, command), nil
} }
@ -365,12 +384,12 @@ func (c *GitCommand) SquashFixupCommit(branchName string, shaValue string) error
// CatFile obtains the content of a file // CatFile obtains the content of a file
func (c *GitCommand) CatFile(fileName string) (string, error) { func (c *GitCommand) CatFile(fileName string) (string, error) {
return c.OSCommand.RunCommandWithOutput("cat " + c.OSCommand.Quote(fileName)) return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("cat %s", c.OSCommand.Quote(fileName)))
} }
// StageFile stages a file // StageFile stages a file
func (c *GitCommand) StageFile(fileName string) error { func (c *GitCommand) StageFile(fileName string) error {
return c.OSCommand.RunCommand("git add " + c.OSCommand.Quote(fileName)) return c.OSCommand.RunCommand(fmt.Sprintf("git add %s", c.OSCommand.Quote(fileName)))
} }
// StageAll stages all files // StageAll stages all files
@ -385,13 +404,11 @@ func (c *GitCommand) UnstageAll() error {
// UnStageFile unstages a file // UnStageFile unstages a file
func (c *GitCommand) UnStageFile(fileName string, tracked bool) error { func (c *GitCommand) UnStageFile(fileName string, tracked bool) error {
var command string command := "git rm --cached %s"
if tracked { if tracked {
command = "git reset HEAD " command = "git reset HEAD %s"
} else {
command = "git rm --cached "
} }
return c.OSCommand.RunCommand(command + c.OSCommand.Quote(fileName)) return c.OSCommand.RunCommand(fmt.Sprintf(command, c.OSCommand.Quote(fileName)))
} }
// GitStatus returns the plaintext short status of the repo // GitStatus returns the plaintext short status of the repo
@ -409,18 +426,18 @@ func (c *GitCommand) IsInMergeState() (bool, error) {
} }
// RemoveFile directly // RemoveFile directly
func (c *GitCommand) RemoveFile(file File) error { func (c *GitCommand) RemoveFile(file *File) error {
// if the file isn't tracked, we assume you want to delete it // if the file isn't tracked, we assume you want to delete it
if file.HasStagedChanges { if file.HasStagedChanges {
if err := c.OSCommand.RunCommand("git reset -- " + file.Name); err != nil { if err := c.OSCommand.RunCommand(fmt.Sprintf("git reset -- %s", file.Name)); err != nil {
return err return err
} }
} }
if !file.Tracked { if !file.Tracked {
return os.RemoveAll(file.Name) return c.removeFile(file.Name)
} }
// if the file is tracked, we assume you want to just check it out // if the file is tracked, we assume you want to just check it out
return c.OSCommand.RunCommand("git checkout -- " + file.Name) return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -- %s", file.Name))
} }
// Checkout checks out a branch, with --force if you set the force arg to true // Checkout checks out a branch, with --force if you set the force arg to true
@ -429,7 +446,7 @@ func (c *GitCommand) Checkout(branch string, force bool) error {
if force { if force {
forceArg = "--force " forceArg = "--force "
} }
return c.OSCommand.RunCommand("git checkout " + forceArg + branch) return c.OSCommand.RunCommand(fmt.Sprintf("git checkout %s %s", forceArg, branch))
} }
// AddPatch prepares a subprocess for adding a patch by patch // AddPatch prepares a subprocess for adding a patch by patch
@ -452,37 +469,66 @@ func (c *GitCommand) PrepareCommitAmendSubProcess() *exec.Cmd {
// Currently it limits the result to 100 commits, but when we get async stuff // Currently it limits the result to 100 commits, but when we get async stuff
// working we can do lazy loading // working we can do lazy loading
func (c *GitCommand) GetBranchGraph(branchName string) (string, error) { func (c *GitCommand) GetBranchGraph(branchName string) (string, error) {
return c.OSCommand.RunCommandWithOutput("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 " + branchName) return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 %s", branchName))
} }
func includesString(list []string, a string) bool { func (c *GitCommand) getMergeBase() (string, error) {
for _, b := range list { currentBranch, err := c.CurrentBranchName()
if b == a { if err != nil {
return true return "", err
}
} }
return false
baseBranch := "master"
if strings.HasPrefix(currentBranch, "feature/") {
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
c.Log.Error(err)
}
return output, nil
} }
// GetCommits obtains the commits of the current branch // GetCommits obtains the commits of the current branch
func (c *GitCommand) GetCommits() []Commit { func (c *GitCommand) GetCommits() ([]*Commit, error) {
pushables := c.GetCommitsToPush() pushables := c.GetCommitsToPush()
log := c.GetLog() log := c.GetLog()
commits := []Commit{}
// now we can split it up and turn it into commits
lines := utils.SplitLines(log) lines := utils.SplitLines(log)
for _, line := range lines { commits := make([]*Commit, len(lines))
// now we can split it up and turn it into commits
for i, line := range lines {
splitLine := strings.Split(line, " ") splitLine := strings.Split(line, " ")
sha := splitLine[0] sha := splitLine[0]
pushed := includesString(pushables, sha) _, pushed := pushables[sha]
commits = append(commits, Commit{ commits[i] = &Commit{
Sha: sha, Sha: sha,
Name: strings.Join(splitLine[1:], " "), Name: strings.Join(splitLine[1:], " "),
Pushed: pushed, Pushed: pushed,
DisplayString: strings.Join(splitLine, " "), DisplayString: strings.Join(splitLine, " "),
}) }
} }
return commits return c.setCommitMergedStatuses(commits)
}
func (c *GitCommand) setCommitMergedStatuses(commits []*Commit) ([]*Commit, error) {
ancestor, err := c.getMergeBase()
if err != nil {
return nil, err
}
if ancestor == "" {
return commits, nil
}
passedAncestor := false
for i, commit := range commits {
if strings.HasPrefix(ancestor, commit.Sha) {
passedAncestor = true
}
commits[i].Merged = passedAncestor
}
return commits, nil
} }
// GetLog gets the git log (currently limited to 30 commits for performance // GetLog gets the git log (currently limited to 30 commits for performance
@ -495,6 +541,7 @@ func (c *GitCommand) GetLog() string {
// assume if there is an error there are no commits yet for this branch // assume if there is an error there are no commits yet for this branch
return "" return ""
} }
return result return result
} }
@ -504,26 +551,22 @@ func (c *GitCommand) Ignore(filename string) error {
} }
// Show shows the diff of a commit // Show shows the diff of a commit
func (c *GitCommand) Show(sha string) string { func (c *GitCommand) Show(sha string) (string, error) {
result, err := c.OSCommand.RunCommandWithOutput("git show --color " + sha) return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git show --color %s", sha))
if err != nil {
panic(err)
}
return result
} }
// Diff returns the diff of a file // Diff returns the diff of a file
func (c *GitCommand) Diff(file File) string { func (c *GitCommand) Diff(file *File) string {
cachedArg := "" cachedArg := ""
trackedArg := "--"
fileName := c.OSCommand.Quote(file.Name) fileName := c.OSCommand.Quote(file.Name)
if file.HasStagedChanges && !file.HasUnstagedChanges { if file.HasStagedChanges && !file.HasUnstagedChanges {
cachedArg = "--cached" cachedArg = "--cached"
} }
trackedArg := "--"
if !file.Tracked && !file.HasStagedChanges { if !file.Tracked && !file.HasStagedChanges {
trackedArg = "--no-index /dev/null" trackedArg = "--no-index /dev/null"
} }
command := fmt.Sprintf("%s %s %s %s", "git diff --color ", cachedArg, trackedArg, fileName) command := fmt.Sprintf("git diff --color %s %s %s", cachedArg, trackedArg, fileName)
// for now we assume an error means the file was deleted // for now we assume an error means the file was deleted
s, _ := c.OSCommand.RunCommandWithOutput(command) s, _ := c.OSCommand.RunCommandWithOutput(command)

View File

@ -1,32 +1,5 @@
package commands package commands
// File : A staged/unstaged file
type File struct {
Name string
HasStagedChanges bool
HasUnstagedChanges bool
Tracked bool
Deleted bool
HasMergeConflicts bool
DisplayString string
Type string // one of 'file', 'directory', and 'other'
}
// Commit : A git commit
type Commit struct {
Sha string
Name string
Pushed bool
DisplayString string
}
// StashEntry : A git stash entry
type StashEntry struct {
Index int
Name string
DisplayString string
}
// Conflict : A git conflict with a start middle and end corresponding to line // Conflict : A git conflict with a start middle and end corresponding to line
// numbers in the file where the conflict bars appear // numbers in the file where the conflict bars appear
type Conflict struct { type Conflict struct {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
package commands
// StashEntry : A git stash entry
type StashEntry struct {
Index int
Name string
DisplayString string
}
// GetDisplayStrings returns the dispaly string of branch
func (s *StashEntry) GetDisplayStrings() []string {
return []string{s.DisplayString}
}

View File

@ -34,7 +34,7 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand) (*
}, nil }, nil
} }
func (b *BranchListBuilder) obtainCurrentBranch() commands.Branch { func (b *BranchListBuilder) obtainCurrentBranch() *commands.Branch {
// I used go-git for this, but that breaks if you've just done a git init, // I used go-git for this, but that breaks if you've just done a git init,
// even though you're on 'master' // even though you're on 'master'
branchName, err := b.GitCommand.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD") branchName, err := b.GitCommand.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD")
@ -44,11 +44,11 @@ func (b *BranchListBuilder) obtainCurrentBranch() commands.Branch {
panic(err.Error()) panic(err.Error())
} }
} }
return commands.Branch{Name: strings.TrimSpace(branchName), Recency: " *"} return &commands.Branch{Name: strings.TrimSpace(branchName), Recency: " *"}
} }
func (b *BranchListBuilder) obtainReflogBranches() []commands.Branch { func (b *BranchListBuilder) obtainReflogBranches() []*commands.Branch {
branches := make([]commands.Branch, 0) branches := make([]*commands.Branch, 0)
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput("git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD") rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput("git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD")
if err != nil { if err != nil {
return branches return branches
@ -58,14 +58,14 @@ func (b *BranchListBuilder) obtainReflogBranches() []commands.Branch {
for _, line := range branchLines { for _, line := range branchLines {
timeNumber, timeUnit, branchName := branchInfoFromLine(line) timeNumber, timeUnit, branchName := branchInfoFromLine(line)
timeUnit = abbreviatedTimeUnit(timeUnit) timeUnit = abbreviatedTimeUnit(timeUnit)
branch := commands.Branch{Name: branchName, Recency: timeNumber + timeUnit} branch := &commands.Branch{Name: branchName, Recency: timeNumber + timeUnit}
branches = append(branches, branch) branches = append(branches, branch)
} }
return branches return branches
} }
func (b *BranchListBuilder) obtainSafeBranches() []commands.Branch { func (b *BranchListBuilder) obtainSafeBranches() []*commands.Branch {
branches := make([]commands.Branch, 0) branches := make([]*commands.Branch, 0)
bIter, err := b.GitCommand.Repo.Branches() bIter, err := b.GitCommand.Repo.Branches()
if err != nil { if err != nil {
@ -73,14 +73,14 @@ func (b *BranchListBuilder) obtainSafeBranches() []commands.Branch {
} }
err = bIter.ForEach(func(b *plumbing.Reference) error { err = bIter.ForEach(func(b *plumbing.Reference) error {
name := b.Name().Short() name := b.Name().Short()
branches = append(branches, commands.Branch{Name: name}) branches = append(branches, &commands.Branch{Name: name})
return nil return nil
}) })
return branches return branches
} }
func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []commands.Branch, included bool) []commands.Branch { func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []*commands.Branch, included bool) []*commands.Branch {
for _, newBranch := range newBranches { for _, newBranch := range newBranches {
if included == branchIncluded(newBranch.Name, existingBranches) { if included == branchIncluded(newBranch.Name, existingBranches) {
finalBranches = append(finalBranches, newBranch) finalBranches = append(finalBranches, newBranch)
@ -89,7 +89,7 @@ func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existi
return finalBranches return finalBranches
} }
func sanitisedReflogName(reflogBranch commands.Branch, safeBranches []commands.Branch) string { func sanitisedReflogName(reflogBranch *commands.Branch, safeBranches []*commands.Branch) string {
for _, safeBranch := range safeBranches { for _, safeBranch := range safeBranches {
if strings.ToLower(safeBranch.Name) == strings.ToLower(reflogBranch.Name) { if strings.ToLower(safeBranch.Name) == strings.ToLower(reflogBranch.Name) {
return safeBranch.Name return safeBranch.Name
@ -99,15 +99,15 @@ func sanitisedReflogName(reflogBranch commands.Branch, safeBranches []commands.B
} }
// Build the list of branches for the current repo // Build the list of branches for the current repo
func (b *BranchListBuilder) Build() []commands.Branch { func (b *BranchListBuilder) Build() []*commands.Branch {
branches := make([]commands.Branch, 0) branches := make([]*commands.Branch, 0)
head := b.obtainCurrentBranch() head := b.obtainCurrentBranch()
safeBranches := b.obtainSafeBranches() safeBranches := b.obtainSafeBranches()
if len(safeBranches) == 0 { if len(safeBranches) == 0 {
return append(branches, head) return append(branches, head)
} }
reflogBranches := b.obtainReflogBranches() reflogBranches := b.obtainReflogBranches()
reflogBranches = uniqueByName(append([]commands.Branch{head}, reflogBranches...)) reflogBranches = uniqueByName(append([]*commands.Branch{head}, reflogBranches...))
for i, reflogBranch := range reflogBranches { for i, reflogBranch := range reflogBranches {
reflogBranches[i].Name = sanitisedReflogName(reflogBranch, safeBranches) reflogBranches[i].Name = sanitisedReflogName(reflogBranch, safeBranches)
} }
@ -118,7 +118,7 @@ func (b *BranchListBuilder) Build() []commands.Branch {
return branches return branches
} }
func branchIncluded(branchName string, branches []commands.Branch) bool { func branchIncluded(branchName string, branches []*commands.Branch) bool {
for _, existingBranch := range branches { for _, existingBranch := range branches {
if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) { if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) {
return true return true
@ -127,8 +127,8 @@ func branchIncluded(branchName string, branches []commands.Branch) bool {
return false return false
} }
func uniqueByName(branches []commands.Branch) []commands.Branch { func uniqueByName(branches []*commands.Branch) []*commands.Branch {
finalBranches := make([]commands.Branch, 0) finalBranches := make([]*commands.Branch, 0)
for _, branch := range branches { for _, branch := range branches {
if branchIncluded(branch.Name, finalBranches) { if branchIncluded(branch.Name, finalBranches) {
continue continue

View File

@ -7,6 +7,7 @@ import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/git" "github.com/jesseduffield/lazygit/pkg/git"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
@ -109,7 +110,7 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
return nil return nil
} }
func (gui *Gui) getSelectedBranch(v *gocui.View) commands.Branch { func (gui *Gui) getSelectedBranch(v *gocui.View) *commands.Branch {
lineNumber := gui.getItemPosition(v) lineNumber := gui.getItemPosition(v)
return gui.State.Branches[lineNumber] return gui.State.Branches[lineNumber]
} }
@ -151,10 +152,15 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
return err return err
} }
gui.State.Branches = builder.Build() gui.State.Branches = builder.Build()
v.Clear() v.Clear()
for _, branch := range gui.State.Branches { list, err := utils.RenderList(gui.State.Branches)
fmt.Fprintln(v, branch.GetDisplayString()) if err != nil {
return err
} }
fmt.Fprint(v, list)
gui.resetOrigin(v) gui.resetOrigin(v)
return gui.refreshStatus(g) return gui.refreshStatus(g)
}) })

View File

@ -12,7 +12,7 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
if message == "" { if message == "" {
return gui.createErrorPanel(g, gui.Tr.SLocalize("CommitWithoutMessageErr")) return gui.createErrorPanel(g, gui.Tr.SLocalize("CommitWithoutMessageErr"))
} }
sub, err := gui.GitCommand.Commit(message) sub, err := gui.GitCommand.Commit(message, false)
if err != nil { if err != nil {
// TODO need to find a way to send through this error // TODO need to find a way to send through this error
if err != gui.Errors.ErrSubProcess { if err != gui.Errors.ErrSubProcess {
@ -37,6 +37,10 @@ func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error {
if _, err := g.SetViewOnTop("commitMessage"); err != nil {
return err
}
message := gui.Tr.TemplateLocalize( message := gui.Tr.TemplateLocalize(
"CloseConfirm", "CloseConfirm",
Teml{ Teml{

View File

@ -2,33 +2,34 @@ package gui
import ( import (
"errors" "errors"
"fmt"
"github.com/fatih/color"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) refreshCommits(g *gocui.Gui) error { func (gui *Gui) refreshCommits(g *gocui.Gui) error {
g.Update(func(*gocui.Gui) error { g.Update(func(*gocui.Gui) error {
gui.State.Commits = gui.GitCommand.GetCommits() commits, err := gui.GitCommand.GetCommits()
if err != nil {
return err
}
gui.State.Commits = commits
v, err := g.View("commits") v, err := g.View("commits")
if err != nil { if err != nil {
panic(err) return err
} }
v.Clear() v.Clear()
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow) list, err := utils.RenderList(gui.State.Commits)
white := color.New(color.FgWhite) if err != nil {
shaColor := white return err
for _, commit := range gui.State.Commits {
if commit.Pushed {
shaColor = red
} else {
shaColor = yellow
}
shaColor.Fprint(v, commit.Sha+" ")
white.Fprintln(v, commit.Name)
} }
fmt.Fprint(v, list)
gui.refreshStatus(g) gui.refreshStatus(g)
if g.CurrentView().Name() == "commits" { if g.CurrentView().Name() == "commits" {
gui.handleCommitSelect(g, v) gui.handleCommitSelect(g, v)
@ -73,7 +74,10 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
} }
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch")) return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
} }
commitText := gui.GitCommand.Show(commit.Sha) commitText, err := gui.GitCommand.Show(commit.Sha)
if err != nil {
return err
}
return gui.renderString(g, "main", commitText) return gui.renderString(g, "main", commitText)
} }
@ -99,7 +103,7 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
} }
// TODO: move to files panel // TODO: move to files panel
func (gui *Gui) anyUnStagedChanges(files []commands.File) bool { func (gui *Gui) anyUnStagedChanges(files []*commands.File) bool {
for _, file := range files { for _, file := range files {
if file.Tracked && file.HasUnstagedChanges { if file.Tracked && file.HasUnstagedChanges {
return true return true
@ -146,7 +150,6 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
} }
return gui.handleCommitSelect(g, v) return gui.handleCommitSelect(g, v)
}) })
return nil
} }
func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
@ -162,13 +165,13 @@ func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
return nil return nil
} }
func (gui *Gui) getSelectedCommit(g *gocui.Gui) (commands.Commit, error) { func (gui *Gui) getSelectedCommit(g *gocui.Gui) (*commands.Commit, error) {
v, err := g.View("commits") v, err := g.View("commits")
if err != nil { if err != nil {
panic(err) panic(err)
} }
if len(gui.State.Commits) == 0 { if len(gui.State.Commits) == 0 {
return commands.Commit{}, errors.New(gui.Tr.SLocalize("NoCommitsThisBranch")) return &commands.Commit{}, errors.New(gui.Tr.SLocalize("NoCommitsThisBranch"))
} }
lineNumber := gui.getItemPosition(v) lineNumber := gui.getItemPosition(v)
if lineNumber > len(gui.State.Commits)-1 { if lineNumber > len(gui.State.Commits)-1 {

View File

@ -7,16 +7,17 @@ import (
// "strings" // "strings"
"fmt"
"strings" "strings"
"github.com/fatih/color"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) stagedFiles() []commands.File { func (gui *Gui) stagedFiles() []*commands.File {
files := gui.State.Files files := gui.State.Files
result := make([]commands.File, 0) result := make([]*commands.File, 0)
for _, file := range files { for _, file := range files {
if file.HasStagedChanges { if file.HasStagedChanges {
result = append(result, file) result = append(result, file)
@ -25,9 +26,9 @@ func (gui *Gui) stagedFiles() []commands.File {
return result return result
} }
func (gui *Gui) trackedFiles() []commands.File { func (gui *Gui) trackedFiles() []*commands.File {
files := gui.State.Files files := gui.State.Files
result := make([]commands.File, 0) result := make([]*commands.File, 0)
for _, file := range files { for _, file := range files {
if file.Tracked { if file.Tracked {
result = append(result, file) result = append(result, file)
@ -116,9 +117,9 @@ func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error {
return gui.Errors.ErrSubProcess return gui.Errors.ErrSubProcess
} }
func (gui *Gui) getSelectedFile(g *gocui.Gui) (commands.File, error) { func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
if len(gui.State.Files) == 0 { if len(gui.State.Files) == 0 {
return commands.File{}, gui.Errors.ErrNoFiles return &commands.File{}, gui.Errors.ErrNoFiles
} }
filesView, err := g.View("files") filesView, err := g.View("files")
if err != nil { if err != nil {
@ -184,7 +185,9 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles")) gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
return gui.renderfilesOptions(g, nil) return gui.renderfilesOptions(g, nil)
} }
gui.renderfilesOptions(g, &file) if err := gui.renderfilesOptions(g, file); err != nil {
return err
}
var content string var content string
if file.HasMergeConflicts { if file.HasMergeConflicts {
return gui.refreshMergePanel(g) return gui.refreshMergePanel(g)
@ -208,6 +211,28 @@ func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
return nil return nil
} }
func (gui *Gui) handleAmendCommitPress(g *gocui.Gui, filesView *gocui.View) error {
if len(gui.stagedFiles()) == 0 && !gui.State.HasMergeConflicts {
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit"))
}
title := strings.Title(gui.Tr.SLocalize("AmendLastCommit"))
question := gui.Tr.SLocalize("SureToAmend")
if len(gui.State.Commits) == 0 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoCommitToAmend"))
}
return gui.createConfirmationPanel(g, filesView, title, question, func(g *gocui.Gui, v *gocui.View) error {
lastCommitMsg := gui.State.Commits[0].Name
_, err := gui.GitCommand.Commit(lastCommitMsg, true)
if err != nil {
return gui.createErrorPanel(g, err.Error())
}
return gui.refreshSidePanels(g)
}, nil)
}
// handleCommitEditorPress - handle when the user wants to commit changes via // handleCommitEditorPress - handle when the user wants to commit changes via
// their editor rather than via the popup panel // their editor rather than via the popup panel
func (gui *Gui) handleCommitEditorPress(g *gocui.Gui, filesView *gocui.View) error { func (gui *Gui) handleCommitEditorPress(g *gocui.Gui, filesView *gocui.View) error {
@ -275,24 +300,6 @@ func (gui *Gui) updateHasMergeConflictStatus() error {
return nil return nil
} }
func (gui *Gui) renderFile(file commands.File, filesView *gocui.View) {
// potentially inefficient to be instantiating these color
// objects with each render
red := color.New(color.FgRed)
green := color.New(color.FgGreen)
if !file.Tracked && !file.HasStagedChanges {
red.Fprintln(filesView, file.DisplayString)
return
}
green.Fprint(filesView, file.DisplayString[0:1])
red.Fprint(filesView, file.DisplayString[1:3])
if file.HasUnstagedChanges {
red.Fprintln(filesView, file.Name)
} else {
green.Fprintln(filesView, file.Name)
}
}
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) { func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
item, err := gui.getSelectedFile(g) item, err := gui.getSelectedFile(g)
if err != nil { if err != nil {
@ -318,10 +325,14 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error {
return err return err
} }
gui.refreshStateFiles() gui.refreshStateFiles()
filesView.Clear() filesView.Clear()
for _, file := range gui.State.Files { list, err := utils.RenderList(gui.State.Files)
gui.renderFile(file, filesView) if err != nil {
return err
} }
fmt.Fprint(filesView, list)
gui.correctCursor(filesView) gui.correctCursor(filesView)
if filesView == g.CurrentView() { if filesView == g.CurrentView() {
gui.handleFileSelect(g, filesView) gui.handleFileSelect(g, filesView)

View File

@ -33,6 +33,7 @@ var OverlappingEdges = false
type SentinelErrors struct { type SentinelErrors struct {
ErrSubProcess error ErrSubProcess error
ErrNoFiles error ErrNoFiles error
ErrSwitchRepo error
} }
// GenerateSentinelErrors makes the sentinel errors for the gui. We're defining it here // GenerateSentinelErrors makes the sentinel errors for the gui. We're defining it here
@ -49,6 +50,7 @@ func (gui *Gui) GenerateSentinelErrors() {
gui.Errors = SentinelErrors{ gui.Errors = SentinelErrors{
ErrSubProcess: errors.New(gui.Tr.SLocalize("RunningSubprocess")), ErrSubProcess: errors.New(gui.Tr.SLocalize("RunningSubprocess")),
ErrNoFiles: errors.New(gui.Tr.SLocalize("NoChangedFiles")), ErrNoFiles: errors.New(gui.Tr.SLocalize("NoChangedFiles")),
ErrSwitchRepo: errors.New("switching repo"),
} }
} }
@ -71,10 +73,10 @@ type Gui struct {
} }
type guiState struct { type guiState struct {
Files []commands.File Files []*commands.File
Branches []commands.Branch Branches []*commands.Branch
Commits []commands.Commit Commits []*commands.Commit
StashEntries []commands.StashEntry StashEntries []*commands.StashEntry
PreviousView string PreviousView string
HasMergeConflicts bool HasMergeConflicts bool
ConflictIndex int ConflictIndex int
@ -83,17 +85,16 @@ type guiState struct {
EditHistory *stack.Stack EditHistory *stack.Stack
Platform commands.Platform Platform commands.Platform
Updating bool Updating bool
Keys []Binding
} }
// NewGui builds a new gui handler // NewGui builds a new gui handler
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) { func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) {
initialState := guiState{ initialState := guiState{
Files: make([]commands.File, 0), Files: make([]*commands.File, 0),
PreviousView: "files", PreviousView: "files",
Commits: make([]commands.Commit, 0), Commits: make([]*commands.Commit, 0),
StashEntries: make([]commands.StashEntry, 0), StashEntries: make([]*commands.StashEntry, 0),
ConflictIndex: 0, ConflictIndex: 0,
ConflictTop: true, ConflictTop: true,
Conflicts: make([]commands.Conflict, 0), Conflicts: make([]commands.Conflict, 0),
@ -293,6 +294,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
// these are only called once (it's a place to put all the things you want // 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) // to happen on startup after the screen is first rendered)
gui.Updater.CheckForNewUpdate(gui.onBackgroundUpdateCheckFinish, false) gui.Updater.CheckForNewUpdate(gui.onBackgroundUpdateCheckFinish, false)
if err := gui.updateRecentRepoList(); err != nil {
return err
}
gui.handleFileSelect(g, filesView) gui.handleFileSelect(g, filesView)
gui.refreshFiles(g) gui.refreshFiles(g)
gui.refreshBranches(g) gui.refreshBranches(g)
@ -401,6 +406,8 @@ func (gui *Gui) RunWithSubprocesses() {
if err := gui.Run(); err != nil { if err := gui.Run(); err != nil {
if err == gocui.ErrQuit { if err == gocui.ErrQuit {
break break
} else if err == gui.Errors.ErrSwitchRepo {
continue
} else if err == gui.Errors.ErrSubProcess { } else if err == gui.Errors.ErrSubProcess {
gui.SubProcess.Stdin = os.Stdin gui.SubProcess.Stdin = os.Stdin
gui.SubProcess.Stdout = os.Stdout gui.SubProcess.Stdout = os.Stdout

View File

@ -1,6 +1,8 @@
package gui package gui
import "github.com/jesseduffield/gocui" import (
"github.com/jesseduffield/gocui"
)
// Binding - a keybinding mapping a key and modifier to a handler. The keypress // 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 only handled if the given view has focus, or handled globally if the view
@ -14,8 +16,26 @@ type Binding struct {
Description string Description string
} }
func (gui *Gui) GetKeybindings() []Binding { // GetDisplayStrings returns the display string of a file
bindings := []Binding{ func (b *Binding) GetDisplayStrings() []string {
return []string{b.GetKey(), b.Description}
}
func (b *Binding) GetKey() string {
r, ok := b.Key.(rune)
key := ""
if ok {
key = string(r)
} else if b.KeyReadable != "" {
key = b.KeyReadable
}
return key
}
func (gui *Gui) GetKeybindings() []*Binding {
bindings := []*Binding{
{ {
ViewName: "", ViewName: "",
Key: 'q', Key: 'q',
@ -73,7 +93,7 @@ func (gui *Gui) GetKeybindings() []Binding {
ViewName: "", ViewName: "",
Key: 'x', Key: 'x',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleMenu, Handler: gui.handleCreateOptionsMenu,
}, { }, {
ViewName: "status", ViewName: "status",
Key: 'e', Key: 'e',
@ -93,11 +113,24 @@ func (gui *Gui) GetKeybindings() []Binding {
Handler: gui.handleCheckForUpdate, Handler: gui.handleCheckForUpdate,
Description: gui.Tr.SLocalize("checkForUpdate"), Description: gui.Tr.SLocalize("checkForUpdate"),
}, { }, {
ViewName: "status",
Key: 's',
Modifier: gocui.ModNone,
Handler: gui.handleCreateRecentReposMenu,
Description: gui.Tr.SLocalize("SwitchRepo"),
},
{
ViewName: "files", ViewName: "files",
Key: 'c', Key: 'c',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleCommitPress, Handler: gui.handleCommitPress,
Description: gui.Tr.SLocalize("CommitChanges"), Description: gui.Tr.SLocalize("CommitChanges"),
}, {
ViewName: "files",
Key: 'A',
Modifier: gocui.ModNone,
Handler: gui.handleAmendCommitPress,
Description: gui.Tr.SLocalize("AmendLastCommit"),
}, { }, {
ViewName: "files", ViewName: "files",
Key: 'C', Key: 'C',
@ -155,7 +188,7 @@ func (gui *Gui) GetKeybindings() []Binding {
Description: gui.Tr.SLocalize("stashFiles"), Description: gui.Tr.SLocalize("stashFiles"),
}, { }, {
ViewName: "files", ViewName: "files",
Key: 'A', Key: 'M',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleAbortMerge, Handler: gui.handleAbortMerge,
Description: gui.Tr.SLocalize("abortMerge"), Description: gui.Tr.SLocalize("abortMerge"),
@ -349,18 +382,13 @@ func (gui *Gui) GetKeybindings() []Binding {
Key: 'q', Key: 'q',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleMenuClose, Handler: gui.handleMenuClose,
}, {
ViewName: "menu",
Key: gocui.KeySpace,
Modifier: gocui.ModNone,
Handler: gui.handleMenuPress,
}, },
} }
// Would make these keybindings global but that interferes with editing // Would make these keybindings global but that interferes with editing
// input in the confirmation panel // input in the confirmation panel
for _, viewName := range []string{"status", "files", "branches", "commits", "stash", "menu"} { for _, viewName := range []string{"status", "files", "branches", "commits", "stash", "menu"} {
bindings = append(bindings, []Binding{ bindings = append(bindings, []*Binding{
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView}, {ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView}, {ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView}, {ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},

View File

@ -8,21 +8,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) handleMenuPress(g *gocui.Gui, v *gocui.View) error {
lineNumber := gui.getItemPosition(v)
if gui.State.Keys[lineNumber].Key == nil {
return nil
}
if len(gui.State.Keys) > lineNumber {
err := gui.handleMenuClose(g, v)
if err != nil {
return err
}
return gui.State.Keys[lineNumber].Handler(g, v)
}
return nil
}
func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error {
// doing nothing for now // doing nothing for now
// but it is needed for switch in newLineFocused // but it is needed for switch in newLineFocused
@ -39,9 +24,9 @@ func (gui *Gui) renderMenuOptions(g *gocui.Gui) error {
} }
func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
// better to delete because for example after closing update confirmation panel, if err := g.DeleteKeybinding("menu", gocui.KeySpace, gocui.ModNone); err != nil {
// the focus isn't set back to any of panels and one is unable to even quit return err
//_, err := g.SetViewOnBottom(v.Name()) }
err := g.DeleteView("menu") err := g.DeleteView("menu")
if err != nil { if err != nil {
return err return err
@ -49,83 +34,38 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
return gui.returnFocus(g, v) return gui.returnFocus(g, v)
} }
func (gui *Gui) GetKey(binding Binding) string { func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error {
r, ok := binding.Key.(rune) list, err := utils.RenderList(items)
key := "" if err != nil {
if ok {
key = string(r)
} else if binding.KeyReadable != "" {
key = binding.KeyReadable
}
return key
}
func (gui *Gui) GetMaxKeyLength(bindings []Binding) int {
max := 0
for _, binding := range bindings {
keyLength := len(gui.GetKey(binding))
if keyLength > max {
max = keyLength
}
}
return max
}
func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error {
var (
contentGlobal, contentPanel []string
bindingsGlobal, bindingsPanel []Binding
)
// clear keys slice, so we don't have ghost elements
gui.State.Keys = gui.State.Keys[:0]
bindings := gui.GetKeybindings()
padWidth := gui.GetMaxKeyLength(bindings)
for _, binding := range bindings {
key := gui.GetKey(binding)
if key != "" && binding.Description != "" {
content := fmt.Sprintf("%s %s", utils.WithPadding(key, padWidth), binding.Description)
switch binding.ViewName {
case "":
contentGlobal = append(contentGlobal, content)
bindingsGlobal = append(bindingsGlobal, binding)
case v.Name():
contentPanel = append(contentPanel, content)
bindingsPanel = append(bindingsPanel, binding)
}
}
}
// append dummy element to have a separator between
// panel and global keybindings
contentPanel = append(contentPanel, "")
bindingsPanel = append(bindingsPanel, Binding{})
content := append(contentPanel, contentGlobal...)
gui.State.Keys = append(bindingsPanel, bindingsGlobal...)
// append newline at the end so the last line would be selectable
contentJoined := strings.Join(content, "\n") + "\n"
// y1-1 so there will not be an extra space at the end of panel
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, contentJoined)
menuView, _ := g.SetView("menu", x0, y0, x1, y1-1, 0)
menuView.Title = strings.Title(gui.Tr.SLocalize("menu"))
menuView.FgColor = gocui.ColorWhite
if err := gui.renderMenuOptions(g); err != nil {
return err return err
} }
fmt.Fprint(menuView, contentJoined) x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, list)
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
menuView.Title = strings.Title(gui.Tr.SLocalize("menu"))
menuView.FgColor = gocui.ColorWhite
menuView.Clear()
fmt.Fprint(menuView, list)
g.Update(func(g *gocui.Gui) error { if err := gui.renderMenuOptions(gui.g); err != nil {
_, err := g.SetViewOnTop("menu") return err
if err != nil { }
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
lineNumber := gui.getItemPosition(v)
return handlePress(lineNumber)
}
if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil {
return err
}
gui.g.Update(func(g *gocui.Gui) error {
if _, err := g.SetViewOnTop("menu"); err != nil {
return err return err
} }
return gui.switchFocus(g, v, menuView) currentView := gui.g.CurrentView()
return gui.switchFocus(gui.g, currentView, menuView)
}) })
return nil return nil
} }

View File

@ -0,0 +1,51 @@
package gui
import (
"errors"
"github.com/jesseduffield/gocui"
)
func (gui *Gui) getBindings(v *gocui.View) []*Binding {
var (
bindingsGlobal, bindingsPanel []*Binding
)
bindings := gui.GetKeybindings()
for _, binding := range bindings {
if binding.GetKey() != "" && binding.Description != "" {
switch binding.ViewName {
case "":
bindingsGlobal = append(bindingsGlobal, binding)
case v.Name():
bindingsPanel = append(bindingsPanel, binding)
}
}
}
// append dummy element to have a separator between
// panel and global keybindings
bindingsPanel = append(bindingsPanel, &Binding{})
return append(bindingsPanel, bindingsGlobal...)
}
func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error {
bindings := gui.getBindings(v)
handleMenuPress := func(index int) error {
if bindings[index].Key == nil {
return nil
}
if index >= len(bindings) {
return errors.New("Index is greater than size of bindings")
}
err := gui.handleMenuClose(g, v)
if err != nil {
return err
}
return bindings[index].Handler(g, v)
}
return gui.createMenu(bindings, handleMenuPress)
}

View File

@ -0,0 +1,69 @@
package gui
import (
"os"
"path/filepath"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type recentRepo struct {
path string
}
func (r *recentRepo) GetDisplayStrings() []string {
yellow := color.New(color.FgMagenta)
base := filepath.Base(r.path)
path := yellow.Sprint(r.path)
return []string{base, path}
}
func (gui *Gui) handleCreateRecentReposMenu(g *gocui.Gui, v *gocui.View) error {
recentRepoPaths := gui.Config.GetAppState().RecentRepos
reposCount := utils.Min(len(recentRepoPaths), 20)
// we won't show the current repo hence the -1
recentRepos := make([]*recentRepo, reposCount-1)
for i, path := range recentRepoPaths[1:reposCount] {
recentRepos[i] = &recentRepo{path: path}
}
handleMenuPress := func(index int) error {
repo := recentRepos[index]
if err := os.Chdir(repo.path); err != nil {
return err
}
newGitCommand, err := commands.NewGitCommand(gui.Log, gui.OSCommand, gui.Tr)
if err != nil {
return err
}
gui.GitCommand = newGitCommand
return gui.Errors.ErrSwitchRepo
}
return gui.createMenu(recentRepos, handleMenuPress)
}
// updateRecentRepoList registers the fact that we opened lazygit in this repo,
// so that we can open the same repo via the 'recent repos' menu
func (gui *Gui) updateRecentRepoList() error {
recentRepos := gui.Config.GetAppState().RecentRepos
currentRepo, err := os.Getwd()
if err != nil {
return err
}
gui.Config.GetAppState().RecentRepos = newRecentReposList(recentRepos, currentRepo)
return gui.Config.SaveAppState()
}
func newRecentReposList(recentRepos []string, currentRepo string) []string {
newRepos := []string{currentRepo}
for _, repo := range recentRepos {
if repo != currentRepo {
newRepos = append(newRepos, repo)
}
}
return newRepos
}

View File

@ -5,6 +5,7 @@ import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) refreshStashEntries(g *gocui.Gui) error { func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
@ -14,10 +15,14 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
panic(err) panic(err)
} }
gui.State.StashEntries = gui.GitCommand.GetStashEntries() gui.State.StashEntries = gui.GitCommand.GetStashEntries()
v.Clear() v.Clear()
for _, stashEntry := range gui.State.StashEntries { list, err := utils.RenderList(gui.State.StashEntries)
fmt.Fprintln(v, stashEntry.DisplayString) if err != nil {
return err
} }
fmt.Fprint(v, list)
return gui.resetOrigin(v) return gui.resetOrigin(v)
}) })
return nil return nil
@ -29,7 +34,7 @@ func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry {
} }
stashView, _ := gui.g.View("stash") stashView, _ := gui.g.View("stash")
lineNumber := gui.getItemPosition(stashView) lineNumber := gui.getItemPosition(stashView)
return &gui.State.StashEntries[lineNumber] return gui.State.StashEntries[lineNumber]
} }
func (gui *Gui) renderStashOptions(g *gocui.Gui) error { func (gui *Gui) renderStashOptions(g *gocui.Gui) error {

View File

@ -2,6 +2,7 @@ package gui
import ( import (
"fmt" "fmt"
"strings"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
@ -51,14 +52,16 @@ func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
dashboardString := fmt.Sprintf( dashboardString := strings.Join(
"%s\n\n%s\n\n%s\n\n%s\n\n%s", []string{
lazygitTitle(), lazygitTitle(),
"Keybindings: https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md", "Copyright (c) 2018 Jesse Duffield",
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md", "Keybindings: https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md",
"Tutorial: https://www.youtube.com/watch?v=VDXvbHZYeKY", "Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues", "Tutorial: https://www.youtube.com/watch?v=VDXvbHZYeKY",
) "Raise an Issue: https://github.com/jesseduffield/lazygit/issues",
"Buy Jesse a coffee: https://donorbox.org/lazygit",
}, "\n\n")
if err := gui.renderString(g, "main", dashboardString); err != nil { if err := gui.renderString(g, "main", dashboardString); err != nil {
return err return err

View File

@ -133,8 +133,15 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
}, },
) )
gui.Log.Info(message) gui.Log.Info(message)
gui.State.PreviousView = oldView.Name()
// 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()
}
} }
newView.Highlight = true newView.Highlight = true
message := gui.Tr.TemplateLocalize( message := gui.Tr.TemplateLocalize(
"newFocusedViewIs", "newFocusedViewIs",
@ -160,7 +167,6 @@ func (gui *Gui) getItemPosition(v *gocui.View) int {
func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
// swallowing cursor movements in main // swallowing cursor movements in main
// TODO: pull this out
if v == nil || v.Name() == "main" { if v == nil || v.Name() == "main" {
return nil return nil
} }
@ -179,19 +185,28 @@ func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error {
// swallowing cursor movements in main // swallowing cursor movements in main
// TODO: pull this out
if v == nil || v.Name() == "main" { if v == nil || v.Name() == "main" {
return nil return nil
} }
cx, cy := v.Cursor() cx, cy := v.Cursor()
ox, oy := v.Origin() ox, oy := v.Origin()
if cy+oy >= len(v.BufferLines())-2 { ly := v.LinesHeight() - 1
_, height := v.Size()
maxY := height - 1
// if we are at the end we just return
if cy+oy == ly {
return nil return nil
} }
if err := v.SetCursor(cx, cy+1); err != nil {
if err := v.SetOrigin(ox, oy+1); err != nil { var err error
return err if cy < maxY {
} err = v.SetCursor(cx, cy+1)
} else {
err = v.SetOrigin(ox, oy+1)
}
if err != nil {
return err
} }
gui.newLineFocused(g, v) gui.newLineFocused(g, v)
@ -208,10 +223,19 @@ func (gui *Gui) resetOrigin(v *gocui.View) error {
// if the cursor down past the last item, move it to the last line // if the cursor down past the last item, move it to the last line
func (gui *Gui) correctCursor(v *gocui.View) error { func (gui *Gui) correctCursor(v *gocui.View) error {
cx, cy := v.Cursor() cx, cy := v.Cursor()
_, oy := v.Origin() ox, oy := v.Origin()
lineCount := len(v.BufferLines()) - 2 _, height := v.Size()
if cy >= lineCount-oy { maxY := height - 1
return v.SetCursor(cx, lineCount-oy) ly := v.LinesHeight() - 1
if oy+cy <= ly {
return nil
}
newCy := utils.Min(ly, maxY)
if err := v.SetCursor(cx, newCy); err != nil {
return err
}
if err := v.SetOrigin(ox, ly-newCy); err != nil {
return err
} }
return nil return nil
} }

View File

@ -34,6 +34,15 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{ }, &i18n.Message{
ID: "CommitChanges", ID: "CommitChanges",
Other: "Commit Veranderingen", Other: "Commit Veranderingen",
}, &i18n.Message{
ID: "AmendLastCommit",
Other: "wijzig laatste commit",
}, &i18n.Message{
ID: "SureToAmend",
Other: "Weet je zeker dat je de laatste commit wilt wijzigen? U kunt het commit-bericht wijzigen vanuit het commits-paneel.",
}, &i18n.Message{
ID: "NoCommitToAmend",
Other: "Er is geen verplichting om te wijzigen.",
}, &i18n.Message{ }, &i18n.Message{
ID: "CommitChangesWithEditor", ID: "CommitChangesWithEditor",
Other: "commit Veranderingen met de git editor", Other: "commit Veranderingen met de git editor",

View File

@ -42,6 +42,15 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{ }, &i18n.Message{
ID: "CommitChanges", ID: "CommitChanges",
Other: "commit changes", Other: "commit changes",
}, &i18n.Message{
ID: "AmendLastCommit",
Other: "amend last commit",
}, &i18n.Message{
ID: "SureToAmend",
Other: "Are you sure you want to amend last commit? You can change commit message from commits panel.",
}, &i18n.Message{
ID: "NoCommitToAmend",
Other: "There's no commit to amend.",
}, &i18n.Message{ }, &i18n.Message{
ID: "CommitChangesWithEditor", ID: "CommitChangesWithEditor",
Other: "commit changes using git editor", Other: "commit changes using git editor",
@ -390,6 +399,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{ }, &i18n.Message{
ID: "ConfirmQuit", ID: "ConfirmQuit",
Other: `Are you sure you want to quit?`, Other: `Are you sure you want to quit?`,
}, &i18n.Message{
ID: "SwitchRepo",
Other: `switch to a recent repo`,
}, },
) )
} }

View File

@ -32,6 +32,15 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{ }, &i18n.Message{
ID: "CommitChanges", ID: "CommitChanges",
Other: "commituj zmiany", Other: "commituj zmiany",
}, &i18n.Message{
ID: "AmendLastCommit",
Other: "zmień ostatnie zatwierdzenie",
}, &i18n.Message{
ID: "SureToAmend",
Other: "Czy na pewno chcesz zmienić ostatnie zatwierdzenie? Możesz zmienić komunikat zatwierdzenia z panelu zatwierdzeń.",
}, &i18n.Message{
ID: "NoCommitToAmend",
Other: "Nie ma zobowiązania do zmiany.",
}, &i18n.Message{ }, &i18n.Message{
ID: "CommitChangesWithEditor", ID: "CommitChangesWithEditor",
Other: "commituj zmiany używając edytora z gita", Other: "commituj zmiany używając edytora z gita",

View File

@ -1,10 +1,12 @@
package utils package utils
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"strings" "strings"
"time" "time"
@ -99,3 +101,116 @@ func ResolvePlaceholderString(str string, arguments map[string]string) string {
} }
return str return str
} }
// Min returns the minimum of two integers
func Min(x, y int) int {
if x < y {
return x
}
return y
}
type Displayable interface {
GetDisplayStrings() []string
}
// RenderList takes a slice of items, confirms they implement the Displayable
// interface, then generates a list of their displaystrings to write to a panel's
// buffer
func RenderList(slice interface{}) (string, error) {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
return "", errors.New("RenderList given a non-slice type")
}
displayables := make([]Displayable, s.Len())
for i := 0; i < s.Len(); i++ {
value, ok := s.Index(i).Interface().(Displayable)
if !ok {
return "", errors.New("item does not implement the Displayable interface")
}
displayables[i] = value
}
return renderDisplayableList(displayables)
}
// renderDisplayableList takes a list of displayable items, obtains their display
// strings via GetDisplayStrings() and then returns a single string containing
// each item's string representation on its own line, with appropriate horizontal
// padding between the item's own strings
func renderDisplayableList(items []Displayable) (string, error) {
if len(items) == 0 {
return "", nil
}
stringArrays := getDisplayStringArrays(items)
if !displayArraysAligned(stringArrays) {
return "", errors.New("Each item must return the same number of strings to display")
}
padWidths := getPadWidths(stringArrays)
paddedDisplayStrings := getPaddedDisplayStrings(stringArrays, padWidths)
return strings.Join(paddedDisplayStrings, "\n"), nil
}
func getPadWidths(stringArrays [][]string) []int {
if len(stringArrays[0]) <= 1 {
return []int{}
}
padWidths := make([]int, len(stringArrays[0])-1)
for i := range padWidths {
for _, strings := range stringArrays {
if len(strings[i]) > padWidths[i] {
padWidths[i] = len(strings[i])
}
}
}
return padWidths
}
func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string {
paddedDisplayStrings := make([]string, len(stringArrays))
for i, stringArray := range stringArrays {
if len(stringArray) == 0 {
continue
}
for j, padWidth := range padWidths {
paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " "
}
paddedDisplayStrings[i] += stringArray[len(padWidths)]
}
return paddedDisplayStrings
}
// displayArraysAligned returns true if every string array returned from our
// list of displayables has the same length
func displayArraysAligned(stringArrays [][]string) bool {
for _, strings := range stringArrays {
if len(strings) != len(stringArrays[0]) {
return false
}
}
return true
}
func getDisplayStringArrays(displayables []Displayable) [][]string {
stringArrays := make([][]string, len(displayables))
for i, item := range displayables {
stringArrays[i] = item.GetDisplayStrings()
}
return stringArrays
}
// IncludesString if the list contains the string
func IncludesString(list []string, a string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}

View File

@ -1,6 +1,7 @@
package utils package utils
import ( import (
"errors"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -167,3 +168,246 @@ func TestResolvePlaceholderString(t *testing.T) {
assert.EqualValues(t, string(s.expected), ResolvePlaceholderString(s.templateString, s.arguments)) assert.EqualValues(t, string(s.expected), ResolvePlaceholderString(s.templateString, s.arguments))
} }
} }
func TestDisplayArraysAligned(t *testing.T) {
type scenario struct {
input [][]string
expected bool
}
scenarios := []scenario{
{
[][]string{{"", ""}, {"", ""}},
true,
},
{
[][]string{{""}, {"", ""}},
false,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, displayArraysAligned(s.input))
}
}
type myDisplayable struct {
strings []string
}
type myStruct struct{}
func (d *myDisplayable) GetDisplayStrings() []string {
return d.strings
}
func TestGetDisplayStringArrays(t *testing.T) {
type scenario struct {
input []Displayable
expected [][]string
}
scenarios := []scenario{
{
[]Displayable{
Displayable(&myDisplayable{[]string{"a", "b"}}),
Displayable(&myDisplayable{[]string{"c", "d"}}),
},
[][]string{{"a", "b"}, {"c", "d"}},
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, getDisplayStringArrays(s.input))
}
}
func TestRenderDisplayableList(t *testing.T) {
type scenario struct {
input []Displayable
expectedString string
expectedError error
}
scenarios := []scenario{
{
[]Displayable{
Displayable(&myDisplayable{[]string{}}),
Displayable(&myDisplayable{[]string{}}),
},
"\n",
nil,
},
{
[]Displayable{
Displayable(&myDisplayable{[]string{"aa", "b"}}),
Displayable(&myDisplayable{[]string{"c", "d"}}),
},
"aa b\nc d",
nil,
},
{
[]Displayable{
Displayable(&myDisplayable{[]string{"a"}}),
Displayable(&myDisplayable{[]string{"b", "c"}}),
},
"",
errors.New("Each item must return the same number of strings to display"),
},
}
for _, s := range scenarios {
str, err := renderDisplayableList(s.input)
assert.EqualValues(t, s.expectedString, str)
assert.EqualValues(t, s.expectedError, err)
}
}
func TestRenderList(t *testing.T) {
type scenario struct {
input interface{}
expectedString string
expectedError error
}
scenarios := []scenario{
{
[]*myDisplayable{
{[]string{"aa", "b"}},
{[]string{"c", "d"}},
},
"aa b\nc d",
nil,
},
{
[]*myStruct{
{},
{},
},
"",
errors.New("item does not implement the Displayable interface"),
},
{
&myStruct{},
"",
errors.New("RenderList given a non-slice type"),
},
}
for _, s := range scenarios {
str, err := RenderList(s.input)
assert.EqualValues(t, s.expectedString, str)
assert.EqualValues(t, s.expectedError, err)
}
}
func TestGetPaddedDisplayStrings(t *testing.T) {
type scenario struct {
stringArrays [][]string
padWidths []int
expected []string
}
scenarios := []scenario{
{
[][]string{{"a", "b"}, {"c", "d"}},
[]int{1},
[]string{"a b", "c d"},
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, getPaddedDisplayStrings(s.stringArrays, s.padWidths))
}
}
func TestGetPadWidths(t *testing.T) {
type scenario struct {
stringArrays [][]string
expected []int
}
scenarios := []scenario{
{
[][]string{{""}, {""}},
[]int{},
},
{
[][]string{{"a"}, {""}},
[]int{},
},
{
[][]string{{"aa", "b", "ccc"}, {"c", "d", "e"}},
[]int{2, 1},
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays))
}
}
func TestMin(t *testing.T) {
type scenario struct {
a int
b int
expected int
}
scenarios := []scenario{
{
1,
1,
1,
},
{
1,
2,
1,
},
{
2,
1,
1,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, Min(s.a, s.b))
}
}
func TestIncludesString(t *testing.T) {
type scenario struct {
list []string
element string
expected bool
}
scenarios := []scenario{
{
[]string{"a", "b"},
"a",
true,
},
{
[]string{"a", "b"},
"c",
false,
},
{
[]string{"a", "b"},
"",
false,
},
{
[]string{""},
"",
true,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, IncludesString(s.list, s.element))
}
}

View File

@ -1,35 +0,0 @@
#!/bin/bash
set -ex; rm -rf repo; mkdir repo; cd repo
git init
git config user.email "test@example.com"
git config user.name "Lazygit Tester"
echo "deleted" > deleted_staged
echo "deleted_unstaged" > deleted_unstaged
echo "modified_staged" > modified_staged
echo "modified_unstaged" > modified_unstaged
echo "renamed" > renamed_before
git add .
git commit -m "files to delete"
rm deleted_staged
rm deleted_unstaged
rm renamed_before
echo "renamed" > renamed_after
echo "more" >> modified_staged
echo "more" >> modified_unstaged
echo "untracked_staged" > untracked_staged
echo "untracked_unstaged" > untracked_unstaged
echo "blah" > "file with space staged"
echo "blah" > "file with space unstaged"
echo "same name as branch" > master
git add deleted_staged
git add modified_staged
git add untracked_staged
git add "file with space staged"
git add renamed_before
git add renamed_after

View File

@ -4,7 +4,7 @@
package gocui package gocui
import "github.com/nsf/termbox-go" import "github.com/jesseduffield/termbox-go"
// Attribute represents a terminal attribute, like color, font style, etc. They // Attribute represents a terminal attribute, like color, font style, etc. They
// can be combined using bitwise OR (|). Note that it is not possible to // can be combined using bitwise OR (|). Note that it is not possible to

View File

@ -7,7 +7,7 @@ package gocui
import ( import (
"errors" "errors"
"github.com/nsf/termbox-go" "github.com/jesseduffield/termbox-go"
) )
var ( var (

View File

@ -4,7 +4,7 @@
package gocui package gocui
import "github.com/nsf/termbox-go" import "github.com/jesseduffield/termbox-go"
// Keybidings are used to link a given key-press event with a handler. // Keybidings are used to link a given key-press event with a handler.
type keybinding struct { type keybinding struct {

View File

@ -10,8 +10,8 @@ import (
"io" "io"
"strings" "strings"
"github.com/jesseduffield/termbox-go"
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"
"github.com/nsf/termbox-go"
) )
// Constants for overlapping edges // Constants for overlapping edges
@ -447,6 +447,10 @@ func (v *View) ViewBufferLines() []string {
return lines return lines
} }
func (v *View) LinesHeight() int {
return len(v.lines)
}
// ViewBuffer returns a string with the contents of the view's buffer that is // ViewBuffer returns a string with the contents of the view's buffer that is
// shown to the user. // shown to the user.
func (v *View) ViewBuffer() string { func (v *View) ViewBuffer() string {

View File

@ -317,6 +317,9 @@ func PollEvent() Event {
event.Type = EventKey event.Type = EventKey
status := extract_event(inbuf, &event, true) status := extract_event(inbuf, &event, true)
if event.N != 0 { if event.N != 0 {
if event.N > len(inbuf) {
event.N = len(inbuf)
}
copy(inbuf, inbuf[event.N:]) copy(inbuf, inbuf[event.N:])
inbuf = inbuf[:len(inbuf)-event.N] inbuf = inbuf[:len(inbuf)-event.N]
} }
@ -345,6 +348,9 @@ func PollEvent() Event {
input_comm <- ev input_comm <- ev
status := extract_event(inbuf, &event, true) status := extract_event(inbuf, &event, true)
if event.N != 0 { if event.N != 0 {
if event.N > len(inbuf) {
event.N = len(inbuf)
}
copy(inbuf, inbuf[event.N:]) copy(inbuf, inbuf[event.N:])
inbuf = inbuf[:len(inbuf)-event.N] inbuf = inbuf[:len(inbuf)-event.N]
} }
@ -359,6 +365,9 @@ func PollEvent() Event {
status := extract_event(inbuf, &event, false) status := extract_event(inbuf, &event, false)
if event.N != 0 { if event.N != 0 {
if event.N > len(inbuf) {
event.N = len(inbuf)
}
copy(inbuf, inbuf[event.N:]) copy(inbuf, inbuf[event.N:])
inbuf = inbuf[:len(inbuf)-event.N] inbuf = inbuf[:len(inbuf)-event.N]
} }