mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-22 05:29:44 +02:00
Merge branch 'master' into feature/informative-commit-colors
This commit is contained in:
commit
8fac19c175
@ -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
20
Gopkg.lock
generated
@ -189,11 +189,19 @@
|
|||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:71e6c15797951d3fabaa944d70253e36a6cee96bf54ca0bc43ca3de3b4814bbb"
|
digest = "1:d6df25dee1274e63c25f84c257bc504359f975dedb791eec7a5b51992146bb76"
|
||||||
name = "github.com/jesseduffield/gocui"
|
name = "github.com/jesseduffield/gocui"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "2cb6e95bbbf850bb32cc1799e07d08ff0f144746"
|
revision = "4fca348422d8b6136e801b222858204a35ee369a"
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
4
go.mod
4
go.mod
@ -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-20180919095827-4fca348422d8
|
||||||
|
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
54
go.sum
@ -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=
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
30
pkg/commands/commit.go
Normal 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
36
pkg/commands/file.go
Normal 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}
|
||||||
|
}
|
@ -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
|
||||||
@ -104,17 +105,17 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 +128,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 +141,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 +169,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 +177,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 {
|
||||||
@ -231,13 +232,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
|
||||||
@ -407,18 +413,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
|
||||||
@ -427,7 +433,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
|
||||||
@ -450,16 +456,7 @@ 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 {
|
|
||||||
for _, b := range list {
|
|
||||||
if b == a {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) getMergeBase() string {
|
func (c *GitCommand) getMergeBase() string {
|
||||||
@ -472,17 +469,18 @@ func (c *GitCommand) getMergeBase() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
pushables := c.GetCommitsToPush()
|
pushables := c.GetCommitsToPush()
|
||||||
log := c.GetLog()
|
log := c.GetLog()
|
||||||
// now we can split it up and turn it into commits
|
|
||||||
lines := utils.SplitLines(log)
|
lines := utils.SplitLines(log)
|
||||||
commits := make([]Commit, len(lines))
|
commits := make([]*Commit, len(lines))
|
||||||
|
// now we can split it up and turn it into commits
|
||||||
for i, line := range lines {
|
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[i] = Commit{
|
commits[i] = &Commit{
|
||||||
Sha: sha,
|
Sha: sha,
|
||||||
Name: strings.Join(splitLine[1:], " "),
|
Name: strings.Join(splitLine[1:], " "),
|
||||||
Pushed: pushed,
|
Pushed: pushed,
|
||||||
@ -493,7 +491,7 @@ func (c *GitCommand) GetCommits() []Commit {
|
|||||||
return commits
|
return commits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) setCommitMergedStatuses(commits []Commit) []Commit {
|
func (c *GitCommand) setCommitMergedStatuses(commits []*Commit) []*Commit {
|
||||||
ancestor := c.getMergeBase()
|
ancestor := c.getMergeBase()
|
||||||
if ancestor == "" {
|
if ancestor == "" {
|
||||||
return commits
|
return commits
|
||||||
@ -518,6 +516,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,7 +535,7 @@ func (c *GitCommand) Show(sha string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 := ""
|
||||||
fileName := c.OSCommand.Quote(file.Name)
|
fileName := c.OSCommand.Quote(file.Name)
|
||||||
if file.HasStagedChanges && !file.HasUnstagedChanges {
|
if file.HasStagedChanges && !file.HasUnstagedChanges {
|
||||||
|
@ -1,33 +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
|
|
||||||
Merged 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 {
|
||||||
|
@ -61,6 +61,7 @@ func newDummyGitCommand() *GitCommand {
|
|||||||
Tr: i18n.NewLocalizer(newDummyLog()),
|
Tr: i18n.NewLocalizer(newDummyLog()),
|
||||||
getGlobalGitConfig: func(string) (string, error) { return "", nil },
|
getGlobalGitConfig: func(string) (string, error) { return "", nil },
|
||||||
getLocalGitConfig: func(string) (string, error) { return "", nil },
|
getLocalGitConfig: func(string) (string, error) { return "", nil },
|
||||||
|
removeFile: func(string) error { return nil },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +276,7 @@ func TestGitCommandGetStashEntries(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
command func(string, ...string) *exec.Cmd
|
command func(string, ...string) *exec.Cmd
|
||||||
test func([]StashEntry)
|
test func([]*StashEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
@ -284,7 +285,7 @@ func TestGitCommandGetStashEntries(t *testing.T) {
|
|||||||
func(string, ...string) *exec.Cmd {
|
func(string, ...string) *exec.Cmd {
|
||||||
return exec.Command("echo")
|
return exec.Command("echo")
|
||||||
},
|
},
|
||||||
func(entries []StashEntry) {
|
func(entries []*StashEntry) {
|
||||||
assert.Len(t, entries, 0)
|
assert.Len(t, entries, 0)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -293,8 +294,8 @@ func TestGitCommandGetStashEntries(t *testing.T) {
|
|||||||
func(string, ...string) *exec.Cmd {
|
func(string, ...string) *exec.Cmd {
|
||||||
return exec.Command("echo", "WIP on add-pkg-commands-test: 55c6af2 increase parallel build\nWIP on master: bb86a3f update github template")
|
return exec.Command("echo", "WIP on add-pkg-commands-test: 55c6af2 increase parallel build\nWIP on master: bb86a3f update github template")
|
||||||
},
|
},
|
||||||
func(entries []StashEntry) {
|
func(entries []*StashEntry) {
|
||||||
expected := []StashEntry{
|
expected := []*StashEntry{
|
||||||
{
|
{
|
||||||
0,
|
0,
|
||||||
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build",
|
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build",
|
||||||
@ -341,7 +342,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
command func(string, ...string) *exec.Cmd
|
command func(string, ...string) *exec.Cmd
|
||||||
test func([]File)
|
test func([]*File)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
@ -350,7 +351,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
|
|||||||
func(cmd string, args ...string) *exec.Cmd {
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
return exec.Command("echo")
|
return exec.Command("echo")
|
||||||
},
|
},
|
||||||
func(files []File) {
|
func(files []*File) {
|
||||||
assert.Len(t, files, 0)
|
assert.Len(t, files, 0)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -362,10 +363,10 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
|
|||||||
"MM file1.txt\nA file3.txt\nAM file2.txt\n?? file4.txt",
|
"MM file1.txt\nA file3.txt\nAM file2.txt\n?? file4.txt",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
func(files []File) {
|
func(files []*File) {
|
||||||
assert.Len(t, files, 4)
|
assert.Len(t, files, 4)
|
||||||
|
|
||||||
expected := []File{
|
expected := []*File{
|
||||||
{
|
{
|
||||||
Name: "file1.txt",
|
Name: "file1.txt",
|
||||||
HasStagedChanges: true,
|
HasStagedChanges: true,
|
||||||
@ -463,22 +464,22 @@ func TestGitCommandCommitAmend(t *testing.T) {
|
|||||||
func TestGitCommandMergeStatusFiles(t *testing.T) {
|
func TestGitCommandMergeStatusFiles(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
oldFiles []File
|
oldFiles []*File
|
||||||
newFiles []File
|
newFiles []*File
|
||||||
test func([]File)
|
test func([]*File)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"Old file and new file are the same",
|
"Old file and new file are the same",
|
||||||
[]File{},
|
[]*File{},
|
||||||
[]File{
|
[]*File{
|
||||||
{
|
{
|
||||||
Name: "new_file.txt",
|
Name: "new_file.txt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
func(files []File) {
|
func(files []*File) {
|
||||||
expected := []File{
|
expected := []*File{
|
||||||
{
|
{
|
||||||
Name: "new_file.txt",
|
Name: "new_file.txt",
|
||||||
},
|
},
|
||||||
@ -490,7 +491,7 @@ func TestGitCommandMergeStatusFiles(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Several files to merge, with some identical",
|
"Several files to merge, with some identical",
|
||||||
[]File{
|
[]*File{
|
||||||
{
|
{
|
||||||
Name: "new_file1.txt",
|
Name: "new_file1.txt",
|
||||||
},
|
},
|
||||||
@ -501,7 +502,7 @@ func TestGitCommandMergeStatusFiles(t *testing.T) {
|
|||||||
Name: "new_file3.txt",
|
Name: "new_file3.txt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]File{
|
[]*File{
|
||||||
{
|
{
|
||||||
Name: "new_file4.txt",
|
Name: "new_file4.txt",
|
||||||
},
|
},
|
||||||
@ -512,8 +513,8 @@ func TestGitCommandMergeStatusFiles(t *testing.T) {
|
|||||||
Name: "new_file1.txt",
|
Name: "new_file1.txt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
func(files []File) {
|
func(files []*File) {
|
||||||
expected := []File{
|
expected := []*File{
|
||||||
{
|
{
|
||||||
Name: "new_file1.txt",
|
Name: "new_file1.txt",
|
||||||
},
|
},
|
||||||
@ -551,7 +552,7 @@ func TestGitCommandUpstreamDifferentCount(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Can't retrieve pushable count",
|
"Can't retrieve pushable count",
|
||||||
func(string, ...string) *exec.Cmd {
|
func(string, ...string) *exec.Cmd {
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
},
|
},
|
||||||
func(pushableCount string, pullableCount string) {
|
func(pushableCount string, pullableCount string) {
|
||||||
assert.EqualValues(t, "?", pushableCount)
|
assert.EqualValues(t, "?", pushableCount)
|
||||||
@ -562,7 +563,7 @@ func TestGitCommandUpstreamDifferentCount(t *testing.T) {
|
|||||||
"Can't retrieve pullable count",
|
"Can't retrieve pullable count",
|
||||||
func(cmd string, args ...string) *exec.Cmd {
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
if args[1] == "head..@{u}" {
|
if args[1] == "head..@{u}" {
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
}
|
}
|
||||||
|
|
||||||
return exec.Command("echo")
|
return exec.Command("echo")
|
||||||
@ -601,17 +602,17 @@ func TestGitCommandGetCommitsToPush(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
command func(string, ...string) *exec.Cmd
|
command func(string, ...string) *exec.Cmd
|
||||||
test func([]string)
|
test func(map[string]bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"Can't retrieve pushable commits",
|
"Can't retrieve pushable commits",
|
||||||
func(string, ...string) *exec.Cmd {
|
func(string, ...string) *exec.Cmd {
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
},
|
},
|
||||||
func(pushables []string) {
|
func(pushables map[string]bool) {
|
||||||
assert.EqualValues(t, []string{}, pushables)
|
assert.EqualValues(t, map[string]bool{}, pushables)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -619,9 +620,9 @@ func TestGitCommandGetCommitsToPush(t *testing.T) {
|
|||||||
func(cmd string, args ...string) *exec.Cmd {
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
return exec.Command("echo", "8a2bb0e\n78976bc")
|
return exec.Command("echo", "8a2bb0e\n78976bc")
|
||||||
},
|
},
|
||||||
func(pushables []string) {
|
func(pushables map[string]bool) {
|
||||||
assert.Len(t, pushables, 2)
|
assert.Len(t, pushables, 2)
|
||||||
assert.EqualValues(t, []string{"8a2bb0e", "78976bc"}, pushables)
|
assert.EqualValues(t, map[string]bool{"8a2bb0e": true, "78976bc": true}, pushables)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -872,7 +873,7 @@ func TestGitCommandCommit(t *testing.T) {
|
|||||||
assert.EqualValues(t, "git", cmd)
|
assert.EqualValues(t, "git", cmd)
|
||||||
assert.EqualValues(t, []string{"commit", "-m", "test"}, args)
|
assert.EqualValues(t, []string{"commit", "-m", "test"}, args)
|
||||||
|
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
},
|
},
|
||||||
func(string) (string, error) {
|
func(string) (string, error) {
|
||||||
return "false", nil
|
return "false", nil
|
||||||
@ -935,7 +936,7 @@ func TestGitCommandPush(t *testing.T) {
|
|||||||
assert.EqualValues(t, "git", cmd)
|
assert.EqualValues(t, "git", cmd)
|
||||||
assert.EqualValues(t, []string{"push", "-u", "origin", "test"}, args)
|
assert.EqualValues(t, []string{"push", "-u", "origin", "test"}, args)
|
||||||
|
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
func(err error) {
|
func(err error) {
|
||||||
@ -967,7 +968,7 @@ func TestGitCommandSquashPreviousTwoCommits(t *testing.T) {
|
|||||||
assert.EqualValues(t, "git", cmd)
|
assert.EqualValues(t, "git", cmd)
|
||||||
assert.EqualValues(t, []string{"reset", "--soft", "HEAD^"}, args)
|
assert.EqualValues(t, []string{"reset", "--soft", "HEAD^"}, args)
|
||||||
|
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
},
|
},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
@ -983,7 +984,7 @@ func TestGitCommandSquashPreviousTwoCommits(t *testing.T) {
|
|||||||
assert.EqualValues(t, "git", cmd)
|
assert.EqualValues(t, "git", cmd)
|
||||||
assert.EqualValues(t, []string{"commit", "--amend", "-m", "test"}, args)
|
assert.EqualValues(t, []string{"commit", "--amend", "-m", "test"}, args)
|
||||||
|
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
},
|
},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
@ -1031,7 +1032,7 @@ func TestGitCommandSquashFixupCommit(t *testing.T) {
|
|||||||
return func(cmd string, args ...string) *exec.Cmd {
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
cmdsCalled = append(cmdsCalled, args)
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
if len(args) > 0 && args[0] == "checkout" {
|
if len(args) > 0 && args[0] == "checkout" {
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
}
|
}
|
||||||
|
|
||||||
return exec.Command("echo")
|
return exec.Command("echo")
|
||||||
@ -1165,7 +1166,7 @@ func TestGitCommandIsInMergeState(t *testing.T) {
|
|||||||
assert.EqualValues(t, "git", cmd)
|
assert.EqualValues(t, "git", cmd)
|
||||||
assert.EqualValues(t, []string{"status", "--untracked-files=all"}, args)
|
assert.EqualValues(t, []string{"status", "--untracked-files=all"}, args)
|
||||||
|
|
||||||
return exec.Command("exit", "1")
|
return exec.Command("test")
|
||||||
},
|
},
|
||||||
func(isInMergeState bool, err error) {
|
func(isInMergeState bool, err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@ -1219,11 +1220,344 @@ func TestGitCommandIsInMergeState(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGitCommandRemoveFile(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
command func() (func(string, ...string) *exec.Cmd, *[][]string)
|
||||||
|
test func(*[][]string, error)
|
||||||
|
file *File
|
||||||
|
removeFile func(string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"An error occurred when resetting",
|
||||||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||||||
|
cmdsCalled := [][]string{}
|
||||||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
|
|
||||||
|
return exec.Command("test")
|
||||||
|
}, &cmdsCalled
|
||||||
|
},
|
||||||
|
func(cmdsCalled *[][]string, err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Len(t, *cmdsCalled, 1)
|
||||||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||||||
|
{"reset", "--", "test"},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
&File{
|
||||||
|
Name: "test",
|
||||||
|
HasStagedChanges: true,
|
||||||
|
},
|
||||||
|
func(string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"An error occurred when removing file",
|
||||||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||||||
|
cmdsCalled := [][]string{}
|
||||||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
|
|
||||||
|
return exec.Command("test")
|
||||||
|
}, &cmdsCalled
|
||||||
|
},
|
||||||
|
func(cmdsCalled *[][]string, err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.EqualError(t, err, "an error occurred when removing file")
|
||||||
|
assert.Len(t, *cmdsCalled, 0)
|
||||||
|
},
|
||||||
|
&File{
|
||||||
|
Name: "test",
|
||||||
|
Tracked: false,
|
||||||
|
},
|
||||||
|
func(string) error {
|
||||||
|
return fmt.Errorf("an error occurred when removing file")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"An error occurred with checkout",
|
||||||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||||||
|
cmdsCalled := [][]string{}
|
||||||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
|
|
||||||
|
return exec.Command("test")
|
||||||
|
}, &cmdsCalled
|
||||||
|
},
|
||||||
|
func(cmdsCalled *[][]string, err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Len(t, *cmdsCalled, 1)
|
||||||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||||||
|
{"checkout", "--", "test"},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
&File{
|
||||||
|
Name: "test",
|
||||||
|
Tracked: true,
|
||||||
|
HasStagedChanges: false,
|
||||||
|
},
|
||||||
|
func(string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Checkout only",
|
||||||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||||||
|
cmdsCalled := [][]string{}
|
||||||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
}, &cmdsCalled
|
||||||
|
},
|
||||||
|
func(cmdsCalled *[][]string, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, *cmdsCalled, 1)
|
||||||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||||||
|
{"checkout", "--", "test"},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
&File{
|
||||||
|
Name: "test",
|
||||||
|
Tracked: true,
|
||||||
|
HasStagedChanges: false,
|
||||||
|
},
|
||||||
|
func(string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Reset and checkout",
|
||||||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||||||
|
cmdsCalled := [][]string{}
|
||||||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
}, &cmdsCalled
|
||||||
|
},
|
||||||
|
func(cmdsCalled *[][]string, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, *cmdsCalled, 2)
|
||||||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||||||
|
{"reset", "--", "test"},
|
||||||
|
{"checkout", "--", "test"},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
&File{
|
||||||
|
Name: "test",
|
||||||
|
Tracked: true,
|
||||||
|
HasStagedChanges: true,
|
||||||
|
},
|
||||||
|
func(string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Reset and remove",
|
||||||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||||||
|
cmdsCalled := [][]string{}
|
||||||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
}, &cmdsCalled
|
||||||
|
},
|
||||||
|
func(cmdsCalled *[][]string, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, *cmdsCalled, 1)
|
||||||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||||||
|
{"reset", "--", "test"},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
&File{
|
||||||
|
Name: "test",
|
||||||
|
Tracked: false,
|
||||||
|
HasStagedChanges: true,
|
||||||
|
},
|
||||||
|
func(filename string) error {
|
||||||
|
assert.Equal(t, "test", filename)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Remove only",
|
||||||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||||||
|
cmdsCalled := [][]string{}
|
||||||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
cmdsCalled = append(cmdsCalled, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
}, &cmdsCalled
|
||||||
|
},
|
||||||
|
func(cmdsCalled *[][]string, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, *cmdsCalled, 0)
|
||||||
|
},
|
||||||
|
&File{
|
||||||
|
Name: "test",
|
||||||
|
Tracked: false,
|
||||||
|
HasStagedChanges: false,
|
||||||
|
},
|
||||||
|
func(filename string) error {
|
||||||
|
assert.Equal(t, "test", filename)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
var cmdsCalled *[][]string
|
||||||
|
gitCmd := newDummyGitCommand()
|
||||||
|
gitCmd.OSCommand.command, cmdsCalled = s.command()
|
||||||
|
gitCmd.removeFile = s.removeFile
|
||||||
|
s.test(cmdsCalled, gitCmd.RemoveFile(s.file))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitCommandCheckout(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
command func(string, ...string) *exec.Cmd
|
||||||
|
test func(error)
|
||||||
|
force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"Checkout",
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"checkout", "test"}, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Checkout forced",
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"checkout", "--force", "test"}, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
gitCmd := newDummyGitCommand()
|
||||||
|
gitCmd.OSCommand.command = s.command
|
||||||
|
s.test(gitCmd.Checkout("test", s.force))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitCommandGetBranchGraph(t *testing.T) {
|
||||||
|
gitCmd := newDummyGitCommand()
|
||||||
|
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"log", "--graph", "--color", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "-100", "test"}, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := gitCmd.GetBranchGraph("test")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitCommandGetCommits(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
command func(string, ...string) *exec.Cmd
|
||||||
|
test func([]*Commit)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"No data found",
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
|
||||||
|
switch args[0] {
|
||||||
|
case "rev-list":
|
||||||
|
assert.EqualValues(t, []string{"rev-list", "@{u}..head", "--abbrev-commit"}, args)
|
||||||
|
return exec.Command("echo")
|
||||||
|
case "log":
|
||||||
|
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
|
||||||
|
return exec.Command("echo")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(commits []*Commit) {
|
||||||
|
assert.Len(t, commits, 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GetCommits returns 2 commits, 1 pushed the other not",
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
|
||||||
|
switch args[0] {
|
||||||
|
case "rev-list":
|
||||||
|
assert.EqualValues(t, []string{"rev-list", "@{u}..head", "--abbrev-commit"}, args)
|
||||||
|
return exec.Command("echo", "8a2bb0e")
|
||||||
|
case "log":
|
||||||
|
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
|
||||||
|
return exec.Command("echo", "8a2bb0e commit 1\n78976bc commit 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(commits []*Commit) {
|
||||||
|
assert.Len(t, commits, 2)
|
||||||
|
assert.EqualValues(t, []*Commit{
|
||||||
|
{
|
||||||
|
Sha: "8a2bb0e",
|
||||||
|
Name: "commit 1",
|
||||||
|
Pushed: true,
|
||||||
|
DisplayString: "8a2bb0e commit 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Sha: "78976bc",
|
||||||
|
Name: "commit 2",
|
||||||
|
Pushed: false,
|
||||||
|
DisplayString: "78976bc commit 2",
|
||||||
|
},
|
||||||
|
}, commits)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
gitCmd := newDummyGitCommand()
|
||||||
|
gitCmd.OSCommand.command = s.command
|
||||||
|
s.test(gitCmd.GetCommits())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGitCommandDiff(t *testing.T) {
|
func TestGitCommandDiff(t *testing.T) {
|
||||||
gitCommand := newDummyGitCommand()
|
gitCommand := newDummyGitCommand()
|
||||||
assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh"))
|
assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh"))
|
||||||
|
|
||||||
files := []File{
|
files := []*File{
|
||||||
{
|
{
|
||||||
Name: "deleted_staged",
|
Name: "deleted_staged",
|
||||||
HasStagedChanges: false,
|
HasStagedChanges: false,
|
||||||
|
13
pkg/commands/stash_entry.go
Normal file
13
pkg/commands/stash_entry.go
Normal 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}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
|
@ -2,10 +2,11 @@ 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 {
|
||||||
@ -15,23 +16,15 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
red := color.New(color.FgRed)
|
|
||||||
yellow := color.New(color.FgGreen)
|
list, err := utils.RenderList(gui.State.Commits)
|
||||||
green := color.New(color.FgYellow)
|
if err != nil {
|
||||||
white := color.New(color.FgWhite)
|
return err
|
||||||
shaColor := white
|
|
||||||
for _, commit := range gui.State.Commits {
|
|
||||||
if !commit.Pushed {
|
|
||||||
shaColor = red
|
|
||||||
} else if !commit.Merged {
|
|
||||||
shaColor = green
|
|
||||||
} 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)
|
||||||
@ -102,7 +95,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
|
||||||
@ -149,7 +142,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 {
|
||||||
@ -165,13 +157,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 {
|
||||||
|
@ -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)
|
||||||
@ -275,24 +278,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 +303,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)
|
||||||
|
@ -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
|
||||||
|
@ -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,6 +113,13 @@ 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,
|
||||||
@ -349,18 +376,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},
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
51
pkg/gui/options_menu_panel.go
Normal file
51
pkg/gui/options_menu_panel.go
Normal 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)
|
||||||
|
}
|
69
pkg/gui/recent_repos_panel.go
Normal file
69
pkg/gui/recent_repos_panel.go
Normal 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
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -160,7 +160,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 +178,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 := len(v.BufferLines()) - 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 +216,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 := len(v.BufferLines()) - 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
|
||||||
}
|
}
|
||||||
|
@ -390,6 +390,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`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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,106 @@ 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
|
||||||
|
}
|
||||||
|
@ -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,211 @@ 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2
vendor/github.com/jesseduffield/gocui/attribute.go
generated
vendored
2
vendor/github.com/jesseduffield/gocui/attribute.go
generated
vendored
@ -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
|
||||||
|
2
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
2
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
@ -7,7 +7,7 @@ package gocui
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/nsf/termbox-go"
|
"github.com/jesseduffield/termbox-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
2
vendor/github.com/jesseduffield/gocui/keybinding.go
generated
vendored
2
vendor/github.com/jesseduffield/gocui/keybinding.go
generated
vendored
@ -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 {
|
||||||
|
6
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
6
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
@ -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) ViewLinesHeight() int {
|
||||||
|
return len(v.viewLines)
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -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]
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user