Selaa lähdekoodia

Merge pull request #31 from achilleas-k/dev

LGTM
Michael Sonntag 6 vuotta sitten
vanhempi
commit
5eb92fbe14
100 muutettua tiedostoa jossa 9881 lisäystä ja 288 poistoa
  1. 0 2
      .dockerignore
  2. 5 9
      .travis.yml
  3. 41 21
      Dockerfile
  4. 3 3
      conf/locale/locale_de-DE.ini
  5. 2 2
      conf/locale/locale_en-GB.ini
  6. 2 2
      conf/locale/locale_en-US.ini
  7. 0 12
      docker/build.sh
  8. 4 8
      docker/finalize.sh
  9. 3 4
      pkg/bindata/bindata.go
  10. 37 50
      public/css/gogs.css
  11. BIN
      public/img/sm_plos-logo-sm.png
  12. 1 1
      public/less/_base.less
  13. 8 25
      public/less/_gin.less
  14. 8 7
      routes/api/v1/search/general.go
  15. 6 5
      routes/api/v1/search/suggest.go
  16. 35 5
      routes/doi.go
  17. 21 23
      routes/repo/view.go
  18. 22 13
      routes/search.go
  19. 31 61
      templates/base/footer.tmpl
  20. 1 1
      templates/mail/auth/activate.tmpl
  21. 1 1
      templates/mail/auth/activate_email.tmpl
  22. 1 1
      templates/mail/auth/invite_email.tmpl
  23. 1 1
      templates/mail/auth/register_notify.tmpl
  24. 1 1
      templates/mail/auth/reset_passwd.tmpl
  25. 12 12
      templates/repo/doifile.tmpl
  26. 4 4
      templates/repo/header.tmpl
  27. 1 1
      templates/repo/view_list.tmpl
  28. 8 13
      templates/user/auth/signup.tmpl
  29. 29 0
      vendor/github.com/G-Node/gig/LICENSE
  30. 2 0
      vendor/github.com/G-Node/gig/README.md
  31. 368 0
      vendor/github.com/G-Node/gig/delta.go
  32. 233 0
      vendor/github.com/G-Node/gig/objects.go
  33. 382 0
      vendor/github.com/G-Node/gig/pack.go
  34. 315 0
      vendor/github.com/G-Node/gig/parse.go
  35. 246 0
      vendor/github.com/G-Node/gig/refs.go
  36. 463 0
      vendor/github.com/G-Node/gig/repo.go
  37. 71 0
      vendor/github.com/G-Node/gig/util.go
  38. 80 0
      vendor/github.com/G-Node/gig/walk.go
  39. 206 0
      vendor/github.com/G-Node/gig/write.go
  40. 19 0
      vendor/github.com/G-Node/git-module/LICENSE
  41. 13 0
      vendor/github.com/G-Node/git-module/README.md
  42. 35 0
      vendor/github.com/G-Node/git-module/blob.go
  43. 158 0
      vendor/github.com/G-Node/git-module/command.go
  44. 321 0
      vendor/github.com/G-Node/git-module/commit.go
  45. 57 0
      vendor/github.com/G-Node/git-module/commit_archive.go
  46. 61 0
      vendor/github.com/G-Node/git-module/error.go
  47. 80 0
      vendor/github.com/G-Node/git-module/git.go
  48. 111 0
      vendor/github.com/G-Node/git-module/hook.go
  49. 285 0
      vendor/github.com/G-Node/git-module/repo.go
  50. 126 0
      vendor/github.com/G-Node/git-module/repo_branch.go
  51. 408 0
      vendor/github.com/G-Node/git-module/repo_commit.go
  52. 400 0
      vendor/github.com/G-Node/git-module/repo_diff.go
  53. 13 0
      vendor/github.com/G-Node/git-module/repo_hook.go
  54. 14 0
      vendor/github.com/G-Node/git-module/repo_object.go
  55. 81 0
      vendor/github.com/G-Node/git-module/repo_pull.go
  56. 209 0
      vendor/github.com/G-Node/git-module/repo_tag.go
  57. 26 0
      vendor/github.com/G-Node/git-module/repo_tree.go
  58. 93 0
      vendor/github.com/G-Node/git-module/sha1.go
  59. 48 0
      vendor/github.com/G-Node/git-module/signature.go
  60. 78 0
      vendor/github.com/G-Node/git-module/submodule.go
  61. 65 0
      vendor/github.com/G-Node/git-module/tag.go
  62. 149 0
      vendor/github.com/G-Node/git-module/tree.go
  63. 57 0
      vendor/github.com/G-Node/git-module/tree_blob.go
  64. 231 0
      vendor/github.com/G-Node/git-module/tree_entry.go
  65. 93 0
      vendor/github.com/G-Node/git-module/utils.go
  66. 29 0
      vendor/github.com/G-Node/go-annex/LICENSE
  67. 50 0
      vendor/github.com/G-Node/go-annex/add.go
  68. 75 0
      vendor/github.com/G-Node/go-annex/file.go
  69. 5 0
      vendor/github.com/G-Node/go-annex/util.go
  70. 29 0
      vendor/github.com/G-Node/libgin/LICENSE
  71. 87 0
      vendor/github.com/G-Node/libgin/libgin/archive.go
  72. 62 0
      vendor/github.com/G-Node/libgin/libgin/dex.go
  73. 134 0
      vendor/github.com/G-Node/libgin/libgin/doi.go
  74. 8 0
      vendor/github.com/G-Node/libgin/libgin/libgin.go
  75. 123 0
      vendor/github.com/Sirupsen/logrus/CHANGELOG.md
  76. 21 0
      vendor/github.com/Sirupsen/logrus/LICENSE
  77. 461 0
      vendor/github.com/Sirupsen/logrus/README.md
  78. 64 0
      vendor/github.com/Sirupsen/logrus/alt_exit.go
  79. 14 0
      vendor/github.com/Sirupsen/logrus/appveyor.yml
  80. 26 0
      vendor/github.com/Sirupsen/logrus/doc.go
  81. 288 0
      vendor/github.com/Sirupsen/logrus/entry.go
  82. 191 0
      vendor/github.com/Sirupsen/logrus/exported.go
  83. 51 0
      vendor/github.com/Sirupsen/logrus/formatter.go
  84. 34 0
      vendor/github.com/Sirupsen/logrus/hooks.go
  85. 89 0
      vendor/github.com/Sirupsen/logrus/json_formatter.go
  86. 329 0
      vendor/github.com/Sirupsen/logrus/logger.go
  87. 143 0
      vendor/github.com/Sirupsen/logrus/logrus.go
  88. 10 0
      vendor/github.com/Sirupsen/logrus/terminal_bsd.go
  89. 11 0
      vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go
  90. 19 0
      vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go
  91. 14 0
      vendor/github.com/Sirupsen/logrus/terminal_linux.go
  92. 195 0
      vendor/github.com/Sirupsen/logrus/text_formatter.go
  93. 62 0
      vendor/github.com/Sirupsen/logrus/writer.go
  94. 951 0
      vendor/golang.org/x/crypto/ssh/terminal/terminal.go
  95. 114 0
      vendor/golang.org/x/crypto/ssh/terminal/util.go
  96. 12 0
      vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
  97. 10 0
      vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
  98. 58 0
      vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
  99. 124 0
      vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
  100. 103 0
      vendor/golang.org/x/crypto/ssh/terminal/util_windows.go

+ 0 - 2
.dockerignore

@@ -1,5 +1,3 @@
-.git
-.git/**
 packager
 packager/**
 scripts

+ 5 - 9
.travis.yml

@@ -1,19 +1,15 @@
 language: go
 go:
-  - 1.9
-  - master
+  - 1.7.x
+  - 1.8.x
+  - 1.9.x
+  - 1.10.x
+  - 1.11.x
 
 before_install:
   - sudo apt-get update -qq
   - sudo apt-get install -y libpam-dev
 
-env:
-  - GO15VENDOREXPERIMENT=1
-
 script:
-  - go get github.com/docopt/docopt-go
-  - go get github.com/G-Node/gin-doi/src
-  - go get gopkg.in/yaml.v2
-  - go get github.com/G-Node/gin-dex/gindex
   - go build -v -tags "pam"
   - go test -v -cover -race ./...

+ 41 - 21
Dockerfile

@@ -1,33 +1,53 @@
-FROM ubuntu:18.04
-
-ENV DEBIAN_FRONTEND noninteractive
-
-RUN apt-get update &&                                   \
-    apt-get install -y --no-install-recommends          \
-                       gcc g++ libc6-dev make golang    \
-                       git git-annex openssh-server     \
-                       python-pip python-setuptools     \
-                       socat tzdata patch    \
-                       libpam0g-dev node-less \
-    && rm -rf /var/lib/apt/lists/*
+FROM golang:alpine AS binarybuilder
+# Install build deps
+RUN apk --no-cache --no-progress add --virtual build-deps build-base git linux-pam-dev python py-pip
+WORKDIR /go/src/github.com/G-Node/gogs
+COPY . .
+RUN make build TAGS="sqlite cert pam"
+
+FROM alpine:latest
+# Install system utils & Gogs runtime dependencies
+ADD https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64 /usr/sbin/gosu
+RUN chmod +x /usr/sbin/gosu \
+  && echo http://dl-2.alpinelinux.org/alpine/edge/community/ >> /etc/apk/repositories \
+  && apk --no-cache --no-progress add \
+    bash \
+    ca-certificates \
+    curl \
+    git \
+    linux-pam \
+    openssh \
+    s6 \
+    shadow \
+    socat \
+    tzdata \
+    python \
+    py-pip
 
 RUN pip install supervisor pyyaml
-
+RUN mkdir /git-annex
+ENV PATH="${PATH}:/git-annex/git-annex.linux"
+RUN apk add --no-cache git openssh curl
+RUN curl -Lo /git-annex/git-annex-standalone-amd64.tar.gz https://downloads.kitenet.net/git-annex/linux/current/git-annex-standalone-amd64.tar.gz
+RUN cd /git-annex && tar -xzf git-annex-standalone-amd64.tar.gz && rm git-annex-standalone-amd64.tar.gz
+RUN apk del --no-cache curl
+RUN ln -s /git-annex/git-annex.linux/git-annex-shell /bin/git-annex-shell
 
 ENV GOGS_CUSTOM /data/gogs
 
-COPY . /app/gogs/build
-WORKDIR /app/gogs/build
-
-RUN ./docker/build-go.sh
-RUN ./docker/build.sh
-RUN ./docker/finalize.sh
-
 # Configure LibC Name Service
 COPY docker/nsswitch.conf /etc/nsswitch.conf
 
+WORKDIR /app/gogs
+COPY docker ./docker
+COPY templates ./templates
+COPY public ./public
+COPY --from=binarybuilder /go/src/github.com/G-Node/gogs/gogs .
+
+RUN ./docker/finalize.sh
+
 # Configure Docker Container
 VOLUME ["/data"]
-VOLUME ["/tmp"]
 EXPOSE 22 3000
 ENTRYPOINT ["/app/gogs/docker/start.sh"]
+CMD ["/bin/s6-svscan", "/app/gogs/docker/s6/"]

+ 3 - 3
conf/locale/locale_de-DE.ini

@@ -20,7 +20,7 @@ signed_in_as=Angemeldet als
 username=Benutzername
 email=E-Mail
 password=Passwort
-re_type=Erneut eingeben
+re_type=Erneut eingeben passwort
 captcha=Captcha
 
 repository=Repository
@@ -470,8 +470,8 @@ releases=Releases
 file_raw=Originalformat
 file_history=Verlauf
 file_view_raw=Ansicht im Originalformat
-file_view_dl= Download
-file_dl= Download
+file_view_dl=Download
+file_dl=Download
 file_permalink=Permalink
 file_too_large=Diese Datei ist zu groß zum Anzeigen
 video_not_supported_in_browser=Ihr Browser unterstützt HTML5 Video-Tags nicht.

+ 2 - 2
conf/locale/locale_en-GB.ini

@@ -22,7 +22,7 @@ signed_in_as=Signed in as
 username=Username
 email=Email
 password=Password
-re_type=Re-Type
+re_type=Re-Type password
 captcha=Captcha
 
 repository=Repository
@@ -473,7 +473,7 @@ file_raw=Raw
 file_dl=Download
 file_history=History
 file_view_raw=View Raw
-file_view_dl= Download
+file_view_dl=Download
 file_permalink=Permalink
 file_too_large=This file is too large to be shown
 video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.

+ 2 - 2
conf/locale/locale_en-US.ini

@@ -22,7 +22,7 @@ signed_in_as = Signed in as
 username = Username
 email = Email
 password = Password
-re_type = Re-Type
+re_type = Re-Type password
 captcha = Captcha
 
 repository = Repository
@@ -474,7 +474,7 @@ commits = Commits
 git_branches = Branches
 releases = Releases
 file_raw = Raw
-file_dl= Download
+file_dl = Download
 file_history = History
 file_view_raw = View Raw
 file_view_dl = Download

+ 0 - 12
docker/build.sh

@@ -5,18 +5,6 @@ set -e
 # Set temp environment vars
 export GOPATH=/tmp/go
 export PATH=/usr/local/go/bin:${PATH}:${GOPATH}/bin
-export GO15VENDOREXPERIMENT=1
-
-#
-go get golang.org/x/crypto/bcrypt
-go get github.com/jteeuwen/go-bindata
-go get github.com/G-Node/gin-doi/src
-go get github.com/G-Node/gin-dex/gindex
-go get github.com/G-Node/git-module
-go get gopkg.in/yaml.v2
-
-cd ${GOPATH}/src/github.com/jteeuwen/go-bindata/go-bindata
-go install
 
 # Build Gogs
 rm -rf ${GOPATH}/src/github.com/G-Node/gogs

+ 4 - 8
docker/finalize.sh

@@ -4,11 +4,10 @@
 set -x
 set -e
 
-# Move to final place
-mv /app/gogs/build/gogs /app/gogs/
-mv /app/gogs/build/templates /app/gogs/
-mv /app/gogs/build/public /app/gogs/
-mv /app/gogs/build/docker /app/gogs/
+# Create git user for Gogs
+addgroup -S git
+adduser -G git -H -D -g 'Gogs Git User' git -h /data/git -s /bin/bash && usermod -p '*' git && passwd -u git
+echo "export GOGS_CUSTOM=${GOGS_CUSTOM}" >> /etc/profile
 
 # Final cleaning
 rm -rf /app/gogs/build
@@ -17,6 +16,3 @@ rm /app/gogs/docker/build-go.sh
 rm /app/gogs/docker/finalize.sh
 rm /app/gogs/docker/nsswitch.conf
 rm /app/gogs/docker/README.md
-
-rm -rf /tmp/go
-rm -rf /usr/local/go

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 3 - 4
pkg/bindata/bindata.go


+ 37 - 50
public/css/gogs.css

@@ -65,7 +65,7 @@ code.wrap {
 }
 .full.height {
   padding: 0;
-  margin: 0 0 -80px 0;
+  margin: 0 0 -200px 0;
   min-height: 100%;
 }
 .following.bar {
@@ -295,7 +295,7 @@ code.wrap {
   border-top: none;
   line-height: 1em;
   color: rgba(0, 0, 0, 0.8);
-  padding: .71428571em 1.14285714em !important;
+  padding: 0.71428571em 1.14285714em !important;
   font-size: 1rem;
   text-transform: none;
   font-weight: 400;
@@ -314,8 +314,8 @@ code.wrap {
   font-weight: 700 !important;
 }
 footer {
-  margin-top: 54px !important;
-  height: 40px;
+  margin-top: 114px !important;
+  height: 100px;
   background-color: white;
   border-top: 1px solid #d6d6d6;
   clear: both;
@@ -864,7 +864,7 @@ footer .ui.language .menu {
   border-top: 0;
 }
 .home {
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .home .logo {
   margin-bottom: 20px;
@@ -895,11 +895,11 @@ footer .ui.language .menu {
 }
 .signup {
   padding-top: 15px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .install {
   padding-top: 45px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .install form label {
   text-align: right;
@@ -929,8 +929,8 @@ footer .ui.language .menu {
 }
 .form .help {
   color: #999999;
-  padding-top: .6em;
-  padding-bottom: .6em;
+  padding-top: 0.6em;
+  padding-bottom: 0.6em;
   display: inline-block;
   word-break: break-word;
 }
@@ -1123,7 +1123,7 @@ footer .ui.language .menu {
 }
 .repository {
   padding-top: 15px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .repository .head .column {
   padding-top: 5px !important;
@@ -1241,7 +1241,7 @@ footer .ui.language .menu {
   padding: 0 5px;
 }
 .repository #clone-panel .clone.button:first-child {
-  border-radius: .28571429rem 0 0 .28571429rem;
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
 }
 .repository #clone-panel .icon.button {
   padding: 0 10px;
@@ -1409,7 +1409,7 @@ footer .ui.language .menu {
   background-color: #f5f2f0;
   font-family: Consolas, Monaco, 'Andale Mono', monospace;
   padding: 1em;
-  margin: .5em 0;
+  margin: 0.5em 0;
 }
 .repository.file.list #file-content #ipython-notebook .nb-input:before,
 .repository.file.list #file-content #ipython-notebook .nb-output:before {
@@ -2148,7 +2148,7 @@ footer .ui.language .menu {
 }
 .repository .diff-file-box .code-diff tbody tr.tag-code td {
   background-color: #F0F0F0 !important;
-  border-color: #D2CECE!important;
+  border-color: #D2CECE !important;
   padding-top: 4px;
   padding-bottom: 4px;
 }
@@ -2208,7 +2208,7 @@ footer .ui.language .menu {
   font-weight: normal;
 }
 .repository.quickstart .guide .clone.button:first-child {
-  border-radius: .28571429rem 0 0 .28571429rem;
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
 }
 .repository.quickstart .guide .ui.action.small.input {
   width: 100%;
@@ -2486,10 +2486,10 @@ footer .ui.language .menu {
 }
 .issue.list > .item .desc a.milestone {
   padding-left: 5px;
-  color: #999!important;
+  color: #999 !important;
 }
 .issue.list > .item .desc a.milestone:hover {
-  color: #000!important;
+  color: #000 !important;
 }
 .issue.list > .item .desc .assignee {
   margin-top: -5px;
@@ -2732,7 +2732,7 @@ footer .ui.language .menu {
 }
 .organization {
   padding-top: 15px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .organization .head .ui.header .text {
   vertical-align: middle;
@@ -2857,7 +2857,7 @@ footer .ui.language .menu {
 }
 .user:not(.icon) {
   padding-top: 15px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .user.settings .list .item.ui.grid {
   margin-top: 15px;
@@ -2940,7 +2940,7 @@ footer .ui.language .menu {
 }
 .dashboard {
   padding-top: 15px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .dashboard.feeds .context.user.menu,
 .dashboard.issues .context.user.menu {
@@ -3076,7 +3076,7 @@ footer .ui.language .menu {
 }
 .admin {
   padding-top: 15px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .admin .table.segment {
   padding: 0;
@@ -3130,7 +3130,7 @@ footer .ui.language .menu {
 }
 .explore {
   padding-top: 15px;
-  padding-bottom: 80px;
+  padding-bottom: 200px;
 }
 .explore .navbar .octicon {
   width: 16px;
@@ -3189,20 +3189,13 @@ footer .ui.language .menu {
   font-family: "Kaushan Script", "Helvetica Neue", Helvetica, Arial, cursive;
   font-size: 16px;
 }
-.bmbf {
-  width: 80px;
-}
 .supersmall {
   padding-left: 10px;
   font-size: 0.8em;
   text-align: left;
 }
 .leftspace {
-  padding-left: 40px;
-}
-.gogssmall {
-  height: 40px;
-  align-content: center;
+  padding-left: 20px;
 }
 figure {
   float: right;
@@ -3233,11 +3226,6 @@ figure figcaption {
   font-family: "Kaushan Script", "Helvetica Neue", Helvetica, Arial, cursive;
   font-size: 16px;
 }
-.bmbf {
-  width: 50px;
-  margin-left: auto;
-  margin-right: auto;
-}
 .supersmall {
   padding-left: 10px;
   font-size: 0.8em;
@@ -3246,12 +3234,6 @@ figure figcaption {
 .leftspace {
   padding-left: 40px;
 }
-.gogssmall {
-  height: 30px;
-  align-content: center;
-  margin-left: auto;
-  margin-right: auto;
-}
 figure {
   float: right;
   max-width: 600px;
@@ -3284,17 +3266,17 @@ figure figcaption {
   float: left;
   background-color: #5b5b5b;
   color: white;
-  border-top-left-radius: .28571429rem;
-  border-bottom-left-radius: .28571429rem;
-  padding: .833em 1em;
+  border-top-left-radius: 0.28571429rem;
+  border-bottom-left-radius: 0.28571429rem;
+  padding: 0.833em 1em;
 }
 .gin.doinr {
   background-color: #2185D0;
   float: right;
   color: white;
-  border-top-right-radius: .28571429rem;
-  border-bottom-right-radius: .28571429rem;
-  padding: .833em 1em;
+  border-top-right-radius: 0.28571429rem;
+  border-bottom-right-radius: 0.28571429rem;
+  padding: 0.833em 1em;
 }
 .ui.image.label.nobg {
   background: none;
@@ -3321,7 +3303,7 @@ textarea#description {
   border-radius: 500rem;
   border-color: #767676 transparent transparent;
   border-style: solid;
-  border-width: .2em;
+  border-width: 0.2em;
   box-shadow: 0 0 0 1px transparent;
 }
 /*--------------
@@ -3408,14 +3390,19 @@ textarea#description {
   text-align: center;
 }
 #file-buttons > a:first-child {
-  border-top-left-radius: .28571429rem;
-  border-bottom-left-radius: .28571429rem;
+  border-top-left-radius: 0.28571429rem;
+  border-bottom-left-radius: 0.28571429rem;
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
 }
 .textnode.focusable.whitespace {
   display: none !important;
 }
+.footericon {
+  height: 35px;
+  vertical-align: middle;
+}
 .footertext {
-  font-size: 0.5rem;
+  font-size: 1rem;
+  vertical-align: middle;
 }

BIN
public/img/sm_plos-logo-sm.png


+ 1 - 1
public/less/_base.less

@@ -1,4 +1,4 @@
-@footer-margin: 40px;
+@footer-margin: 100px;
 
 body:not(.full-width) {
 	font-family: "PingFang SC", "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;

+ 8 - 25
public/less/_gin.less

@@ -3,10 +3,6 @@
   font-size: 16px;
 }
 
-.bmbf {
-  width: 80px;
-}
-
 .supersmall {
   padding-left: 10px;
   font-size: 0.8em;
@@ -14,13 +10,9 @@
 }
 
 .leftspace {
-  padding-left: 40px;
+  padding-left: 20px;
 }
 
-.gogssmall {
-  height: 40px;
-  align-content: center;
-}
 
 figure {
   float: right;
@@ -57,12 +49,6 @@ figure figcaption {
   font-size: 16px;
 }
 
-.bmbf {
-  width: 50px;
-    margin-left: auto;
-    margin-right: auto;
-}
-
 .supersmall {
   padding-left: 10px;
   font-size: 0.8em;
@@ -73,13 +59,6 @@ figure figcaption {
   padding-left: 40px;
 }
 
-.gogssmall {
-  height: 30px;
-  align-content: center;
-  margin-left: auto;
-  margin-right: auto;
-}
-
 figure {
   float: right;
   max-width: 600px;
@@ -276,8 +255,12 @@ textarea#description {
     display: none !important;
 }
 
-.footertext{
-    font-size:.5rem
+.footericon {
+  height: 35px;
+  vertical-align: middle;
 }
 
-
+.footertext{
+    font-size: 1rem;
+    vertical-align: middle;
+}

+ 8 - 7
routes/api/v1/search/general.go

@@ -1,17 +1,18 @@
 package search
 
 import (
-	"github.com/G-Node/gogs/pkg/context"
-	"github.com/G-Node/gin-dex/gindex"
-	"net/http"
-	"github.com/G-Node/gogs/pkg/setting"
-	"encoding/json"
 	"bytes"
+	"encoding/json"
 	"io/ioutil"
+	"net/http"
+
+	"github.com/G-Node/gogs/pkg/context"
+	"github.com/G-Node/gogs/pkg/setting"
+	"github.com/G-Node/libgin/libgin"
 )
 
 func Search(c *context.APIContext) {
-	if ! c.IsLogged {
+	if !c.IsLogged {
 		c.Status(http.StatusUnauthorized)
 		return
 	}
@@ -19,7 +20,7 @@ func Search(c *context.APIContext) {
 		c.Status(http.StatusNotImplemented)
 		return
 	}
-	ireq := gindex.SearchRequest{Token: c.GetCookie(setting.SessionConfig.CookieName), UserID: c.User.ID,
+	ireq := libgin.SearchRequest{Token: c.GetCookie(setting.SessionConfig.CookieName), UserID: c.User.ID,
 		Querry: c.Params("querry"), CsrfT: c.GetCookie(setting.CSRFCookieName)}
 	data, err := json.Marshal(ireq)
 	if err != nil {

+ 6 - 5
routes/api/v1/search/suggest.go

@@ -1,13 +1,14 @@
 package search
 
 import (
-	"net/http"
-	"github.com/G-Node/gin-dex/gindex"
-	"encoding/json"
 	"bytes"
+	"encoding/json"
 	"io/ioutil"
+	"net/http"
+
 	"github.com/G-Node/gogs/pkg/context"
 	"github.com/G-Node/gogs/pkg/setting"
+	"github.com/G-Node/libgin/libgin"
 )
 
 func Suggest(c *context.APIContext) {
@@ -15,8 +16,8 @@ func Suggest(c *context.APIContext) {
 		c.Status(http.StatusNotImplemented)
 		return
 	}
-	ireq := gindex.SearchRequest{Token: c.GetCookie(setting.SessionConfig.CookieName),
-		Querry: c.Params("querry"), CsrfT: c.GetCookie(setting.CSRFCookieName), SType:gindex.SEARCH_SUGGEST}
+	ireq := libgin.SearchRequest{Token: c.GetCookie(setting.SessionConfig.CookieName),
+		Querry: c.Params("querry"), CsrfT: c.GetCookie(setting.CSRFCookieName), SType: libgin.SEARCH_SUGGEST}
 	if c.IsLogged {
 		ireq.UserID = c.User.ID
 	}

+ 35 - 5
routes/doi.go

@@ -1,13 +1,17 @@
 package routes
 
 import (
-	"github.com/G-Node/gogs/pkg/context"
-	"github.com/G-Node/gogs/pkg/setting"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"encoding/base64"
 	"fmt"
+	"io"
 	"net/http"
-	"github.com/G-Node/gin-doi/src"
-	log "gopkg.in/clog.v1"
 
+	"github.com/G-Node/gogs/pkg/context"
+	"github.com/G-Node/gogs/pkg/setting"
+	log "gopkg.in/clog.v1"
 )
 
 func RequestDoi(c *context.Context) {
@@ -16,7 +20,7 @@ func RequestDoi(c *context.Context) {
 		return
 	}
 	token := c.GetCookie(setting.SessionConfig.CookieName)
-	token, err := ginDoi.Encrypt([]byte(setting.Doi.DoiKey), token)
+	token, err := encrypt([]byte(setting.Doi.DoiKey), token)
 	if err != nil {
 		log.Error(0, "Could not encrypt Secret key:%s", err)
 		c.Status(http.StatusInternalServerError)
@@ -26,3 +30,29 @@ func RequestDoi(c *context.Context) {
 		c.User.Name, token)
 	c.Redirect(url)
 }
+
+// NOTE: TEMPORARY COPY FROM gin-doi
+
+// encrypt string to base64 crypto using AES
+func encrypt(key []byte, text string) (string, error) {
+	plaintext := []byte(text)
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return "", err
+	}
+
+	// The IV needs to be unique, but not secure. Therefore it's common to
+	// include it at the beginning of the ciphertext.
+	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+	iv := ciphertext[:aes.BlockSize]
+	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+		return "", err
+	}
+
+	stream := cipher.NewCFBEncrypter(block, iv)
+	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+
+	// convert to base64
+	return base64.URLEncoding.EncodeToString(ciphertext), nil
+}

+ 21 - 23
routes/repo/view.go

@@ -5,24 +5,21 @@
 package repo
 
 import (
+	"bufio"
 	"bytes"
+	"encoding/json"
+	"encoding/xml"
 	"fmt"
 	gotemplate "html/template"
+	"io"
 	"io/ioutil"
+	"os"
 	"path"
 	"strings"
 
-	"github.com/Unknwon/paginater"
-	log "gopkg.in/clog.v1"
-
 	"github.com/G-Node/git-module"
-
-	"bufio"
-	"io"
-	"os"
-
-	"github.com/G-Node/gin-doi/src"
 	"github.com/G-Node/go-annex"
+	"github.com/G-Node/godML/odml"
 	"github.com/G-Node/gogs/models"
 	"github.com/G-Node/gogs/pkg/context"
 	"github.com/G-Node/gogs/pkg/markup"
@@ -30,12 +27,12 @@ import (
 	"github.com/G-Node/gogs/pkg/template"
 	"github.com/G-Node/gogs/pkg/template/highlight"
 	"github.com/G-Node/gogs/pkg/tool"
+	"github.com/G-Node/libgin/libgin"
+	"github.com/Unknwon/paginater"
 	"github.com/go-macaron/captcha"
-	"gopkg.in/yaml.v2"
-	"github.com/G-Node/godML/odml"
-	"encoding/json"
-	"encoding/xml"
 	"golang.org/x/net/html/charset"
+	log "gopkg.in/clog.v1"
+	"gopkg.in/yaml.v2"
 )
 
 const (
@@ -67,7 +64,7 @@ func renderDirectory(c *context.Context, treeLink string) {
 	c.Data["DOI"] = false
 	var readmeFile *git.Blob
 	for _, entry := range entries {
-		if entry.IsDir() || (!markup.IsReadmeFile(entry.Name()) && !(entry.Name() == "datacite.yml") && !(entry.Name() == "LICENSE")) {
+		if entry.IsDir() || !markup.IsReadmeFile(entry.Name()) && entry.Name() != "datacite.yml" && entry.Name() != "LICENSE" {
 			continue
 		}
 
@@ -76,24 +73,24 @@ func renderDirectory(c *context.Context, treeLink string) {
 			setting.UI.MaxDisplayFileSize {
 			readmeFile = entry.Blob()
 		} else if entry.Name() == "datacite.yml" {
-			c.Data["DOI"] = true
+			c.Data["HasDatacite"] = true
 			doiData, err := entry.Blob().Data()
 			if err != nil {
-				log.Trace("Doi Blob could not be read:%v", err)
+				log.Trace("DOI Blob could not be read: %v", err)
 			}
 			buf, err := ioutil.ReadAll(doiData)
-			doiInfo := ginDoi.CBerry{}
+			doiInfo := libgin.DOIRegInfo{}
 			err = yaml.Unmarshal(buf, &doiInfo)
 			if err != nil {
-				log.Trace("Doi Blob could not be unmarshalled:%v", err)
+				log.Trace("DOI Blob could not be unmarshalled: %v", err)
 			}
-			c.Data["DoiInfo"] = &doiInfo
+			c.Data["DOIInfo"] = &doiInfo
 
 			doi := GDoiRepo(c, setting.Doi.DoiBase)
 			//ddata, err := ginDoi.GDoiMData(doi, "https://api.datacite.org/works/") //todo configure URL?
 
-			c.Data["DoiReg"] = ginDoi.IsRegsitredDoi(doi)
-			c.Data["doi"] = doi
+			c.Data["DOIReg"] = libgin.IsRegisteredDOI(doi)
+			c.Data["DOI"] = doi
 
 		}
 	}
@@ -511,11 +508,12 @@ func Forks(c *context.Context) {
 
 // Get the (theoretical ) doi for a repository. Make sure its tge doi for the Base repo
 // in cas eits a repo from the doi user
-func GDoiRepo(c *context.Context, doiBAse string) string {
+func GDoiRepo(c *context.Context, doiBase string) string {
 	repoN := c.Repo.Repository.FullName()
 	// check whether this repo belongs to doi and is a fork
 	if c.Repo.Repository.IsFork && c.Repo.Owner.Name == "doi" {
 		repoN = c.Repo.Repository.BaseRepo.FullName()
 	}
-	return ginDoi.MakeDoi(ginDoi.RepoP2UUID(repoN), doiBAse)
+	uuid := libgin.RepoPathToUUID(repoN)
+	return doiBase + uuid[:6]
 }

+ 22 - 13
routes/search.go

@@ -1,16 +1,17 @@
 package routes
 
 import (
-	"github.com/G-Node/gogs/pkg/context"
-	"net/http"
-	"github.com/G-Node/gin-dex/gindex"
-	"encoding/json"
 	"bytes"
-	"io/ioutil"
-	"github.com/G-Node/gogs/pkg/setting"
+	"encoding/json"
 	"fmt"
+	"io/ioutil"
+	"net/http"
 	"strconv"
-	"github.com/Sirupsen/logrus"
+
+	"github.com/G-Node/gogs/pkg/context"
+	"github.com/G-Node/gogs/pkg/setting"
+	"github.com/G-Node/libgin/libgin"
+	log "gopkg.in/clog.v1"
 )
 
 const (
@@ -24,7 +25,7 @@ func Search(c *context.Context, keywords string, sType int64) ([]byte, error) {
 		return nil, fmt.Errorf("Extended search not implemented")
 	}
 
-	ireq := gindex.SearchRequest{Token: c.GetCookie(setting.SessionConfig.CookieName),
+	ireq := libgin.SearchRequest{Token: c.GetCookie(setting.SessionConfig.CookieName),
 		Querry: keywords, CsrfT: c.GetCookie(setting.CSRFCookieName), SType: sType, UserID: -10}
 	if c.IsLogged {
 		ireq.UserID = c.User.ID
@@ -59,7 +60,7 @@ func ExploreData(c *context.Context) {
 	c.Data["Keywords"] = keywords
 	sType, err := strconv.ParseInt(c.Query("stype"), 10, 0)
 	if err != nil {
-		logrus.Errorf("Serach type not understood:%+v", err)
+		log.Error(2, "Search type not understood: %s", err.Error())
 		sType = 0
 	}
 	data, err := Search(c, keywords, sType)
@@ -68,7 +69,7 @@ func ExploreData(c *context.Context) {
 		return
 	}
 
-	res := gindex.SearchResults{}
+	res := libgin.SearchResults{}
 	err = json.Unmarshal(data, &res)
 	if err != nil {
 		c.Handle(http.StatusInternalServerError, "Could not display result", err)
@@ -87,17 +88,17 @@ func ExploreCommits(c *context.Context) {
 	keywords := c.Query("q")
 	sType, err := strconv.ParseInt(c.Query("stype"), 10, 0)
 	if err != nil {
-		logrus.Errorf("Serach type not understood:%+v", err)
+		log.Error(2, "Search type not understood: %s", err.Error())
 		sType = 0
 	}
 	data, err := Search(c, keywords, sType)
 
 	if err != nil {
-		c.Handle(http.StatusInternalServerError, "Could nor querry", err)
+		c.Handle(http.StatusInternalServerError, "Could not query", err)
 		return
 	}
 
-	res := gindex.SearchResults{}
+	res := libgin.SearchResults{}
 	err = json.Unmarshal(data, &res)
 	if err != nil {
 		c.Handle(http.StatusInternalServerError, "Could not display result", err)
@@ -106,3 +107,11 @@ func ExploreCommits(c *context.Context) {
 	c.Data["Commits"] = res.Commits
 	c.HTML(200, EXPLORE_COMMITS)
 }
+
+type SearchRequest struct {
+	Token  string
+	CsrfT  string
+	UserID int64
+	Querry string
+	SType  int64
+}

+ 31 - 61
templates/base/footer.tmpl

@@ -1,79 +1,49 @@
 {{/*
 <html>
 <body>
-<div>
-	*/}}
-</div>
-<footer>
-	<div class="following bar light">
-	<div class="ui container">
-	<div class="ui grid">
-		<div class="column">
-			<div class="ui top secondary menu center">
-				<a class="item brand footertext" href="http://www.g-node.org">
-					<img class="ui mini image"
-							 src="https://projects.g-node.org/assets/gnode-bootstrap-theme/1.2.0-snapshot/img/gnode-icon-50x50-transparent.png"/>
-					© G-Node, 2016-2018
-				</a>
-				<a class="item brand footertext" href="/G-Node/Info/wiki/about">About</a>
-				<a class="item brand footertext" href="/G-Node/Info/wiki/imprint">Imprint</a>
-				<a class="item brand footertext" href="/G-Node/Info/wiki/contact">Contact</a>
-				<a class="item brand footertext" href="/G-Node/Info/wiki/Terms+of+Use">Terms of Use</a>
-				<a class="item brand footertext " href="/G-Node/Info/wiki/Datenschutz">Datenschutz</a>
-
-
-				<div class="ui supersmall leftspace">
-					Powered by:
-					<a href="https://github.com/gogits/gogs">
-						<img class="ui gogssmall image" src="{{AppSubURL}}/img/gogs.svg"/>
-					</a>
-				</div>
-				<div class="ui supersmall">
-					Hosted by:
-					<a href="http://neuro.bio.lmu.de">
-					<img class="ui bmbf image" src="{{AppSubURL}}/img/lmu.png"/>
-					</a>
-				</div>
-				<div class="ui supersmall">
-					Funded by:
-					<a href="http://www.bmbf.de">
-					<img class="ui bmbf image" src="{{AppSubURL}}/img/bmbf.png"/>
-					</a>
-				</div>
-				<div class="ui supersmall">
-					Registered with:
-					<a href="http://doi.org/10.17616/R3SX9N">
-						<img class="ui bmbf image" src="{{AppSubURL}}/img/re3.png"/>
-					</a>
-				</div>
-				<div class="ui supersmall">
-					Recommended by:
-					<a href="https://www.nature.com/sdata/policies/repositories#neurosci">
-						<img class="ui gogssmall image" src="{{AppSubURL}}/img/sdatarecbadge.jpg"/>
-					</a>
-				</div>
+	<div>
+*/}}
+	</div>
+	<footer>
+		<div class="ui container">
+			<div class="ui center links item brand footertext">
+				<a href="http://www.g-node.org"><img class="ui mini footericon" src="https://projects.g-node.org/assets/gnode-bootstrap-theme/1.2.0-snapshot/img/gnode-icon-50x50-transparent.png"/>© G-Node, 2016-2019</a>
+				<a href="/G-Node/Info/wiki/about">About</a>
+				<a href="/G-Node/Info/wiki/imprint">Imprint</a>
+				<a href="/G-Node/Info/wiki/contact">Contact</a>
+				<a href="/G-Node/Info/wiki/Terms+of+Use">Terms of Use</a>
+				<a href="/G-Node/Info/wiki/Datenschutz">Datenschutz</a>
+			</div>
+			<div class="ui center links item brand footertext">
+				<span>Powered by:      <a href="https://github.com/gogits/gogs"><img class="ui mini footericon" src="{{AppSubURL}}/img/gogs.svg"/></a>         </span>
+				<span>Hosted by:       <a href="http://neuro.bio.lmu.de"><img class="ui mini footericon" src="{{AppSubURL}}/img/lmu.png"/></a>          </span>
+				<span>Funded by:       <a href="http://www.bmbf.de"><img class="ui mini footericon" src="{{AppSubURL}}/img/bmbf.png"/></a>         </span>
+				<span>Registered with: <a href="http://doi.org/10.17616/R3SX9N"><img class="ui mini footericon" src="{{AppSubURL}}/img/re3.png"/></a>          </span>
+				<span>Recommended by:  <a href="https://www.nature.com/sdata/policies/repositories#neurosci"><img class="ui mini footericon" src="{{AppSubURL}}/img/sdatarecbadge.jpg"/><a href="https://journals.plos.org/plosone/s/data-availability#loc-neuroscience"><img class="ui mini footericon" src="{{AppSubURL}}/img/sm_plos-logo-sm.png"/></a></span>
 			</div>
 		</div>
-	</div>
-</footer>
+	</footer>
 </body>
 
 <!-- Third-party libraries -->
 {{if .RequireHighlightJS}}
-<link rel="stylesheet" href="{{AppSubURL}}/plugins/highlight-9.6.0/github.css">
-<script src="{{AppSubURL}}/plugins/highlight-9.6.0/highlight.pack.js"></script>
+	<link rel="stylesheet" href="{{AppSubURL}}/plugins/highlight-9.6.0/github.css">
+	<script src="{{AppSubURL}}/plugins/highlight-9.6.0/highlight.pack.js"></script>
 {{end}}
 {{if .RequireMinicolors}}
-<link rel="stylesheet" href="{{AppSubURL}}/plugins/jquery.minicolors-2.2.3/jquery.minicolors.css">
-<script src="{{AppSubURL}}/plugins/jquery.minicolors-2.2.3/jquery.minicolors.min.js"></script>
+	<link rel="stylesheet" href="{{AppSubURL}}/plugins/jquery.minicolors-2.2.3/jquery.minicolors.css">
+	<script src="{{AppSubURL}}/plugins/jquery.minicolors-2.2.3/jquery.minicolors.min.js"></script>
 {{end}}
 {{if .RequireDatetimepicker}}
-<link rel="stylesheet" href="{{AppSubURL}}/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.css">
-<script src="{{AppSubURL}}/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.js"></script>
+	<link rel="stylesheet" href="{{AppSubURL}}/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.css">
+	<script src="{{AppSubURL}}/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.js"></script>
 {{end}}
 {{if .RequireDropzone}}
-<link rel="stylesheet" href="{{AppSubURL}}/plugins/dropzone-4.2.0/dropzone.css">
-<script src="{{AppSubURL}}/plugins/dropzone-4.2.0/dropzone.js"></script>
+	<link rel="stylesheet" href="{{AppSubURL}}/plugins/dropzone-4.2.0/dropzone.css">
+	<script src="{{AppSubURL}}/plugins/dropzone-4.2.0/dropzone.js"></script>
+{{end}}
+{{if .RequireAutosize}}
+	<script src="{{AppSubURL}}/plugins/autosize-4.0.2/autosize.min.js"></script>
 {{end}}
 <script src="{{AppSubURL}}/js/libs/emojify-1.1.0.min.js"></script>
 <script src="{{AppSubURL}}/js/libs/clipboard-1.5.9.min.js"></script>

+ 1 - 1
templates/mail/auth/activate.tmpl

@@ -10,6 +10,6 @@
 	<p>Please click the following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>:</p>
 	<p><a href="{{AppURL}}user/activate?code={{.Code}}">{{AppURL}}user/activate?code={{.Code}}</a></p>
 	<p>Not working? Try copying and pasting it to your browser.</p>
-	<p>© 2017 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
+	<p>© 2019 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
 </body>
 </html>

+ 1 - 1
templates/mail/auth/activate_email.tmpl

@@ -10,6 +10,6 @@
 	<p>Please click the following link to verify your email address within <b>{{.ActiveCodeLives}} hours</b>:</p>
 	<p><a href="{{AppURL}}user/activate_email?code={{.Code}}&email={{.Email}}">{{AppURL}}user/activate_email?code={{.Code}}&email={{.Email}}</a></p>
 	<p>Not working? Try copying and pasting it to your browser.</p>
-	<p>© 2017 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
+	<p>© 2019 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
 </body>
 </html>

+ 1 - 1
templates/mail/auth/invite_email.tmpl

@@ -17,6 +17,6 @@
 <p>Not working? Try copying and pasting it to your browser.</p>
 <p>If you are not interested just ignore this mail!</p>
 <p> All the best from Munich!</p>
-<p>© 2017 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
+<p>© 2019 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
 </body>
 </html>

+ 1 - 1
templates/mail/auth/register_notify.tmpl

@@ -9,6 +9,6 @@
 	<p>Hi <b>{{.Username}}</b>, this is your registration confirmation email for {{AppName}}!</p>
 	<p>You can now login via username: {{.Username}}.</p>
 	<p><a href="{{AppURL}}user/login">{{AppURL}}user/login</a></p>
-	<p>© 2017 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
+	<p>© 2019 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
 </body>
 </html>

+ 1 - 1
templates/mail/auth/reset_passwd.tmpl

@@ -10,6 +10,6 @@
 <p>Please click the following link to verify your email address within <b>{{.ResetPwdCodeLives}} hours</b>:</p>
 <p><a href="{{AppURL}}user/reset_password?code={{.Code}}">{{AppURL}}user/reset_password?code={{.Code}}</a></p>
 <p>Not working? Try copying and pasting it to your browser.</p>
-<p>© 2017 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
+<p>© 2019 <a target="_blank" href="{{AppURL}}">{{AppName}}</a></p>
 </body>
 </html>

+ 12 - 12
templates/repo/doifile.tmpl

@@ -13,36 +13,36 @@
 				<tbody>
 				<tr>
 						<td>Title</td>
-						<td>{{.DoiInfo.Title}}</td>
+						<td>{{.DOIInfo.Title}}</td>
 				</tr>
 				<tr>
 						<td>Authors</td>
 						<td>
-								{{range $index, $auth := .DoiInfo.Authors}}
+								{{range $index, $auth := .DOIInfo.Authors}}
 								{{ $auth.RenderAuthor }}
 								<br>
 								{{end}}
 						</td>
 				</tr>
-				{{if .DoiInfo.Description}}
+				{{if .DOIInfo.Description}}
 				<tr>
 						<td>Description</td>
-						<td>{{.DoiInfo.Description}}
+						<td>{{.DOIInfo.Description}}
 						</td>
 				</tr>
 				{{end}}
-				{{if .DoiInfo.License}}
+				{{if .DOIInfo.License}}
 				<tr>
 						<td>License</td>
-						<td>{{.DoiInfo.License.Name}} ({{.DoiInfo.License.Url}})
+						<td>{{.DOIInfo.License.Name}} ({{.DOIInfo.License.URL}})
 						</td>
 				</tr>
 				{{end}}
 				<tr>
 						<td>References</td>
 						<td>
-								{{range $index, $ref := .DoiInfo.References}}
-								{{ $ref.Name }} [{{ $ref.Doi }}] ({{ $ref.Reftype }})
+								{{range $index, $ref := .DOIInfo.References}}
+								{{ $ref.Name }} [{{ $ref.DOI }}] ({{ $ref.Reftype }})
 								<br>
 								{{end}}
 						</td>
@@ -50,17 +50,17 @@
 				<tr>
 						<td>Funding</td>
 						<td>
-								{{range $index, $ref := .DoiInfo.Funding}}
+								{{range $index, $ref := .DOIInfo.Funding}}
 								{{ $ref}}
 								<br>
 								{{end}}
 						</td>
 				</tr>
-				{{if .DoiInfo.Keywords}}
+				{{if .DOIInfo.Keywords}}
 				<tr>
 						<td>Keywords</td>
 						<td>
-								{{range $index, $sub := .DoiInfo.Keywords}}
+								{{range $index, $sub := .DOIInfo.Keywords}}
 								{{ $sub }}
 								<br>
 								{{end}}
@@ -69,7 +69,7 @@
 				<tr>
 					<td>Resource Type</td>
 					<td>
-						<i>{{.DoiInfo.GetType}}</i><br>
+						<i>{{.DOIInfo.GetType}}</i><br>
 					</td>
 				</tr>
 				{{end}}

+ 4 - 4
templates/repo/header.tmpl

@@ -33,8 +33,8 @@
 									</a>
 								</div>
 							{{end}}
-							{{if not $.DoiReg}}
-								{{if and (and $.DOI $.IsRepositoryAdmin) (not .IsPrivate)}}
+							{{if not $.DOIReg}}
+								{{if and (and $.HasDatacite $.IsRepositoryAdmin) (not .IsPrivate)}}
 								<div class="ui labeled button" tabindex="0">
 									<a class="ui basic button"
 										 href="/{{.Owner.Name}}/{{.Name}}/doi">
@@ -60,10 +60,10 @@
 								{{end}}
 							{{else}}
 										<div class="nobg ui image label">
-											<a href="https://doi.org/{{$.doi}}">
+											<a href="https://doi.org/{{$.DOI}}">
 												<div class="gin doi">DOI</div>
 											</a>
-												<div class="gin doinr">{{$.doi}}</div>
+												<div class="gin doinr">{{$.DOI}}</div>
 										</div>
 							{{end}}
 						</div>

+ 1 - 1
templates/repo/view_list.tmpl

@@ -71,6 +71,6 @@
 {{if and .ReadmeExist .IsTextFile}}
 	{{template "repo/view_file" .}}
 {{end}}
-{{if and .DOI .IsTextFile}}
+{{if and .HasDatacite .IsTextFile}}
 	{{template "repo/doifile" .}}
 {{end}}

+ 8 - 13
templates/user/auth/signup.tmpl

@@ -12,11 +12,9 @@
 					{{if .DisableRegistration}}
 						<p>{{.i18n.Tr "auth.disable_register_prompt"}}</p>
 					{{else}}
-					<div class="ui piled yellow segment">
-						<h4 class="ui header">Please note!</h4>
-						For Registration we require only username, password and email. Please use an institutional email to
-						register. Otherwise you will only be able to use a subset of gins functionality and your maximum
-						repository size will be dramatically reduced
+					<div class="ui piled segment">
+						For Registration we require only username, password, and a valid email address, but adding your name and affiliation is recommended.
+						Please use an institutional email address for registration to benefit from the full set of features of GIN.
 					</div>
 					<div class="ui required inline field {{if .Err_UserName}}error{{end}}"
 							 data-tooltip="Username is used to create and to display your repositories. Choose wisely">
@@ -37,11 +35,11 @@
 						<input id="retype" name="retype" type="password" value="{{.retype}}" required>
 					</div>
 					<div class="inline field">
-						<label for="retype">Full Name</label>
+						<label for="full_name">Full Name</label>
 						<input id="full_name" name="full_name" value="{{.full_name}}">
 					</div>
 					<div class="inline field">
-						<label for="retype">Affiliation</label>
+						<label for="affiliation">Affiliation</label>
 						<input id="affiliation" name="affiliation" value="{{.affiliation}}">
 					</div>
 					{{if .EnableCaptcha}}
@@ -54,15 +52,12 @@
 						<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
 					</div>
 					{{end}}
-
-					<div class="inline field">
-						<label></label>
-						<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button>
+					<div class="ui segment">
+						By clicking "{{.i18n.Tr "auth.create_new_account"}}"</a>, you agree to our <a href="/G-Node/Info/wiki/Terms+of+Use">Terms of Use</a> and <a href="/G-Node/Info/wiki/Datenschutz">Data Processing Policy</a>.
 					</div>
 					<div class="inline field">
 						<label></label>
-						<a href="/G-Node/Info/wiki/Terms+of+Use">By clicking "{{.i18n.Tr "auth.create_new_account"}}", you agree to
-							our Terms of Use.</a>
+						<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button>
 					</div>
 					{{end}}
 				</div>

+ 29 - 0
vendor/github.com/G-Node/gig/LICENSE

@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2017, German Neuroinformatics Node
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 2 - 0
vendor/github.com/G-Node/gig/README.md

@@ -0,0 +1,2 @@
+# gig
+gig  is (some) Git in Go

+ 368 - 0
vendor/github.com/G-Node/gig/delta.go

@@ -0,0 +1,368 @@
+package gig
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+)
+
+//Delta represents a git delta representation. Either BaseRef
+//or BaseOff are valid fields, depending on its Type().
+type Delta struct {
+	gitObject
+
+	BaseRef    SHA1
+	BaseOff    int64
+	SizeSource int64
+	SizeTarget int64
+
+	pf  *PackFile
+	op  DeltaOp
+	err error
+}
+
+func readByte(r io.Reader) (byte, error) {
+	var err error
+	n := 0
+	var b [1]byte
+
+	for n != 1 && err == nil {
+		n, err = r.Read(b[:])
+	}
+
+	if n != 1 {
+		return 0, err
+	}
+
+	return b[0], nil
+}
+
+func parseDelta(obj gitObject) (*Delta, error) {
+	delta := Delta{gitObject: obj}
+
+	//all delta objects come from a PackFile and
+	//therefore git.Source is must be a *packReader
+	source := delta.source.(*packReader)
+	delta.pf = source.fd
+
+	var err error
+	if obj.otype == ObjRefDelta {
+		_, err = source.Read(delta.BaseRef[:])
+		//TODO: check n?
+
+		if err != nil {
+			return nil, err
+		}
+
+	} else {
+		off, err := readVarint(source)
+		if err != nil {
+			return nil, err
+		}
+
+		delta.BaseOff = source.start - off
+	}
+
+	err = delta.wrapSourceWithDeflate()
+	if err != nil {
+		return nil, err
+	}
+
+	delta.SizeSource, err = readVarSize(delta.source, 0)
+	if err != nil {
+		return nil, err
+	}
+
+	delta.SizeTarget, err = readVarSize(delta.source, 0)
+	if err != nil {
+		return nil, err
+	}
+
+	return &delta, nil
+}
+
+func readVarSize(r io.Reader, offset uint) (size int64, err error) {
+	size = int64(0)
+	b := byte(0x80)
+
+	// [0111 1111 ... 1111] (int64) is biggest decode-able
+	// value we get by shifting byte b = 0x7F [0111 1111]
+	// left 8*7 = 56 times; the next attempt must overflow.
+	for i := offset; b&0x80 != 0 && i < 57; i += 7 {
+		b, err = readByte(r)
+		if err != nil {
+			return 0, fmt.Errorf("git: io error: %v", err)
+		}
+
+		size |= int64(b&0x7F) << i
+	}
+
+	// means i > 56, would overflow (see above).
+	if b&0x80 != 0 {
+		return 0, fmt.Errorf("int64 overflow")
+	}
+
+	return size, nil
+}
+
+func decodeInt(r io.Reader, b byte, l uint) (size int64, err error) {
+
+	for i := uint(0); i < l; i++ {
+
+		if b&(1<<i) != 0 {
+			var d byte
+			d, err = readByte(r)
+			if err != nil {
+				return
+			}
+
+			size |= int64(d) << (i * 8)
+		}
+	}
+
+	return
+}
+
+func readVarint(r io.Reader) (int64, error) {
+	b, err := readByte(r)
+	if err != nil {
+		return 0, fmt.Errorf("git: io error: %v", err)
+	}
+
+	size := int64(b & 0x7F)
+
+	for b&0x80 != 0 {
+		b, err = readByte(r)
+		if err != nil {
+			return 0, fmt.Errorf("git: io error: %v", err)
+		}
+
+		size++
+
+		// [0000 0001 ... 0000] (int64)
+		//          ^ bit 0x38 (56)
+		// shifting by 7 will shift the bit into the
+		// sign bit of int64, i.e. we have overflow.
+		if size > (1<<0x38)-1 {
+			return 0, fmt.Errorf("int64 overflow")
+		}
+
+		size = (size << 7) + int64(b&0x7F)
+	}
+
+	return size, nil
+}
+
+//DeltaOpCode is the operation code for delta compression
+//instruction set.
+type DeltaOpCode byte
+
+//DeltaOpCode values.
+const (
+	DeltaOpInsert = 1 //insert data from the delta data into dest
+	DeltaOpCopy   = 2 //copy data from the original source into dest
+)
+
+//DeltaOp represents the delta compression operation. Offset is
+//only valid for DeltaOpCopy operations.
+type DeltaOp struct {
+	Op     DeltaOpCode
+	Size   int64
+	Offset int64
+}
+
+//Op returns the current operations
+func (d *Delta) Op() DeltaOp {
+	return d.op
+}
+
+//Err retrieves the current error state, if any
+func (d *Delta) Err() error {
+	if err := d.err; err != io.EOF {
+		return err
+	}
+	return nil
+}
+
+//NextOp reads the next DeltaOp from the delta data stream.
+//Returns false when there are no operations left or on error;
+//use Err() to decide between the two cases.
+func (d *Delta) NextOp() bool {
+
+	if d.err != nil {
+		return false
+	}
+
+	b, err := readByte(d.source)
+	if err != nil {
+		return false
+	}
+
+	if b&0x80 != 0 {
+		d.op.Op = DeltaOpCopy
+		op := b & 0x7F
+		d.op.Offset, d.err = decodeInt(d.source, op, 4)
+		if d.err != nil {
+			return false
+		}
+
+		d.op.Size, d.err = decodeInt(d.source, op>>4, 3)
+		if d.err != nil {
+			return false
+		}
+
+		if d.op.Size == 0 {
+			d.op.Size = 0x10000
+		}
+	} else if n := b; n > 0 {
+		d.op.Op = DeltaOpInsert
+		d.op.Size = int64(n)
+	} else {
+		d.err = fmt.Errorf("git: unknown delta op code")
+		return false
+	}
+
+	return true
+}
+
+//Patch applies the delta data onto r and writes the result to w.
+func (d *Delta) Patch(r io.ReadSeeker, w io.Writer) error {
+
+	for d.NextOp() {
+		op := d.Op()
+		switch op.Op {
+		case DeltaOpCopy:
+			_, err := r.Seek(op.Offset, os.SEEK_SET)
+			if err != nil {
+				return err
+			}
+
+			_, err = io.CopyN(w, r, op.Size)
+			if err != nil {
+				return err
+			}
+		case DeltaOpInsert:
+			_, err := io.CopyN(w, d.source, op.Size)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return d.Err()
+}
+
+//SkipOp prepares the delta stream to move to the next operation
+//without actually carrying out the delta operation. Useful for
+//printing the delta stream.
+func (d *Delta) SkipOp() {
+	op := d.Op()
+	if op.Op == DeltaOpInsert {
+		_, d.err = io.CopyN(ioutil.Discard, d.source, op.Size)
+	}
+}
+
+//WriteTo would write the object to disk in the git object
+//representation. It is not NOT IMPLEMENTED for the delta
+//object.
+func (d *Delta) WriteTo(w io.Writer) (int64, error) {
+	return 0, fmt.Errorf("WriteTo not implemented for Delta")
+}
+
+type deltaChain struct {
+	baseObj gitObject
+	baseOff int64
+
+	links []Delta
+}
+
+func (c *deltaChain) Len() int {
+	return len(c.links)
+}
+
+type objectSource interface {
+	openRawObject(id SHA1) (gitObject, error)
+}
+
+func buildDeltaChain(d *Delta, s objectSource) (*deltaChain, error) {
+	var chain deltaChain
+	var err error
+
+	for err == nil {
+
+		chain.links = append(chain.links, *d)
+
+		var obj gitObject
+		if d.otype == ObjRefDelta {
+			obj, err = s.openRawObject(d.BaseRef)
+		} else {
+			obj, err = d.pf.readRawObject(d.BaseOff)
+		}
+
+		if err != nil {
+			break
+		}
+
+		if IsStandardObject(obj.otype) {
+			chain.baseObj = obj
+			chain.baseOff = d.BaseOff
+			break
+		} else if !IsDeltaObject(obj.otype) {
+			err = fmt.Errorf("git: unexpected object type in delta chain")
+			break
+		}
+
+		d, err = parseDelta(obj)
+	}
+
+	if err != nil {
+		//cleanup
+		return nil, err
+	}
+
+	return &chain, nil
+}
+
+func (c *deltaChain) resolve() (Object, error) {
+
+	ibuf := bytes.NewBuffer(make([]byte, 0, c.baseObj.Size()))
+	n, err := io.Copy(ibuf, c.baseObj.source)
+	if err != nil {
+		return nil, err
+	}
+
+	if n != c.baseObj.Size() {
+		return nil, io.ErrUnexpectedEOF
+	}
+
+	obuf := bytes.NewBuffer(make([]byte, 0, c.baseObj.Size()))
+
+	for i := len(c.links); i > 0; i-- {
+		lk := c.links[i-1]
+
+		if lk.SizeTarget > int64(^uint(0)>>1) {
+			return nil, fmt.Errorf("git: target to large for delta unpatching")
+		}
+
+		obuf.Grow(int(lk.SizeTarget))
+		obuf.Truncate(0)
+
+		err = lk.Patch(bytes.NewReader(ibuf.Bytes()), obuf)
+
+		if err != nil {
+			return nil, err
+		}
+
+		if lk.SizeTarget != int64(obuf.Len()) {
+			return nil, fmt.Errorf("git: size mismatch while patching delta object")
+		}
+
+		obuf, ibuf = ibuf, obuf
+	}
+
+	//ibuf is holding the data
+	obj := gitObject{c.baseObj.otype, int64(ibuf.Len()), ioutil.NopCloser(ibuf)}
+	return parseObject(obj)
+}

+ 233 - 0
vendor/github.com/G-Node/gig/objects.go

@@ -0,0 +1,233 @@
+package gig
+
+import (
+	"encoding/hex"
+	"fmt"
+	"io"
+	"os"
+	"strings"
+	"time"
+)
+
+//SHA1 is the object identifying checksum of
+// the object data
+type SHA1 [20]byte
+
+func (oid SHA1) String() string {
+	return hex.EncodeToString(oid[:])
+}
+
+//ParseSHA1 expects a string with a hex encoded sha1.
+//It will trim the string of newline and space before
+//parsing.
+func ParseSHA1(input string) (sha SHA1, err error) {
+	data, err := hex.DecodeString(strings.Trim(input, " \n"))
+	if err != nil {
+		return
+	} else if len(data) != 20 {
+		err = fmt.Errorf("git: sha1 must be 20 bytes")
+		return
+	}
+
+	copy(sha[:], data)
+	return
+}
+
+//Signature is a combination of who (Name, Email) and when (Date, Offset).
+//Used by Commit, Tag to link an action (committer, author, tagger, ...)
+//with a person in a point in time.
+type Signature struct {
+	Name   string
+	Email  string
+	Date   time.Time
+	Offset *time.Location
+}
+
+func (s Signature) String() string {
+	return fmt.Sprintf("%s <%s> %d %s", s.Name, s.Email, s.Date.Unix(), s.Offset)
+}
+
+//ObjectType is to the git object type
+type ObjectType byte
+
+//The defined bits match the ones used in
+//the git pack file format.
+const (
+	_         = iota
+	ObjCommit = ObjectType(iota)
+	ObjTree
+	ObjBlob
+	ObjTag
+
+	ObjOFSDelta = ObjectType(0x6)
+	ObjRefDelta = ObjectType(0x7)
+)
+
+//ParseObjectType takes a string and converts it
+//to the corresponding ObjectType or error if
+//the string doesn't match any type.
+func ParseObjectType(s string) (ObjectType, error) {
+	s = strings.Trim(s, "\n ")
+	switch s {
+	case "commit":
+		return ObjCommit, nil
+	case "tree":
+		return ObjTree, nil
+	case "blob":
+		return ObjBlob, nil
+	case "tag":
+		return ObjTag, nil
+	}
+
+	return ObjectType(0), fmt.Errorf("git: unknown object: %q", s)
+}
+
+func (ot ObjectType) String() string {
+	switch ot {
+	case ObjCommit:
+		return "commit"
+	case ObjTree:
+		return "tree"
+	case ObjBlob:
+		return "blob"
+	case ObjTag:
+		return "tag"
+	case ObjOFSDelta:
+		return "delta-ofs"
+	case ObjRefDelta:
+		return "delta-ref"
+	}
+	return "unknown"
+}
+
+//IsStandardObject checks if an object is
+//one of the four common objects such as
+//commit, tree, blob, tag.
+func IsStandardObject(ot ObjectType) bool {
+	return ot > 0 && ot < 5
+}
+
+//IsDeltaObject checks if an object is a
+//delta object, i.e. OFSDelta or RefDelta
+func IsDeltaObject(ot ObjectType) bool {
+	return ot == ObjOFSDelta || ot == ObjRefDelta
+}
+
+//Object represents a git object. It has
+//information common to all git objects,
+//like their type and their size. Also,
+//all git objects should be closed via
+//Close().
+type Object interface {
+	Type() ObjectType
+	Size() int64
+
+	io.WriterTo
+	io.Closer
+}
+
+type gitObject struct {
+	otype ObjectType
+	size  int64
+
+	source io.ReadCloser
+}
+
+func (o *gitObject) Type() ObjectType {
+	return o.otype
+}
+
+func (o *gitObject) Size() int64 {
+	return o.size
+}
+
+func (o *gitObject) Close() error {
+	if o.source == nil {
+		return nil
+	}
+	return o.source.Close()
+}
+
+//Commit represents one git commit.
+type Commit struct {
+	gitObject
+
+	Tree      SHA1
+	Parent    []SHA1
+	Author    Signature
+	Committer Signature
+	Message   string
+	GPGSig    string
+}
+
+//Date returns the commit timestamps (with the correct location).
+func (c *Commit) Date() time.Time {
+	return c.Committer.Date.In(c.Committer.Offset)
+}
+
+//Tree represents the git tree object.
+type Tree struct {
+	gitObject
+
+	entry *TreeEntry
+	err   error
+}
+
+//TreeEntry holds information about a single
+//entry in the git Tree object.
+type TreeEntry struct {
+	Mode os.FileMode
+	Type ObjectType
+	ID   SHA1
+	Name string
+}
+
+//Next advances the pointer to the next TreeEntry
+//within the Tree object. Returns false if it was
+//pointing to the last element (EOF condition), or
+//if there was an error while advacing. Use Err()
+//to resolve between the to conditions.
+func (tree *Tree) Next() bool {
+	tree.entry, tree.err = parseTreeEntry(tree.source)
+	return tree.err == nil
+}
+
+//Err returns the last error non-EOF error encountered.
+func (tree *Tree) Err() error {
+	if err := tree.err; err != nil && err != io.EOF {
+		return err
+	}
+
+	return nil
+}
+
+//Entry returns the current TreeEntry.
+func (tree *Tree) Entry() *TreeEntry {
+	return tree.entry
+}
+
+//Blob represents a git blob object.
+type Blob struct {
+	gitObject
+}
+
+func (b *Blob) Read(data []byte) (n int, err error) {
+	n, err = b.source.Read(data)
+	return
+}
+
+func MakeAnnexBlob(fp *os.File, size int64) *Blob {
+	return &Blob{gitObject{otype: ObjBlob, size: size, source: fp}}
+}
+
+//Tag represents a git tag object.
+type Tag struct {
+	gitObject
+
+	Object  SHA1
+	ObjType ObjectType
+	Tag     string
+	Tagger  Signature
+	Message string
+	GPGSig  string
+}

+ 382 - 0
vendor/github.com/G-Node/gig/pack.go

@@ -0,0 +1,382 @@
+package gig
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"os"
+	"strings"
+)
+
+// Resources:
+//  https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt
+//  http://schacon.github.io/gitbook/7_the_packfile.html
+
+//PackHeader stores version and number of objects in the packfile
+// all data is in network-byte order (big-endian)
+type PackHeader struct {
+	Sig     [4]byte
+	Version uint32
+	Objects uint32
+}
+
+//FanOut table where the "N-th entry of this table records the
+// number of objects in the corresponding pack, the first
+// byte of whose object name is less than or equal to N.
+type FanOut [256]uint32
+
+//Bounds returns the how many objects whose first byte
+//has a value of b-1 (in s) and b (returned in e)
+//are contained in the fanout table
+func (fo FanOut) Bounds(b byte) (s, e int) {
+	e = int(fo[b])
+	if b > 0 {
+		s = int(fo[b-1])
+	}
+	return
+}
+
+//PackIndex represents the git pack file
+//index. It is the main object to use for
+//opening objects contained in packfiles
+//vai OpenObject
+type PackIndex struct {
+	*os.File
+
+	Version uint32
+	FO      FanOut
+
+	shaBase int64
+}
+
+//PackFile is git pack file with the actual
+//data in it. It should normally not be used
+//directly.
+type PackFile struct {
+	*os.File
+
+	Version  uint32
+	ObjCount uint32
+}
+
+//PackIndexOpen opens the git pack file with the given
+//path. The ".idx" if missing will be appended.
+func PackIndexOpen(path string) (*PackIndex, error) {
+
+	if !strings.HasSuffix(path, ".idx") {
+		path += ".idx"
+	}
+
+	fd, err := os.Open(path)
+
+	if err != nil {
+		return nil, fmt.Errorf("git: could not read pack index: %v", err)
+	}
+
+	idx := &PackIndex{File: fd, Version: 1}
+
+	var peek [4]byte
+	err = binary.Read(fd, binary.BigEndian, &peek)
+	if err != nil {
+		fd.Close()
+		return nil, fmt.Errorf("git: could not read pack index: %v", err)
+	}
+
+	if bytes.Equal(peek[:], []byte("\377tOc")) {
+		binary.Read(fd, binary.BigEndian, &idx.Version)
+	}
+
+	if idx.Version == 1 {
+		_, err = idx.Seek(0, 0)
+		if err != nil {
+			fd.Close()
+			return nil, fmt.Errorf("git: io error: %v", err)
+		}
+	} else if idx.Version > 2 {
+		fd.Close()
+		return nil, fmt.Errorf("git: unsupported pack index version: %d", idx.Version)
+	}
+
+	err = binary.Read(idx, binary.BigEndian, &idx.FO)
+	if err != nil {
+		idx.Close()
+		return nil, fmt.Errorf("git: io error: %v", err)
+	}
+
+	idx.shaBase = int64((idx.Version-1)*8) + int64(binary.Size(idx.FO))
+
+	return idx, nil
+}
+
+//ReadSHA1 reads the SHA1 stared at position pos (in the FanOut table).
+func (pi *PackIndex) ReadSHA1(chksum *SHA1, pos int) error {
+	if version := pi.Version; version != 2 {
+		return fmt.Errorf("git: v%d version support incomplete", version)
+	}
+
+	start := pi.shaBase
+	_, err := pi.ReadAt(chksum[0:20], start+int64(pos)*int64(20))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+//ReadOffset returns the offset in the pack file of the object
+//at position pos in the FanOut table.
+func (pi *PackIndex) ReadOffset(pos int) (int64, error) {
+	if version := pi.Version; version != 2 {
+		return -1, fmt.Errorf("git: v%d version incomplete", version)
+	}
+
+	//header[2*4] + FanOut[256*4] + n * (sha1[20]+crc[4])
+	start := int64(2*4+256*4) + int64(pi.FO[255]*24) + int64(pos*4)
+
+	var offset uint32
+
+	_, err := pi.Seek(start, 0)
+	if err != nil {
+		return -1, fmt.Errorf("git: io error: %v", err)
+	}
+
+	err = binary.Read(pi, binary.BigEndian, &offset)
+	if err != nil {
+		return -1, err
+	}
+
+	//see if msb is set, if so this is an
+	// offset into the 64b_offset table
+	if val := uint32(1<<31) & offset; val != 0 {
+		return -1, fmt.Errorf("git: > 31 bit offests not implemented. Meh")
+	}
+
+	return int64(offset), nil
+}
+
+func (pi *PackIndex) findSHA1(target SHA1) (int, error) {
+
+	//s, e and midpoint are one-based indices,
+	//where s is the index before interval and
+	//e is the index of the last element in it
+	//-> search interval is: (s | 1, 2, ... e]
+	s, e := pi.FO.Bounds(target[0])
+
+	//invariant: object is, if present, in the interval, (s, e]
+	for s < e {
+		midpoint := s + (e-s+1)/2
+
+		var sha SHA1
+		err := pi.ReadSHA1(&sha, midpoint-1)
+		if err != nil {
+			return 0, fmt.Errorf("git: io error: %v", err)
+		}
+
+		switch bytes.Compare(target[:], sha[:]) {
+		case -1: // target < sha1, new interval (s, m-1]
+			e = midpoint - 1
+		case +1: //taget > sha1, new interval (m, e]
+			s = midpoint
+		default:
+			return midpoint - 1, nil
+		}
+	}
+
+	return 0, fmt.Errorf("git: sha1 not found in index")
+}
+
+//FindOffset tries to find  object with the id target and if
+//if found returns the offset of the object in the pack file.
+//Returns an error that can be detected by os.IsNotExist if
+//the object could not be found.
+func (pi *PackIndex) FindOffset(target SHA1) (int64, error) {
+
+	pos, err := pi.findSHA1(target)
+	if err != nil {
+		return 0, err
+	}
+
+	off, err := pi.ReadOffset(pos)
+	if err != nil {
+		return 0, err
+	}
+
+	return off, nil
+}
+
+//OpenPackFile opens the corresponding pack file.
+func (pi *PackIndex) OpenPackFile() (*PackFile, error) {
+	f := pi.Name()
+	pf, err := OpenPackFile(f[:len(f)-4] + ".pack")
+	if err != nil {
+		return nil, err
+	}
+
+	return pf, nil
+}
+
+//OpenObject will try to find the object with the given id
+//in it is index and then reach out to its corresponding
+//pack file to open the actual git Object.
+//If the object cannot be found it will return an error
+//the can be detected via os.IsNotExist()
+//Delta objects will returned as such and not be resolved.
+func (pi *PackIndex) OpenObject(id SHA1) (Object, error) {
+
+	off, err := pi.FindOffset(id)
+
+	if err != nil {
+		return nil, err
+	}
+
+	pf, err := pi.OpenPackFile()
+	if err != nil {
+		return nil, err
+	}
+
+	obj, err := pf.readRawObject(off)
+
+	if err != nil {
+		return nil, err
+	}
+
+	if IsStandardObject(obj.otype) {
+		return parseObject(obj)
+	}
+
+	if !IsDeltaObject(obj.otype) {
+		return nil, fmt.Errorf("git: unsupported object")
+	}
+
+	//This is a delta object
+	delta, err := parseDelta(obj)
+
+	return delta, err
+}
+
+//OpenPackFile opens the git pack file at the given path
+//It will check the pack file header and version.
+//Currently only version 2 is supported.
+//NB: This is low-level API and should most likely
+//not be used directly.
+func OpenPackFile(path string) (*PackFile, error) {
+	osfd, err := os.Open(path)
+
+	if err != nil {
+		return nil, err
+	}
+
+	var header PackHeader
+	err = binary.Read(osfd, binary.BigEndian, &header)
+	if err != nil {
+		return nil, fmt.Errorf("git: could not read header: %v", err)
+	}
+
+	if string(header.Sig[:]) != "PACK" {
+		return nil, fmt.Errorf("git: packfile signature error")
+	}
+
+	if header.Version != 2 {
+		return nil, fmt.Errorf("git: unsupported packfile version")
+	}
+
+	fd := &PackFile{File: osfd,
+		Version:          header.Version,
+		ObjCount:         header.Objects}
+
+	return fd, nil
+}
+
+func (pf *PackFile) readRawObject(offset int64) (gitObject, error) {
+	r := newPackReader(pf, offset)
+
+	b, err := r.ReadByte()
+	if err != nil {
+		return gitObject{}, fmt.Errorf("git: io error: %v", err)
+	}
+
+	//object header format:
+	//[mxxx tttt] (byte)
+	//      tttt -> type [4 bit]
+	otype := ObjectType((b & 0x70) >> 4)
+
+	//  xxx      -> size [3 bit]
+	size := int64(b & 0xF)
+
+	// m         -> 1, if size > 2^3 (n-byte encoding)
+	if b&0x80 != 0 {
+		s, err := readVarSize(r, 4)
+		if err != nil {
+			return gitObject{}, err
+		}
+
+		size += s
+	}
+	obj := gitObject{otype, size, r}
+
+	if IsStandardObject(otype) {
+		err = obj.wrapSourceWithDeflate()
+		if err != nil {
+			return gitObject{}, err
+		}
+	}
+
+	return obj, nil
+}
+
+//OpenObject reads the git object header at offset and
+//then parses the data as the corresponding object type.
+func (pf *PackFile) OpenObject(offset int64) (Object, error) {
+
+	obj, err := pf.readRawObject(offset)
+
+	if err != nil {
+		return nil, err
+	}
+
+	switch obj.otype {
+	case ObjCommit:
+		return parseCommit(obj)
+	case ObjTree:
+		return parseTree(obj)
+	case ObjBlob:
+		return parseBlob(obj)
+	case ObjTag:
+		return parseTag(obj)
+
+	case ObjOFSDelta:
+		fallthrough
+	case ObjRefDelta:
+		return parseDelta(obj)
+
+	default:
+		return nil, fmt.Errorf("git: unknown object type")
+	}
+}
+
+type packReader struct {
+	fd    *PackFile
+	start int64
+	off   int64
+}
+
+func newPackReader(fd *PackFile, offset int64) *packReader {
+	return &packReader{fd: fd, start: offset, off: offset}
+}
+
+func (p *packReader) Read(d []byte) (n int, err error) {
+	n, err = p.fd.ReadAt(d, p.off)
+	p.off += int64(n)
+	return
+}
+
+func (p *packReader) ReadByte() (c byte, err error) {
+	var b [1]byte
+	_, err = p.Read(b[:])
+	c = b[0]
+	return
+}
+
+func (p *packReader) Close() (err error) {
+	return //noop
+}

+ 315 - 0
vendor/github.com/G-Node/gig/parse.go

@@ -0,0 +1,315 @@
+package gig
+
+import (
+	"bufio"
+	"bytes"
+	"compress/zlib"
+	"fmt"
+	"io"
+	"io/ioutil"
+
+	"os"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func parseSignature(line string) (Signature, error) {
+	//Format: "<name> <email> <unix timestamp> <time zone offset>"
+	//i.e. "A U Thor <author@example.com> 1462210432 +0200"
+
+	u := Signature{}
+
+	//<name>
+	start := strings.Index(line, " <")
+	if start == -1 {
+		return u, fmt.Errorf("invalid signature format")
+	}
+	u.Name = line[:start]
+
+	//<email>
+	end := strings.Index(line, "> ")
+	if end == -1 {
+		return u, fmt.Errorf("invalid signature format")
+	}
+	u.Email = line[start+2: end]
+
+	//<unix timestamp>
+	tstr, off := split2(line[end+2:], " ")
+	i, err := strconv.ParseInt(tstr, 10, 64)
+
+	if err != nil || len(off) != 5 {
+		return u, fmt.Errorf("invalid signature time format")
+	}
+	u.Date = time.Unix(i, 0)
+
+	//<time zone offset>
+	h, herr := strconv.Atoi(off[1:3])
+	m, merr := strconv.Atoi(off[3:])
+
+	if herr != nil || merr != nil {
+		return u, fmt.Errorf("invalid signature offset format")
+	}
+
+	o := (h*60 + m) * 60
+
+	if off[0] == '-' {
+		o *= -1
+	}
+
+	u.Offset = time.FixedZone(off, o)
+
+	return u, nil
+}
+
+func parseCommitGPGSig(r *bufio.Reader, w *bytes.Buffer) error {
+	for {
+		l, err := r.ReadString('\n')
+		if err != nil {
+			return nil
+		} else if l[0] == ' ' {
+			_, err = w.WriteString(fmt.Sprintf("\n%s", strings.Trim(l, " \n")))
+			if err != nil {
+				return err
+			}
+			continue
+		} else if l[0] == '\n' {
+			return r.UnreadByte()
+		}
+
+		return fmt.Errorf("Unexpected end of gpg signature")
+	}
+}
+
+func parseTagGPGSig(r *bufio.Reader, w *bytes.Buffer) error {
+	//!Tag signatures do not have trailing whitespaces
+	for {
+		l, err := r.ReadString('\n')
+		if err != nil {
+			return nil
+		}
+		_, err = w.WriteString(fmt.Sprintf("\n%s", strings.Trim(l, " \n")))
+		if err != nil {
+			return err
+		}
+		if !strings.Contains(l, "-----END PGP SIGNATURE-----") {
+			continue
+		} else {
+			return nil
+		}
+	}
+}
+
+func openRawObject(path string) (gitObject, error) {
+	fd, err := os.Open(path)
+	if err != nil {
+		return gitObject{}, err
+	}
+
+	// we wrap the zlib reader below, so it will be
+	// propery closed
+	r, err := zlib.NewReader(fd)
+	if err != nil {
+		return gitObject{}, fmt.Errorf("git: could not create zlib reader: %v", err)
+	}
+
+	// general object format is
+	// [type][space][length {ASCII}][\0]
+
+	line, err := readUntilNul(r)
+	if err != nil {
+		return gitObject{}, err
+	}
+
+	tstr, lstr := split2(line, " ")
+	size, err := strconv.ParseInt(lstr, 10, 64)
+
+	if err != nil {
+		return gitObject{}, fmt.Errorf("git: object parse error: %v", err)
+	}
+
+	otype, err := ParseObjectType(tstr)
+	if err != nil {
+		return gitObject{}, err
+	}
+
+	obj := gitObject{otype, size, r}
+	obj.wrapSource(r)
+
+	return obj, nil
+}
+
+func parseObject(obj gitObject) (Object, error) {
+	switch obj.otype {
+	case ObjCommit:
+		return parseCommit(obj)
+
+	case ObjTree:
+		return parseTree(obj)
+
+	case ObjBlob:
+		return parseBlob(obj)
+
+	case ObjTag:
+		return parseTag(obj)
+	}
+
+	obj.Close()
+	return nil, fmt.Errorf("git: unsupported object")
+}
+
+func parseCommit(obj gitObject) (*Commit, error) {
+	c := &Commit{gitObject: obj}
+
+	lr := &io.LimitedReader{R: obj.source, N: obj.size}
+	br := bufio.NewReader(lr)
+
+	var err error
+	for {
+		var l string
+		l, err = br.ReadString('\n')
+		head, tail := split2(l, " ")
+
+		switch head {
+		case "tree":
+			c.Tree, err = ParseSHA1(tail)
+		case "parent":
+			parent, err := ParseSHA1(tail)
+			if err == nil {
+				c.Parent = append(c.Parent, parent)
+			}
+		case "author":
+			c.Author, err = parseSignature(strings.Trim(tail, "\n"))
+		case "committer":
+			c.Committer, err = parseSignature(strings.Trim(tail, "\n"))
+		case "gpgsig":
+			sw := bytes.NewBufferString(strings.Trim(tail, "\n"))
+			err = parseCommitGPGSig(br, sw)
+			c.GPGSig = sw.String()
+		}
+
+		if err != nil || head == "\n" {
+			break
+		}
+	}
+
+	if err != nil && err != io.EOF {
+		return nil, err
+	}
+
+	data, err := ioutil.ReadAll(br)
+
+	if err != nil {
+		return nil, err
+	}
+
+	c.Message = string(data)
+	return c, nil
+}
+
+func parseTree(obj gitObject) (*Tree, error) {
+	tree := Tree{obj, nil, nil}
+	return &tree, nil
+}
+
+func parseTreeEntry(r io.Reader) (*TreeEntry, error) {
+	//format is: [mode{ASCII, octal}][space][name][\0][SHA1]
+	entry := &TreeEntry{}
+
+	l, err := readUntilNul(r) // read until \0
+
+	if err != nil {
+		return nil, err
+	}
+
+	mstr, name := split2(l, " ")
+	mode, err := strconv.ParseUint(mstr, 8, 32)
+	if err != nil {
+		return nil, err
+	}
+
+	//TODO: this is not correct because
+	// we need to shift the "st_mode" file
+	// info bits by 16
+	entry.Mode = os.FileMode(mode)
+
+	if entry.Mode == 040000 {
+		entry.Type = ObjTree
+	} else {
+		entry.Type = ObjBlob
+	}
+
+	entry.Name = name
+
+	n, err := r.Read(entry.ID[:])
+
+	if err != nil && err != io.EOF {
+		return nil, err
+	} else if err == io.EOF && n != 20 {
+		return nil, fmt.Errorf("git: unexpected EOF")
+	}
+
+	return entry, nil
+}
+
+func parseBlob(obj gitObject) (*Blob, error) {
+	blob := &Blob{obj}
+	return blob, nil
+}
+
+func parseTag(obj gitObject) (*Tag, error) {
+	c := &Tag{gitObject: obj}
+
+	lr := &io.LimitedReader{R: c.source, N: c.size}
+	br := bufio.NewReader(lr)
+	var mess bytes.Buffer
+
+	var err error
+	for {
+		var l string
+		l, err = br.ReadString('\n')
+		head, tail := split2(l, " ")
+
+		switch head {
+		case "object":
+			c.Object, err = ParseSHA1(tail)
+		case "type":
+			c.ObjType, err = ParseObjectType(tail)
+		case "tag":
+			c.Tag = strings.Trim(tail, "\n")
+		case "tagger":
+			c.Tagger, err = parseSignature(strings.Trim(tail, "\n"))
+		case "-----BEGIN":
+			//with signed tags (in difference to signed commits) the
+			// signatures do not start with "gpgsig" but just with
+			//"-----BEGIN PGP SIGNATURE-----"
+			//(tbd)
+			sw := bytes.NewBufferString(strings.Trim(
+				fmt.Sprintf("%s %s", head, tail),
+				"\n"))
+			err = parseTagGPGSig(br, sw)
+			c.GPGSig = sw.String()
+		default:
+			//Capture descriptions for tags here.The old way works for unsigned
+			//tags but not for signed ones.
+			// Be Aware! The message comes before the gpg signature
+			// not after as with commits
+			mess.WriteString(l)
+		}
+
+		if err != nil {
+			//For tags gpg signatures can come after the tag description
+			// which might start and also contain a single newline.
+			// therefore the ||head=="\n" part
+			// has been removed. i guess this wont break anything as err will
+			// eventually become EOF for tags and hence the loop will break
+			// (tbd)
+			break
+		}
+	}
+	if err != nil && err != io.EOF {
+		return nil, err
+	}
+	c.Message = mess.String()[1:]
+	return c, nil
+}

+ 246 - 0
vendor/github.com/G-Node/gig/refs.go

@@ -0,0 +1,246 @@
+package gig
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path"
+	"path/filepath"
+	"strings"
+)
+
+type Ref interface {
+	Repo() *Repository
+	Name() string
+	Fullname() string
+	Namespace() string
+	Resolve() (SHA1, error)
+}
+
+type ref struct {
+	repo *Repository
+	name string
+	ns   string // #special, #branch, or the name like 'remote', 'tags'
+}
+
+func (r *ref) Name() string {
+	return r.name
+}
+
+func (r *ref) Fullname() string {
+	fullname := r.name
+	if !strings.HasPrefix(r.ns, "#") {
+		fullname = path.Join(r.ns, r.name)
+	}
+	return fullname
+}
+
+func (r *ref) Repo() *Repository {
+	return r.repo
+}
+
+func (r *ref) Namespace() string {
+	return r.ns
+}
+
+func IsBranchRef(r Ref) bool {
+	return r.Namespace() == "#branch"
+}
+
+//IDRef is a reference that points via
+//a sha1 directly to a git object
+type IDRef struct {
+	ref
+	id SHA1
+}
+
+//Resolve for IDRef returns the stored object
+//id (SHA1)
+func (r *IDRef) Resolve() (SHA1, error) {
+	return r.id, nil
+}
+
+//SymbolicRef is a reference that points
+//to another reference
+type SymbolicRef struct {
+	ref
+	Symbol string
+}
+
+//Resolve will resolve the symbolic reference into
+//an object id.
+func (r *SymbolicRef) Resolve() (SHA1, error) {
+	gdir := fmt.Sprintf("--git-dir=%s", r.repo.Path)
+
+	cmd := exec.Command("git", gdir, "rev-parse", r.Fullname())
+	body, err := cmd.Output()
+
+	if err != nil {
+		var id SHA1
+		return id, err
+	}
+
+	return ParseSHA1(string(body))
+}
+
+func parseRefName(filename string) (name, ns string, err error) {
+	comps := strings.Split(filename, "/")
+	n := len(comps)
+
+	if n < 1 || n == 2 || (n > 2 && comps[0] != "refs") {
+		err = fmt.Errorf("git: unexpected ref name: %v", filename)
+		return
+	}
+
+	if n == 1 {
+		name = comps[0]
+		ns = "#special"
+	}
+
+	// 'man gitrepository-layout' is really helpfull
+	// 'man git-check-ref-format' too
+	// [HEAD|ORIG_HEAD] -> special head
+	// [0|refs][1|<ns>][2+|name]
+	// <ns> == "heads" -> local branch"
+	switch {
+	case n == 1:
+		name = comps[0]
+		ns = "#special"
+	case comps[1] == "heads":
+		name = path.Join(comps[2:]...)
+		ns = "#branch"
+	default:
+		name = path.Join(comps[2:]...)
+		ns = comps[1]
+	}
+	return
+}
+
+func (repo *Repository) parseRef(filename string) (Ref, error) {
+
+	name, ns, err := parseRefName(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	base := ref{repo, name, ns}
+
+	//now to the actual contents of the ref
+	data, err := ioutil.ReadFile(filepath.Join(repo.Path, filename))
+	if err != nil {
+		if os.IsNotExist(err) {
+			return repo.findPackedRef(base.Fullname())
+		}
+		return nil, err
+	}
+
+	b := string(data)
+	if strings.HasPrefix(b, "ref:") {
+		trimmed := strings.Trim(b[4:], " \n")
+		return &SymbolicRef{base, trimmed}, nil
+	}
+
+	id, err := ParseSHA1(b)
+	if err == nil {
+		return &IDRef{base, id}, nil
+	}
+
+	return nil, fmt.Errorf("git: unknown ref type: %q", b)
+}
+
+func (repo *Repository) listRefWithName(name string) (res []Ref) {
+	gdir := fmt.Sprintf("--git-dir=%s", repo.Path)
+	cmd := exec.Command("git", gdir, "show-ref", name)
+	body, err := cmd.Output()
+
+	if err != nil {
+		return
+	}
+
+	r := bytes.NewBuffer(body)
+
+	for {
+		var l string
+		l, err = r.ReadString('\n')
+		if err != nil {
+			break
+		}
+
+		_, name := split2(l[:len(l)-1], " ")
+		r, err := repo.parseRef(name)
+
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "git: could not parse ref with name %q: %v", name, err)
+			continue
+		}
+
+		res = append(res, r)
+	}
+
+	return
+}
+
+func (repo *Repository) loadPackedRefs() ([]Ref, error) {
+
+	fd, err := os.Open(filepath.Join(repo.Path, "packed-refs"))
+	if err != nil {
+		return nil, err
+	}
+	defer fd.Close()
+
+	r := bufio.NewReader(fd)
+
+	var refs []Ref
+	for {
+		var l string
+		l, err = r.ReadString('\n')
+		if err != nil {
+			break
+		}
+
+		head, tail := split2(l, " ")
+		if tail == "" {
+			//probably a peeled id (i.e. "^SHA1")
+			//TODO: do something with it
+			continue
+		}
+
+		name, ns, err := parseRefName(tail[:len(tail)-1])
+		if err != nil {
+			//TODO: log error, panic?
+			continue
+		}
+
+		id, err := ParseSHA1(head)
+		if err != nil {
+			//TODO: same as above
+			continue
+		}
+
+		refs = append(refs, &IDRef{ref{repo, name, ns}, id})
+	}
+
+	if err != nil && err != io.EOF {
+		return nil, err
+	}
+
+	return refs, nil
+}
+
+func (repo *Repository) findPackedRef(name string) (Ref, error) {
+	refs, err := repo.loadPackedRefs()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, ref := range refs {
+		if ref.Fullname() == name {
+			return ref, nil
+		}
+	}
+	return nil, fmt.Errorf("ref with name %q not found", name)
+}

+ 463 - 0
vendor/github.com/G-Node/gig/repo.go

@@ -0,0 +1,463 @@
+package gig
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path"
+	"path/filepath"
+	"strings"
+)
+
+//Repository represents an on disk git repository.
+type Repository struct {
+	Path string
+}
+
+//InitBareRepository creates a bare git repository at path.
+func InitBareRepository(path string) (*Repository, error) {
+
+	path, err := filepath.Abs(path)
+	if err != nil {
+		return nil, fmt.Errorf("Could not determine absolute path: %v", err)
+	}
+
+	cmd := exec.Command("git", "init", "--bare", path)
+	err = cmd.Run()
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &Repository{Path: path}, nil
+}
+
+//IsBareRepository checks if path is a bare git repository.
+func IsBareRepository(path string) bool {
+
+	cmd := exec.Command("git", fmt.Sprintf("--git-dir=%s", path), "rev-parse", "--is-bare-repository")
+	body, err := cmd.Output()
+
+	if err != nil {
+		return false
+	}
+
+	status := strings.Trim(string(body), "\n ")
+	return status == "true"
+}
+
+//OpenRepository opens the repository at path. Currently
+//verifies that it is a (bare) repository and returns an
+//error if the check fails.
+func OpenRepository(path string) (*Repository, error) {
+
+	path, err := filepath.Abs(path)
+	if err != nil {
+		return nil, fmt.Errorf("git: could not determine absolute path")
+	}
+
+	if !IsBareRepository(path) {
+		return nil, fmt.Errorf("git: not a bare repository")
+	}
+
+	return &Repository{Path: path}, nil
+}
+
+//DiscoverRepository returns the git repository that contains the
+//current working directory, or and error if the current working
+//dir does not lie inside one.
+func DiscoverRepository() (*Repository, error) {
+	cmd := exec.Command("git", "rev-parse", "--git-dir")
+	data, err := cmd.Output()
+	if err != nil {
+		return nil, err
+	}
+
+	path := strings.Trim(string(data), "\n ")
+	return &Repository{Path: path}, nil
+}
+
+//ReadDescription returns the contents of the description file.
+func (repo *Repository) ReadDescription() string {
+	path := filepath.Join(repo.Path, "description")
+
+	dat, err := ioutil.ReadFile(path)
+	if err != nil {
+		return ""
+	}
+
+	return string(dat)
+}
+
+//WriteDescription writes the contents of the description file.
+func (repo *Repository) WriteDescription(description string) error {
+	path := filepath.Join(repo.Path, "description")
+
+	// not atomic, fine for now
+	return ioutil.WriteFile(path, []byte(description), 0666)
+}
+
+// DeleteCollaborator removes a collaborator file from the repositories sharing folder.
+func (repo *Repository) DeleteCollaborator(username string) error {
+	filePath := filepath.Join(repo.Path, "gin", "sharing", username)
+
+	return os.Remove(filePath)
+}
+
+//OpenObject returns the git object for a give id (SHA1).
+func (repo *Repository) OpenObject(id SHA1) (Object, error) {
+	obj, err := repo.openRawObject(id)
+
+	if err != nil {
+		return nil, err
+	}
+
+	if IsStandardObject(obj.otype) {
+		return parseObject(obj)
+	}
+
+	//not a standard object, *must* be a delta object,
+	// we know of no other types
+	if !IsDeltaObject(obj.otype) {
+		return nil, fmt.Errorf("git: unsupported object")
+	}
+
+	delta, err := parseDelta(obj)
+	if err != nil {
+		return nil, err
+	}
+
+	chain, err := buildDeltaChain(delta, repo)
+
+	if err != nil {
+		return nil, err
+	}
+
+	//TODO: check depth, and especially expected memory usage
+	// beofre actually patching it
+
+	return chain.resolve()
+}
+
+func (repo *Repository) openRawObject(id SHA1) (gitObject, error) {
+	idstr := id.String()
+	opath := filepath.Join(repo.Path, "objects", idstr[:2], idstr[2:])
+
+	obj, err := openRawObject(opath)
+
+	if err == nil {
+		return obj, nil
+	} else if err != nil && !os.IsNotExist(err) {
+		return obj, err
+	}
+
+	indicies := repo.loadPackIndices()
+
+	for _, f := range indicies {
+
+		idx, err := PackIndexOpen(f)
+		if err != nil {
+			continue
+		}
+
+		//TODO: we should leave index files open,
+		defer idx.Close()
+
+		off, err := idx.FindOffset(id)
+
+		if err != nil {
+			continue
+		}
+
+		pf, err := idx.OpenPackFile()
+		if err != nil {
+			return gitObject{}, err
+		}
+
+		obj, err := pf.readRawObject(off)
+
+		if err != nil {
+			return gitObject{}, err
+		}
+
+		return obj, nil
+	}
+
+	// from inspecting the os.isNotExist source it
+	// seems that if we have "not found" in the message
+	// os.IsNotExist() report true, which is what we want
+	return gitObject{}, fmt.Errorf("git: object not found")
+}
+
+func (repo *Repository) loadPackIndices() []string {
+	target := filepath.Join(repo.Path, "objects", "pack", "*.idx")
+	files, err := filepath.Glob(target)
+
+	if err != nil {
+		panic(err)
+	}
+
+	return files
+}
+
+//OpenRef returns the Ref with the given name or an error
+//if either no maching could be found or in case the match
+//was not unique.
+func (repo *Repository) OpenRef(name string) (Ref, error) {
+
+	if name == "HEAD" {
+		return repo.parseRef("HEAD")
+	}
+
+	matches := repo.listRefWithName(name)
+
+	//first search in local heads
+	var locals []Ref
+	for _, v := range matches {
+		if IsBranchRef(v) {
+			if name == v.Fullname() {
+				return v, nil
+			}
+			locals = append(locals, v)
+		}
+	}
+
+	// if we find a single local match
+	// we return it directly
+	if len(locals) == 1 {
+		return locals[0], nil
+	}
+
+	switch len(matches) {
+	case 0:
+		return nil, fmt.Errorf("git: ref matching %q not found", name)
+	case 1:
+		return matches[0], nil
+	}
+	return nil, fmt.Errorf("git: ambiguous ref name, multiple matches")
+}
+
+//Readlink returns the destination of a symbilc link blob object
+func (repo *Repository) Readlink(id SHA1) (string, error) {
+
+	b, err := repo.OpenObject(id)
+	if err != nil {
+		return "", err
+	}
+
+	if b.Type() != ObjBlob {
+		return "", fmt.Errorf("id must point to a blob")
+	}
+
+	blob := b.(*Blob)
+
+	//TODO: check size and don't read unreasonable large blobs
+	data, err := ioutil.ReadAll(blob)
+
+	if err != nil {
+		return "", err
+	}
+
+	return string(data), nil
+}
+
+//ObjectForPath will resolve the path to an object
+//for the file tree starting in the node root.
+//The root object can be either a Commit, Tree or Tag.
+func (repo *Repository) ObjectForPath(root Object, pathstr string) (Object, error) {
+
+	var node Object
+	var err error
+
+	switch o := root.(type) {
+	case *Tree:
+		node = root
+	case *Commit:
+		node, err = repo.OpenObject(o.Tree)
+	case *Tag:
+		node, err = repo.OpenObject(o.Object)
+	default:
+		return nil, fmt.Errorf("unsupported root object type")
+	}
+
+	if err != nil {
+		return nil, fmt.Errorf("could not root tree object: %v", err)
+	}
+
+	cleaned := path.Clean(strings.Trim(pathstr, " /"))
+	comps := strings.Split(cleaned, "/")
+
+	var i int
+	for i = 0; i < len(comps); i++ {
+
+		tree, ok := node.(*Tree)
+		if !ok {
+			cwd := strings.Join(comps[:i+1], "/")
+			err := &os.PathError{
+				Op:   "convert git.Object to git.Tree",
+				Path: cwd,
+				Err:  fmt.Errorf("expected tree object, got %s", node.Type()),
+			}
+			return nil, err
+		}
+
+		//Since we call path.Clean(), this should really
+		//only happen at the root, but it is safe to
+		//have here anyway
+		if comps[i] == "." || comps[i] == "/" {
+			continue
+		}
+
+		var id *SHA1
+		for tree.Next() {
+			entry := tree.Entry()
+			if entry.Name == comps[i] {
+				id = &entry.ID
+				break
+			}
+		}
+
+		if err = tree.Err(); err != nil {
+			cwd := strings.Join(comps[:i+1], "/")
+			return nil, &os.PathError{
+				Op:   "find object",
+				Path: cwd,
+				Err:  err}
+		} else if id == nil {
+			cwd := strings.Join(comps[:i+1], "/")
+			return nil, &os.PathError{
+				Op:   "find object",
+				Path: cwd,
+				Err:  os.ErrNotExist}
+		}
+
+		node, err = repo.OpenObject(*id)
+		if err != nil {
+			cwd := strings.Join(comps[:i+1], "/")
+			return nil, &os.PathError{
+				Op:   "open object",
+				Path: cwd,
+				Err:  err,
+			}
+		}
+	}
+
+	return node, nil
+}
+
+// usefmt is the option string used by CommitsForRef to return a formatted git commit log.
+const usefmt = `--pretty=format:
+Commit:=%H%n
+Committer:=%cn%n
+Author:=%an%n
+Date-iso:=%ai%n
+Date-rel:=%ar%n
+Subject:=%s%n
+Changes:=`
+
+// CommitSummary represents a subset of information from a git commit.
+type CommitSummary struct {
+	Commit       string
+	Committer    string
+	Author       string
+	DateIso      string
+	DateRelative string
+	Subject      string
+	Changes      []string
+}
+
+// CommitsForRef executes a custom git log command for the specified ref of the
+// associated git repository and returns the resulting byte array.
+func (repo *Repository) CommitsForRef(ref string) ([]CommitSummary, error) {
+
+	raw, err := commitsForRef(repo.Path, ref, usefmt)
+	if err != nil {
+		return nil, err
+	}
+
+	sep := ":="
+	var comList []CommitSummary
+	r := bytes.NewReader(raw)
+	br := bufio.NewReader(r)
+
+	var changesFlag bool
+	for {
+		// Consume line until newline character
+		l, err := br.ReadString('\n')
+
+		if strings.Contains(l, sep) {
+			splitList := strings.SplitN(l, sep, 2)
+
+			key := splitList[0]
+			val := splitList[1]
+			switch key {
+			case "Commit":
+				// reset non key line flags
+				changesFlag = false
+				newCommit := CommitSummary{Commit: val}
+				comList = append(comList, newCommit)
+			case "Committer":
+				comList[len(comList)-1].Committer = val
+			case "Author":
+				comList[len(comList)-1].Author = val
+			case "Date-iso":
+				comList[len(comList)-1].DateIso = val
+			case "Date-rel":
+				comList[len(comList)-1].DateRelative = val
+			case "Subject":
+				comList[len(comList)-1].Subject = val
+			case "Changes":
+				// Setting changes flag so we know, that the next lines are probably file change notification lines.
+				changesFlag = true
+			default:
+				fmt.Printf("[W] commits: unexpected key %q, value %q\n", key, strings.Trim(val, "\n"))
+			}
+		} else if changesFlag && strings.Contains(l, "\t") {
+			comList[len(comList)-1].Changes = append(comList[len(comList)-1].Changes, l)
+		}
+
+		// Breaks at the latest when EOF err is raised
+		if err != nil {
+			break
+		}
+	}
+	if err != io.EOF && err != nil {
+		return nil, err
+	}
+
+	return comList, nil
+}
+
+// commitsForRef executes a custom git log command for the specified ref of the
+// given git repository with the specified log format string and returns the resulting byte array.
+// Function is kept private to force handling of the []byte inside the package.
+func commitsForRef(repoPath, ref, usefmt string) ([]byte, error) {
+	gdir := fmt.Sprintf("--git-dir=%s", repoPath)
+
+	cmd := exec.Command("git", gdir, "log", ref, usefmt, "--name-status")
+	body, err := cmd.Output()
+	if err != nil {
+		return nil, fmt.Errorf("failed running git log: %s\n", err.Error())
+	}
+	return body, nil
+}
+
+// BranchExists runs the "git branch <branchname> --list" command.
+// It will return an error, if the command fails, true, if the result is not empty and false otherwise.
+func (repo *Repository) BranchExists(branch string) (bool, error) {
+	gdir := fmt.Sprintf("--git-dir=%s", repo.Path)
+
+	cmd := exec.Command("git", gdir, "branch", branch, "--list")
+	body, err := cmd.Output()
+	if err != nil {
+		return false, err
+	} else if len(body) == 0 {
+		return false, nil
+	}
+
+	return true, nil
+}

+ 71 - 0
vendor/github.com/G-Node/gig/util.go

@@ -0,0 +1,71 @@
+package gig
+
+import (
+	"io"
+	"bytes"
+	"strings"
+	"compress/zlib"
+)
+
+func readUntilNul(r io.Reader) (string, error) {
+	buf := bytes.NewBuffer(make([]byte, 0))
+	for {
+		var b [1]byte
+		_, err := r.Read(b[:])
+		if err != nil {
+			return "", err
+		} else if b[0] == 0 {
+			break
+		}
+		buf.WriteByte(b[0])
+	}
+
+	return buf.String(), nil
+}
+
+func split2(s, sep string) (head, tail string) {
+	comps := strings.SplitN(s, sep, 2)
+	head = comps[0]
+	if len(comps) > 1 {
+		tail = comps[1]
+	}
+	return
+}
+
+type zlibReadCloser struct {
+	io.LimitedReader     //R of io.LimitedReader is the zlib reader
+	source io.ReadCloser //the underlying source
+}
+
+func (r *zlibReadCloser) Close() error {
+	var e1, e2 error
+
+	// this shouldn't fail ever actually, since the wrapped
+	//  object should have been an io.ReadCloser
+	if rc, ok := r.LimitedReader.R.(io.Closer); ok {
+		e1 = rc.Close()
+	}
+
+	e2 = r.source.Close()
+
+	if e1 == nil && e2 == nil {
+		return nil
+	} else if e2 != nil {
+		return e2
+	}
+	return e1
+}
+
+func (o *gitObject) wrapSourceWithDeflate() error {
+	r, err := zlib.NewReader(o.source)
+	if err != nil {
+		return err
+	}
+
+	o.source = &zlibReadCloser{io.LimitedReader{R: r, N: o.size}, o.source}
+	return nil
+}
+
+func (o *gitObject) wrapSource(rc io.ReadCloser) {
+	o.source = &zlibReadCloser{io.LimitedReader{R: rc, N: o.size}, o.source}
+}

+ 80 - 0
vendor/github.com/G-Node/gig/walk.go

@@ -0,0 +1,80 @@
+package gig
+
+import "fmt"
+
+func (repo *Repository) WalkRef(refname string, goOn func(SHA1) bool) (map[SHA1]*Commit, error) {
+	head, err := repo.OpenRef(refname)
+	if err != nil {
+		return nil, err
+	}
+
+	HId, err := head.Resolve()
+	if err != nil {
+		return nil, err
+	}
+
+	commits := make(map[SHA1]*Commit)
+	repo.walkCommitTree(commits, HId, goOn)
+	return commits, nil
+}
+
+func (repo *Repository) walkCommitTree(commits map[SHA1]*Commit, commitId SHA1,
+	goOn func(SHA1) bool) error {
+	commit, err := repo.OpenObject(commitId)
+	commit.Close()
+	if err != nil {
+		return err
+	}
+
+	if _, ok := commits[commitId]; !ok && goOn(commitId) {
+		commits[commitId] = commit.(*Commit)
+		for _, parent := range commit.(*Commit).Parent {
+			repo.walkCommitTree(commits, parent, goOn)
+		}
+		return nil
+	} else {
+		return nil
+	}
+}
+
+func (repo *Repository) GetBlobsForCommit(commit *Commit, blobs map[SHA1]*Blob) error {
+	treeOb, err := repo.OpenObject(commit.Tree)
+	if err != nil {
+		return err
+	}
+	defer treeOb.Close()
+
+	tree, ok := treeOb.(*Tree)
+	if !ok {
+		return fmt.Errorf("Could not assert a tree")
+	}
+
+	err = repo.GetBlobsForTree(tree, blobs)
+	return err
+}
+
+func (repo *Repository) GetBlobsForTree(tree *Tree, blobs map[SHA1]*Blob) error {
+	for tree.Next() {
+		trEntry := tree.Entry()
+		switch trEntry.Type {
+		case ObjBlob:
+			if blobOb, err := repo.OpenObject(trEntry.ID); err != nil {
+				return err
+			} else {
+				blobs[trEntry.ID] = blobOb.(*Blob)
+				blobOb.Close()
+			}
+		case ObjTree:
+			if treeOb, err := repo.OpenObject(trEntry.ID); err != nil {
+				return err
+			} else {
+				if err = repo.GetBlobsForTree(treeOb.(*Tree), blobs); err != nil {
+					treeOb.Close()
+					return err
+				}
+
+			}
+		}
+	}
+	return tree.Err()
+}

+ 206 - 0
vendor/github.com/G-Node/gig/write.go

@@ -0,0 +1,206 @@
+package gig
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"strings"
+)
+
+func writeHeader(o Object, w *bufio.Writer) (n int64, err error) {
+
+	x, err := w.WriteString(o.Type().String())
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	x, err = w.WriteString(" ")
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	x, err = w.WriteString(fmt.Sprintf("%d", o.Size()))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	err = w.WriteByte(0)
+	if err != nil {
+		return n, err
+	}
+
+	return n + 1, nil
+}
+
+//WriteTo writes the commit object to the writer in the on-disk format
+//i.e. as it would be stored in the git objects dir (although uncompressed).
+func (c *Commit) WriteTo(writer io.Writer) (int64, error) {
+	w := bufio.NewWriter(writer)
+
+	n, err := writeHeader(c, w)
+	if err != nil {
+		return n, err
+	}
+
+	x, err := w.WriteString(fmt.Sprintf("tree %s\n", c.Tree))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	for _, p := range c.Parent {
+		x, err = w.WriteString(fmt.Sprintf("parent %s\n", p))
+		n += int64(x)
+		if err != nil {
+			return n, err
+		}
+	}
+
+	x, err = w.WriteString(fmt.Sprintf("author %s\n", c.Author))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	x, err = w.WriteString(fmt.Sprintf("committer %s\n", c.Committer))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	if c.GPGSig != "" {
+		s := strings.Replace(c.GPGSig, "\n", "\n ", -1)
+		x, err = w.WriteString(fmt.Sprintf("gpgsig %s\n", s))
+		n += int64(x)
+		if err != nil {
+			return n, err
+		}
+
+	}
+
+	x, err = w.WriteString(fmt.Sprintf("\n%s", c.Message))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	err = w.Flush()
+	return n, err
+}
+
+//WriteTo writes the tree object to the writer in the on-disk format
+//i.e. as it would be stored in the git objects dir (although uncompressed).
+func (t *Tree) WriteTo(writer io.Writer) (int64, error) {
+
+	w := bufio.NewWriter(writer)
+
+	n, err := writeHeader(t, w)
+	if err != nil {
+		return n, err
+	}
+
+	for t.Next() {
+		//format is: [mode{ASCII, octal}][space][name][\0][SHA1]
+		entry := t.Entry()
+		line := fmt.Sprintf("%o %s", entry.Mode, entry.Name)
+		x, err := w.WriteString(line)
+		n += int64(x)
+		if err != nil {
+			return n, err
+		}
+
+		err = w.WriteByte(0)
+		if err != nil {
+			return n, err
+		}
+		n++
+
+		x, err = w.Write(entry.ID[:])
+		n += int64(x)
+		if err != nil {
+			return n, err
+		}
+	}
+
+	if err = t.Err(); err != nil {
+		return n, err
+	}
+
+	err = w.Flush()
+	return n, err
+}
+
+//WriteTo writes the blob object to the writer in the on-disk format
+//i.e. as it would be stored in the git objects dir (although uncompressed).
+func (b *Blob) WriteTo(writer io.Writer) (int64, error) {
+	w := bufio.NewWriter(writer)
+
+	n, err := writeHeader(b, w)
+	if err != nil {
+		return n, err
+	}
+
+	x, err := io.Copy(w, b.source)
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	err = w.Flush()
+	return n, err
+}
+
+//WriteTo writes the tag object to the writer in the on-disk format
+//i.e. as it would be stored in the git objects dir (although uncompressed).
+func (t *Tag) WriteTo(writer io.Writer) (int64, error) {
+	w := bufio.NewWriter(writer)
+
+	n, err := writeHeader(t, w)
+	if err != nil {
+		return n, err
+	}
+
+	x, err := w.WriteString(fmt.Sprintf("object %s\n", t.Object))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	x, err = w.WriteString(fmt.Sprintf("type %s\n", t.ObjType))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	x, err = w.WriteString(fmt.Sprintf("tag %s\n", t.Tag))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	x, err = w.WriteString(fmt.Sprintf("tagger %s\n\n", t.Tagger))
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+
+	x, err = w.WriteString(t.Message)
+	n += int64(x)
+	if err != nil {
+		return n, err
+	}
+	if t.GPGSig != "" {
+		x, err = w.WriteString(fmt.Sprintf("%s\n", t.GPGSig))
+		n += int64(x)
+		if err != nil {
+			return n, err
+		}
+
+	}
+
+	err = w.Flush()
+	return n, err
+}

+ 19 - 0
vendor/github.com/G-Node/git-module/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2015 All Gogs Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 13 - 0
vendor/github.com/G-Node/git-module/README.md

@@ -0,0 +1,13 @@
+# Git Module [![Build Status](https://travis-ci.org/gogits/git-module.svg?branch=master)](https://travis-ci.org/gogits/git-module)
+
+Package git-module is a Go module for Git access through shell commands.
+
+## Limitations
+
+- Go version must be at least **1.4**.
+- Git version must be no less than **1.7.1**, and greater than or equal to **1.8.3** is recommended.
+- For Windows users, try use as new a version as possible.
+
+## License
+
+This project is under the MIT License. See the [LICENSE](LICENSE) file for the full license text.

+ 35 - 0
vendor/github.com/G-Node/git-module/blob.go

@@ -0,0 +1,35 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bytes"
+	"io"
+)
+
+// Blob represents a Git object.
+type Blob struct {
+	repo *Repository
+	*TreeEntry
+}
+
+// Data gets content of blob all at once and wrap it as io.Reader.
+// This can be very slow and memory consuming for huge content.
+func (b *Blob) Data() (io.Reader, error) {
+	stdout := new(bytes.Buffer)
+	stderr := new(bytes.Buffer)
+
+	// Preallocate memory to save ~50% memory usage on big files.
+	stdout.Grow(int(b.Size() + 2048))
+
+	if err := b.DataPipeline(stdout, stderr); err != nil {
+		return nil, concatenateError(err, stderr.String())
+	}
+	return stdout, nil
+}
+
+func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
+	return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
+}

+ 158 - 0
vendor/github.com/G-Node/git-module/command.go

@@ -0,0 +1,158 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"os"
+	"os/exec"
+	"strings"
+	"time"
+)
+
+// Command represents a command with its subcommands or arguments.
+type Command struct {
+	name string
+	args []string
+	envs []string
+}
+
+func (c *Command) String() string {
+	if len(c.args) == 0 {
+		return c.name
+	}
+	return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " "))
+}
+
+// NewCommand creates and returns a new Git Command based on given command and arguments.
+func NewCommand(args ...string) *Command {
+	return &Command{
+		name: "git",
+		args: args,
+	}
+}
+
+// NewCommand creates and returns a new Git Command based on given command and arguments.
+func NewACommand(args ...string) *Command {
+	return &Command{
+		name: "git-annex",
+		args: args,
+	}
+}
+
+// AddArguments adds new argument(s) to the command.
+func (c *Command) AddArguments(args ...string) *Command {
+	c.args = append(c.args, args...)
+	return c
+}
+
+// AddEnvs adds new environment variables to the command.
+func (c *Command) AddEnvs(envs ...string) *Command {
+	c.envs = append(c.envs, envs...)
+	return c
+}
+
+const DEFAULT_TIMEOUT = 60 * time.Second
+
+// RunInDirTimeoutPipeline executes the command in given directory with given timeout,
+// it pipes stdout and stderr to given io.Writer.
+func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
+	if timeout == -1 {
+		timeout = DEFAULT_TIMEOUT
+	}
+
+	if len(dir) == 0 {
+		log(c.String())
+	} else {
+		log("%s: %v", dir, c)
+	}
+
+	cmd := exec.Command(c.name, c.args...)
+	if c.envs != nil {
+		cmd.Env = append(os.Environ(), c.envs...)
+	}
+	cmd.Dir = dir
+	cmd.Stdout = stdout
+	cmd.Stderr = stderr
+	if err := cmd.Start(); err != nil {
+		return err
+	}
+
+	done := make(chan error)
+	go func() {
+		done <- cmd.Wait()
+	}()
+
+	var err error
+	select {
+	case <-time.After(timeout):
+		if cmd.Process != nil && cmd.ProcessState != nil && !cmd.ProcessState.Exited() {
+			if err := cmd.Process.Kill(); err != nil {
+				return fmt.Errorf("fail to kill process: %v", err)
+			}
+		}
+
+		<-done
+		return ErrExecTimeout{timeout}
+	case err = <-done:
+	}
+
+	return err
+}
+
+// RunInDirTimeout executes the command in given directory with given timeout,
+// and returns stdout in []byte and error (combined with stderr).
+func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
+	stdout := new(bytes.Buffer)
+	stderr := new(bytes.Buffer)
+	if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil {
+		return nil, concatenateError(err, stderr.String())
+	}
+
+	if stdout.Len() > 0 {
+		log("stdout:\n%s", stdout.Bytes()[:1024])
+	}
+	return stdout.Bytes(), nil
+}
+
+// RunInDirPipeline executes the command in given directory,
+// it pipes stdout and stderr to given io.Writer.
+func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
+	return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr)
+}
+
+// RunInDir executes the command in given directory
+// and returns stdout in []byte and error (combined with stderr).
+func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
+	return c.RunInDirTimeout(-1, dir)
+}
+
+// RunInDir executes the command in given directory
+// and returns stdout in string and error (combined with stderr).
+func (c *Command) RunInDir(dir string) (string, error) {
+	stdout, err := c.RunInDirTimeout(-1, dir)
+	if err != nil {
+		return "", err
+	}
+	return string(stdout), nil
+}
+
+// RunTimeout executes the command in defualt working directory with given timeout,
+// and returns stdout in string and error (combined with stderr).
+func (c *Command) RunTimeout(timeout time.Duration) (string, error) {
+	stdout, err := c.RunInDirTimeout(timeout, "")
+	if err != nil {
+		return "", err
+	}
+	return string(stdout), nil
+}
+
+// Run executes the command in defualt working directory
+// and returns stdout in string and error (combined with stderr).
+func (c *Command) Run() (string, error) {
+	return c.RunTimeout(-1)
+}

+ 321 - 0
vendor/github.com/G-Node/git-module/commit.go

@@ -0,0 +1,321 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bufio"
+	"bytes"
+	"container/list"
+	"fmt"
+	"io"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"github.com/mcuadros/go-version"
+)
+
+// Commit represents a git commit.
+type Commit struct {
+	Tree
+	ID            sha1 // The ID of this commit object
+	Author        *Signature
+	Committer     *Signature
+	CommitMessage string
+
+	parents        []sha1 // SHA1 strings
+	submoduleCache *objectCache
+}
+
+// Message returns the commit message. Same as retrieving CommitMessage directly.
+func (c *Commit) Message() string {
+	return c.CommitMessage
+}
+
+// Summary returns first line of commit message.
+func (c *Commit) Summary() string {
+	return strings.Split(c.CommitMessage, "\n")[0]
+}
+
+// ParentID returns oid of n-th parent (0-based index).
+// It returns nil if no such parent exists.
+func (c *Commit) ParentID(n int) (sha1, error) {
+	if n >= len(c.parents) {
+		return sha1{}, ErrNotExist{"", ""}
+	}
+	return c.parents[n], nil
+}
+
+// Parent returns n-th parent (0-based index) of the commit.
+func (c *Commit) Parent(n int) (*Commit, error) {
+	id, err := c.ParentID(n)
+	if err != nil {
+		return nil, err
+	}
+	parent, err := c.repo.getCommit(id)
+	if err != nil {
+		return nil, err
+	}
+	return parent, nil
+}
+
+// ParentCount returns number of parents of the commit.
+// 0 if this is the root commit,  otherwise 1,2, etc.
+func (c *Commit) ParentCount() int {
+	return len(c.parents)
+}
+
+func isImageFile(data []byte) (string, bool) {
+	contentType := http.DetectContentType(data)
+	if strings.Index(contentType, "image/") != -1 {
+		return contentType, true
+	}
+	return contentType, false
+}
+
+func (c *Commit) IsImageFile(name string) bool {
+	blob, err := c.GetBlobByPath(name)
+	if err != nil {
+		return false
+	}
+
+	dataRc, err := blob.Data()
+	if err != nil {
+		return false
+	}
+	buf := make([]byte, 1024)
+	n, _ := dataRc.Read(buf)
+	buf = buf[:n]
+	_, isImage := isImageFile(buf)
+	return isImage
+}
+
+// GetCommitByPath return the commit of relative path object.
+func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) {
+	return c.repo.getCommitByPathWithID(c.ID, relpath)
+}
+
+// AddAllChanges marks local changes to be ready for commit.
+func AddChanges(repoPath string, all bool, files ...string) error {
+	cmd := NewCommand("add")
+	if all {
+		cmd.AddArguments("--all")
+	}
+	_, err := cmd.AddArguments(files...).RunInDir(repoPath)
+	return err
+}
+
+type CommitChangesOptions struct {
+	Committer *Signature
+	Author    *Signature
+	Message   string
+}
+
+// CommitChanges commits local changes with given committer, author and message.
+// If author is nil, it will be the same as committer.
+func CommitChanges(repoPath string, opts CommitChangesOptions) error {
+	cmd := NewCommand()
+	if opts.Committer != nil {
+		cmd.AddEnvs("GIT_COMMITTER_NAME="+opts.Committer.Name, "GIT_COMMITTER_EMAIL="+opts.Committer.Email)
+	}
+	cmd.AddArguments("commit")
+
+	if opts.Author == nil {
+		opts.Author = opts.Committer
+	}
+	if opts.Author != nil {
+		cmd.AddArguments(fmt.Sprintf("--author='%s <%s>'", opts.Author.Name, opts.Author.Email))
+	}
+	cmd.AddArguments("-m", opts.Message)
+
+	_, err := cmd.RunInDir(repoPath)
+	// No stderr but exit status 1 means nothing to commit.
+	if err != nil && err.Error() == "exit status 1" {
+		return nil
+	}
+	return err
+}
+
+func commitsCount(repoPath, revision, relpath string) (int64, error) {
+	var cmd *Command
+	isFallback := false
+	if version.Compare(gitVersion, "1.8.0", "<") {
+		isFallback = true
+		cmd = NewCommand("log", "--pretty=format:''")
+	} else {
+		cmd = NewCommand("rev-list", "--count")
+	}
+	cmd.AddArguments(revision)
+	if len(relpath) > 0 {
+		cmd.AddArguments("--", relpath)
+	}
+
+	stdout, err := cmd.RunInDir(repoPath)
+	if err != nil {
+		return 0, err
+	}
+
+	if isFallback {
+		return int64(strings.Count(stdout, "\n")) + 1, nil
+	}
+	return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
+}
+
+func (c *Commit) CommitsCount() (int64, error) {
+	return CommitsCount(c.repo.Path, c.ID.String())
+}
+
+func (c *Commit) CommitsByRangeSize(page, size int) (*list.List, error) {
+	return c.repo.CommitsByRangeSize(c.ID.String(), page, size)
+}
+
+func (c *Commit) CommitsByRange(page int) (*list.List, error) {
+	return c.repo.CommitsByRange(c.ID.String(), page)
+}
+
+func (c *Commit) CommitsBefore() (*list.List, error) {
+	return c.repo.getCommitsBefore(c.ID)
+}
+
+func (c *Commit) CommitsBeforeLimit(num int) (*list.List, error) {
+	return c.repo.getCommitsBeforeLimit(c.ID, num)
+}
+
+func (c *Commit) CommitsBeforeUntil(commitID string) (*list.List, error) {
+	endCommit, err := c.repo.GetCommit(commitID)
+	if err != nil {
+		return nil, err
+	}
+	return c.repo.CommitsBetween(c, endCommit)
+}
+
+func (c *Commit) SearchCommits(keyword string) (*list.List, error) {
+	return c.repo.searchCommits(c.ID, keyword)
+}
+
+func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error) {
+	return c.repo.getFilesChanged(pastCommit, c.ID.String())
+}
+
+func (c *Commit) GetSubModules() (*objectCache, error) {
+	if c.submoduleCache != nil {
+		return c.submoduleCache, nil
+	}
+
+	entry, err := c.GetTreeEntryByPath(".gitmodules")
+	if err != nil {
+		return nil, err
+	}
+	rd, err := entry.Blob().Data()
+	if err != nil {
+		return nil, err
+	}
+
+	scanner := bufio.NewScanner(rd)
+	c.submoduleCache = newObjectCache()
+	var ismodule bool
+	var path string
+	for scanner.Scan() {
+		if strings.HasPrefix(scanner.Text(), "[submodule") {
+			ismodule = true
+			continue
+		}
+		if ismodule {
+			fields := strings.Split(scanner.Text(), "=")
+			k := strings.TrimSpace(fields[0])
+			if k == "path" {
+				path = strings.TrimSpace(fields[1])
+			} else if k == "url" {
+				c.submoduleCache.Set(path, &SubModule{path, strings.TrimSpace(fields[1])})
+				ismodule = false
+			}
+		}
+	}
+
+	return c.submoduleCache, nil
+}
+
+func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
+	modules, err := c.GetSubModules()
+	if err != nil {
+		return nil, err
+	}
+
+	module, has := modules.Get(entryname)
+	if has {
+		return module.(*SubModule), nil
+	}
+	return nil, nil
+}
+
+// CommitFileStatus represents status of files in a commit.
+type CommitFileStatus struct {
+	Added    []string
+	Removed  []string
+	Modified []string
+}
+
+func NewCommitFileStatus() *CommitFileStatus {
+	return &CommitFileStatus{
+		[]string{}, []string{}, []string{},
+	}
+}
+
+// GetCommitFileStatus returns file status of commit in given repository.
+func GetCommitFileStatus(repoPath, commitID string) (*CommitFileStatus, error) {
+	stdout, w := io.Pipe()
+	done := make(chan struct{})
+	fileStatus := NewCommitFileStatus()
+	go func() {
+		scanner := bufio.NewScanner(stdout)
+		for scanner.Scan() {
+			fields := strings.Fields(scanner.Text())
+			if len(fields) < 2 {
+				continue
+			}
+
+			switch fields[0][0] {
+			case 'A':
+				fileStatus.Added = append(fileStatus.Added, fields[1])
+			case 'D':
+				fileStatus.Removed = append(fileStatus.Removed, fields[1])
+			case 'M':
+				fileStatus.Modified = append(fileStatus.Modified, fields[1])
+			}
+		}
+		done <- struct{}{}
+	}()
+
+	stderr := new(bytes.Buffer)
+	err := NewCommand("log", "-1", "--name-status", "--pretty=format:''", commitID).RunInDirPipeline(repoPath, w, stderr)
+	w.Close() // Close writer to exit parsing goroutine
+	if err != nil {
+		return nil, concatenateError(err, stderr.String())
+	}
+
+	<-done
+	return fileStatus, nil
+}
+
+// FileStatus returns file status of commit.
+func (c *Commit) FileStatus() (*CommitFileStatus, error) {
+	return GetCommitFileStatus(c.repo.Path, c.ID.String())
+}
+
+// GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository.
+func GetFullCommitID(repoPath, shortID string) (string, error) {
+	if len(shortID) >= 40 {
+		return shortID, nil
+	}
+
+	commitID, err := NewCommand("rev-parse", shortID).RunInDir(repoPath)
+	if err != nil {
+		if strings.Contains(err.Error(), "exit status 128") {
+			return "", ErrNotExist{shortID, ""}
+		}
+		return "", err
+	}
+	return strings.TrimSpace(commitID), nil
+}

+ 57 - 0
vendor/github.com/G-Node/git-module/commit_archive.go

@@ -0,0 +1,57 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/G-Node/gogs/pkg/setting"
+	"github.com/G-Node/libgin/libgin"
+)
+
+type ArchiveType int
+
+const (
+	ZIP ArchiveType = iota + 1
+	TARGZ
+	GIN
+)
+
+func (c *Commit) CreateArchive(target string, archiveType ArchiveType, cloneL string) error {
+	var format string
+	switch archiveType {
+	case ZIP:
+		format = "zip"
+	case TARGZ:
+		format = "tar.gz"
+	case GIN:
+		// TODO: Fix me!
+		to := filepath.Join(setting.Repository.Upload.TempPath, "archives", filepath.Base(strings.TrimSuffix(c.repo.Path, ".git")))
+		defer os.RemoveAll(to)
+		_, err := NewCommand("clone", c.repo.Path, to).RunTimeout(-1)
+		if err != nil {
+			return err
+		}
+		_, err = NewCommand("remote", "set-url", "origin", cloneL).RunInDirTimeout(-1, to)
+		if err != nil {
+			return err
+		}
+		fp, err := os.Create(target)
+		defer fp.Close()
+		if err != nil {
+			return err
+		}
+		err = libgin.MakeZip(fp, to)
+		return err
+	default:
+		return fmt.Errorf("unknown format: %v", archiveType)
+	}
+
+	_, err := NewCommand("archive", "--prefix="+filepath.Base(strings.TrimSuffix(c.repo.Path, ".git"))+"/", "--format="+format, "-o", target, c.ID.String()).RunInDir(c.repo.Path)
+	return err
+}

+ 61 - 0
vendor/github.com/G-Node/git-module/error.go

@@ -0,0 +1,61 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"time"
+)
+
+type ErrExecTimeout struct {
+	Duration time.Duration
+}
+
+func IsErrExecTimeout(err error) bool {
+	_, ok := err.(ErrExecTimeout)
+	return ok
+}
+
+func (err ErrExecTimeout) Error() string {
+	return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
+}
+
+type ErrNotExist struct {
+	ID      string
+	RelPath string
+}
+
+func IsErrNotExist(err error) bool {
+	_, ok := err.(ErrNotExist)
+	return ok
+}
+
+func (err ErrNotExist) Error() string {
+	return fmt.Sprintf("object does not exist [id: %s, rel_path: %s]", err.ID, err.RelPath)
+}
+
+type ErrUnsupportedVersion struct {
+	Required string
+}
+
+func IsErrUnsupportedVersion(err error) bool {
+	_, ok := err.(ErrUnsupportedVersion)
+	return ok
+}
+
+func (err ErrUnsupportedVersion) Error() string {
+	return fmt.Sprintf("Operation requires higher version [required: %s]", err.Required)
+}
+
+type ErrNoMergeBase struct{}
+
+func IsErrNoMergeBase(err error) bool {
+	_, ok := err.(ErrNoMergeBase)
+	return ok
+}
+
+func (err ErrNoMergeBase) Error() string {
+	return "no merge based found"
+}

+ 80 - 0
vendor/github.com/G-Node/git-module/git.go

@@ -0,0 +1,80 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"strings"
+	"time"
+)
+
+const _VERSION = "0.7.0"
+
+func Version() string {
+	return _VERSION
+}
+
+var (
+	// Debug enables verbose logging on everything.
+	// This should be false in case Gogs starts in SSH mode.
+	Debug  = false
+	Prefix = "[git-module] "
+)
+
+func log(format string, args ...interface{}) {
+	if !Debug {
+		return
+	}
+
+	fmt.Print(Prefix)
+	if len(args) == 0 {
+		fmt.Println(format)
+	} else {
+		fmt.Printf(format+"\n", args...)
+	}
+}
+
+var gitVersion string
+
+// Version returns current Git version from shell.
+func BinVersion() (string, error) {
+	if len(gitVersion) > 0 {
+		return gitVersion, nil
+	}
+
+	stdout, err := NewCommand("version").Run()
+	if err != nil {
+		return "", err
+	}
+
+	fields := strings.Fields(stdout)
+	if len(fields) < 3 {
+		return "", fmt.Errorf("not enough output: %s", stdout)
+	}
+
+	// Handle special case on Windows.
+	i := strings.Index(fields[2], "windows")
+	if i >= 1 {
+		gitVersion = fields[2][:i-1]
+		return gitVersion, nil
+	}
+
+	gitVersion = fields[2]
+	return gitVersion, nil
+}
+
+func init() {
+	BinVersion()
+}
+
+// Fsck verifies the connectivity and validity of the objects in the database
+func Fsck(repoPath string, timeout time.Duration, args ...string) error {
+	// Make sure timeout makes sense.
+	if timeout <= 0 {
+		timeout = -1
+	}
+	_, err := NewCommand("fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath)
+	return err
+}

+ 111 - 0
vendor/github.com/G-Node/git-module/hook.go

@@ -0,0 +1,111 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"errors"
+	"io/ioutil"
+	"os"
+	"path"
+	"strings"
+)
+
+var (
+	// Direcotry of hook and sample files. Can be changed to "custom_hooks" for very purpose.
+	HookDir       = "hooks"
+	HookSampleDir = HookDir
+	// HookNames is a list of Git server hooks' name that are supported.
+	HookNames = []string{
+		"pre-receive",
+		"update",
+		"post-receive",
+	}
+)
+
+var (
+	ErrNotValidHook = errors.New("not a valid Git hook")
+)
+
+// IsValidHookName returns true if given name is a valid Git hook.
+func IsValidHookName(name string) bool {
+	for _, hn := range HookNames {
+		if hn == name {
+			return true
+		}
+	}
+	return false
+}
+
+// Hook represents a Git hook.
+type Hook struct {
+	name     string
+	IsActive bool   // Indicates whether repository has this hook.
+	Content  string // Content of hook if it's active.
+	Sample   string // Sample content from Git.
+	path     string // Hook file path.
+}
+
+// GetHook returns a Git hook by given name and repository.
+func GetHook(repoPath, name string) (*Hook, error) {
+	if !IsValidHookName(name) {
+		return nil, ErrNotValidHook
+	}
+	h := &Hook{
+		name: name,
+		path: path.Join(repoPath, HookDir, name),
+	}
+	if isFile(h.path) {
+		data, err := ioutil.ReadFile(h.path)
+		if err != nil {
+			return nil, err
+		}
+		h.IsActive = true
+		h.Content = string(data)
+		return h, nil
+	}
+
+	// Check sample file
+	samplePath := path.Join(repoPath, HookSampleDir, h.name) + ".sample"
+	if isFile(samplePath) {
+		data, err := ioutil.ReadFile(samplePath)
+		if err != nil {
+			return nil, err
+		}
+		h.Sample = string(data)
+	}
+	return h, nil
+}
+
+func (h *Hook) Name() string {
+	return h.name
+}
+
+// Update updates content hook file.
+func (h *Hook) Update() error {
+	if len(strings.TrimSpace(h.Content)) == 0 {
+		if isExist(h.path) {
+			return os.Remove(h.path)
+		}
+		return nil
+	}
+	os.MkdirAll(path.Dir(h.path), os.ModePerm)
+	return ioutil.WriteFile(h.path, []byte(strings.Replace(h.Content, "\r", "", -1)), os.ModePerm)
+}
+
+// ListHooks returns a list of Git hooks of given repository.
+func ListHooks(repoPath string) (_ []*Hook, err error) {
+	if !isDir(path.Join(repoPath, "hooks")) {
+		return nil, errors.New("hooks path does not exist")
+	}
+
+	hooks := make([]*Hook, len(HookNames))
+	for i, name := range HookNames {
+		hooks[i], err = GetHook(repoPath, name)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return hooks, nil
+}

+ 285 - 0
vendor/github.com/G-Node/git-module/repo.go

@@ -0,0 +1,285 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bytes"
+	"container/list"
+	"errors"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/Unknwon/com"
+)
+
+// Repository represents a Git repository.
+type Repository struct {
+	Path string
+
+	commitCache *objectCache
+	tagCache    *objectCache
+}
+
+const _PRETTY_LOG_FORMAT = `--pretty=format:%H`
+
+func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, error) {
+	l := list.New()
+	if len(logs) == 0 {
+		return l, nil
+	}
+
+	parts := bytes.Split(logs, []byte{'\n'})
+
+	for _, commitId := range parts {
+		commit, err := repo.GetCommit(string(commitId))
+		if err != nil {
+			return nil, err
+		}
+		l.PushBack(commit)
+	}
+
+	return l, nil
+}
+
+type NetworkOptions struct {
+	URL     string
+	Timeout time.Duration
+}
+
+// IsRepoURLAccessible checks if given repository URL is accessible.
+func IsRepoURLAccessible(opts NetworkOptions) bool {
+	cmd := NewCommand("ls-remote", "-q", "-h", opts.URL, "HEAD")
+	if opts.Timeout <= 0 {
+		opts.Timeout = -1
+	}
+	_, err := cmd.RunTimeout(opts.Timeout)
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+// InitRepository initializes a new Git repository.
+func InitRepository(repoPath string, bare bool) error {
+	os.MkdirAll(repoPath, os.ModePerm)
+
+	cmd := NewCommand("init")
+	if bare {
+		cmd.AddArguments("--bare")
+	}
+	_, err := cmd.RunInDir(repoPath)
+	return err
+}
+
+// OpenRepository opens the repository at the given path.
+func OpenRepository(repoPath string) (*Repository, error) {
+	repoPath, err := filepath.Abs(repoPath)
+	if err != nil {
+		return nil, err
+	} else if !isDir(repoPath) {
+		return nil, errors.New("no such file or directory")
+	}
+
+	return &Repository{
+		Path:        repoPath,
+		commitCache: newObjectCache(),
+		tagCache:    newObjectCache(),
+	}, nil
+}
+
+type CloneRepoOptions struct {
+	Mirror  bool
+	Bare    bool
+	Quiet   bool
+	Branch  string
+	Timeout time.Duration
+}
+
+// Clone clones original repository to target path.
+func Clone(from, to string, opts CloneRepoOptions) (err error) {
+	toDir := path.Dir(to)
+	if err = os.MkdirAll(toDir, os.ModePerm); err != nil {
+		return err
+	}
+
+	cmd := NewCommand("clone")
+	if opts.Mirror {
+		cmd.AddArguments("--mirror")
+	}
+	if opts.Bare {
+		cmd.AddArguments("--bare")
+	}
+	if opts.Quiet {
+		cmd.AddArguments("--quiet")
+	}
+	if len(opts.Branch) > 0 {
+		cmd.AddArguments("-b", opts.Branch)
+	}
+	cmd.AddArguments(from, to)
+
+	if opts.Timeout <= 0 {
+		opts.Timeout = -1
+	}
+	_, err = cmd.RunTimeout(opts.Timeout)
+	return err
+}
+
+type FetchRemoteOptions struct {
+	Prune   bool
+	Timeout time.Duration
+}
+
+// Fetch fetches changes from remotes without merging.
+func Fetch(repoPath string, opts FetchRemoteOptions) error {
+	cmd := NewCommand("fetch")
+	if opts.Prune {
+		cmd.AddArguments("--prune")
+	}
+
+	if opts.Timeout <= 0 {
+		opts.Timeout = -1
+	}
+	_, err := cmd.RunInDirTimeout(opts.Timeout, repoPath)
+	return err
+}
+
+type PullRemoteOptions struct {
+	All     bool
+	Rebase  bool
+	Remote  string
+	Branch  string
+	Timeout time.Duration
+}
+
+// Pull pulls changes from remotes.
+func Pull(repoPath string, opts PullRemoteOptions) error {
+	cmd := NewCommand("pull")
+	if opts.Rebase {
+		cmd.AddArguments("--rebase")
+	}
+	if opts.All {
+		cmd.AddArguments("--all")
+	} else {
+		cmd.AddArguments(opts.Remote)
+		cmd.AddArguments(opts.Branch)
+	}
+
+	if opts.Timeout <= 0 {
+		opts.Timeout = -1
+	}
+	_, err := cmd.RunInDirTimeout(opts.Timeout, repoPath)
+	return err
+}
+
+// PushWithEnvs pushs local commits to given remote branch with given environment variables.
+func PushWithEnvs(repoPath, remote, branch string, envs []string) error {
+	_, err := NewCommand("push", remote, branch).AddEnvs(envs...).RunInDir(repoPath)
+	return err
+}
+
+// Push pushs local commits to given remote branch.
+func Push(repoPath, remote, branch string) error {
+	return PushWithEnvs(repoPath, remote, branch, nil)
+}
+
+type CheckoutOptions struct {
+	Branch    string
+	OldBranch string
+	Timeout   time.Duration
+}
+
+// Checkout checkouts a branch
+func Checkout(repoPath string, opts CheckoutOptions) error {
+	cmd := NewCommand("checkout")
+	if len(opts.OldBranch) > 0 {
+		cmd.AddArguments("-b")
+	}
+
+	cmd.AddArguments(opts.Branch)
+
+	if len(opts.OldBranch) > 0 {
+		cmd.AddArguments(opts.OldBranch)
+	}
+	if opts.Timeout <= 0 {
+		opts.Timeout = -1
+	}
+	_, err := cmd.RunInDirTimeout(opts.Timeout, repoPath)
+	return err
+}
+
+// ResetHEAD resets HEAD to given revision or head of branch.
+func ResetHEAD(repoPath string, hard bool, revision string) error {
+	cmd := NewCommand("reset")
+	if hard {
+		cmd.AddArguments("--hard")
+	}
+	_, err := cmd.AddArguments(revision).RunInDir(repoPath)
+	return err
+}
+
+// MoveFile moves a file to another file or directory.
+func MoveFile(repoPath, oldTreeName, newTreeName string) error {
+	_, err := NewCommand("mv").AddArguments(oldTreeName, newTreeName).RunInDir(repoPath)
+	return err
+}
+
+// CountObject represents disk usage report of Git repository.
+type CountObject struct {
+	Count         int64
+	Size          int64
+	InPack        int64
+	Packs         int64
+	SizePack      int64
+	PrunePackable int64
+	Garbage       int64
+	SizeGarbage   int64
+}
+
+const (
+	_STAT_COUNT          = "count: "
+	_STAT_SIZE           = "size: "
+	_STAT_IN_PACK        = "in-pack: "
+	_STAT_PACKS          = "packs: "
+	_STAT_SIZE_PACK      = "size-pack: "
+	_STAT_PRUNE_PACKABLE = "prune-packable: "
+	_STAT_GARBAGE        = "garbage: "
+	_STAT_SIZE_GARBAGE   = "size-garbage: "
+)
+
+// GetRepoSize returns disk usage report of repository in given path.
+func GetRepoSize(repoPath string) (*CountObject, error) {
+	cmd := NewCommand("count-objects", "-v")
+	stdout, err := cmd.RunInDir(repoPath)
+	if err != nil {
+		return nil, err
+	}
+
+	countObject := new(CountObject)
+	for _, line := range strings.Split(stdout, "\n") {
+		switch {
+		case strings.HasPrefix(line, _STAT_COUNT):
+			countObject.Count = com.StrTo(line[7:]).MustInt64()
+		case strings.HasPrefix(line, _STAT_SIZE):
+			countObject.Size = com.StrTo(line[6:]).MustInt64() * 1024
+		case strings.HasPrefix(line, _STAT_IN_PACK):
+			countObject.InPack = com.StrTo(line[9:]).MustInt64()
+		case strings.HasPrefix(line, _STAT_PACKS):
+			countObject.Packs = com.StrTo(line[7:]).MustInt64()
+		case strings.HasPrefix(line, _STAT_SIZE_PACK):
+			countObject.SizePack = com.StrTo(line[11:]).MustInt64() * 1024
+		case strings.HasPrefix(line, _STAT_PRUNE_PACKABLE):
+			countObject.PrunePackable = com.StrTo(line[16:]).MustInt64()
+		case strings.HasPrefix(line, _STAT_GARBAGE):
+			countObject.Garbage = com.StrTo(line[9:]).MustInt64()
+		case strings.HasPrefix(line, _STAT_SIZE_GARBAGE):
+			countObject.SizeGarbage = com.StrTo(line[14:]).MustInt64() * 1024
+		}
+	}
+
+	return countObject, nil
+}

+ 126 - 0
vendor/github.com/G-Node/git-module/repo_branch.go

@@ -0,0 +1,126 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/mcuadros/go-version"
+)
+
+const BRANCH_PREFIX = "refs/heads/"
+
+// IsReferenceExist returns true if given reference exists in the repository.
+func IsReferenceExist(repoPath, name string) bool {
+	_, err := NewCommand("show-ref", "--verify", name).RunInDir(repoPath)
+	return err == nil
+}
+
+// IsBranchExist returns true if given branch exists in the repository.
+func IsBranchExist(repoPath, name string) bool {
+	return IsReferenceExist(repoPath, BRANCH_PREFIX+name)
+}
+
+func (repo *Repository) IsBranchExist(name string) bool {
+	return IsBranchExist(repo.Path, name)
+}
+
+// Branch represents a Git branch.
+type Branch struct {
+	Name string
+	Path string
+}
+
+// GetHEADBranch returns corresponding branch of HEAD.
+func (repo *Repository) GetHEADBranch() (*Branch, error) {
+	stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	stdout = strings.TrimSpace(stdout)
+
+	if !strings.HasPrefix(stdout, BRANCH_PREFIX) {
+		return nil, fmt.Errorf("invalid HEAD branch: %v", stdout)
+	}
+
+	return &Branch{
+		Name: stdout[len(BRANCH_PREFIX):],
+		Path: stdout,
+	}, nil
+}
+
+// SetDefaultBranch sets default branch of repository.
+func (repo *Repository) SetDefaultBranch(name string) error {
+	if version.Compare(gitVersion, "1.7.10", "<") {
+		return ErrUnsupportedVersion{"1.7.10"}
+	}
+
+	_, err := NewCommand("symbolic-ref", "HEAD", BRANCH_PREFIX+name).RunInDir(repo.Path)
+	return err
+}
+
+// GetBranches returns all branches of the repository.
+func (repo *Repository) GetBranches() ([]string, error) {
+	stdout, err := NewCommand("show-ref", "--heads").RunInDir(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	infos := strings.Split(stdout, "\n")
+	branches := make([]string, len(infos)-1)
+	for i, info := range infos[:len(infos)-1] {
+		fields := strings.Fields(info)
+		if len(fields) != 2 {
+			continue // NOTE: I should believe git will not give me wrong string.
+		}
+		branches[i] = strings.TrimPrefix(fields[1], BRANCH_PREFIX)
+	}
+	return branches, nil
+}
+
+// Option(s) for delete branch
+type DeleteBranchOptions struct {
+	Force bool
+}
+
+// DeleteBranch deletes a branch from given repository path.
+func DeleteBranch(repoPath, name string, opts DeleteBranchOptions) error {
+	cmd := NewCommand("branch")
+
+	if opts.Force {
+		cmd.AddArguments("-D")
+	} else {
+		cmd.AddArguments("-d")
+	}
+
+	cmd.AddArguments(name)
+	_, err := cmd.RunInDir(repoPath)
+
+	return err
+}
+
+// DeleteBranch deletes a branch from repository.
+func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error {
+	return DeleteBranch(repo.Path, name, opts)
+}
+
+// AddRemote adds a new remote to repository.
+func (repo *Repository) AddRemote(name, url string, fetch bool) error {
+	cmd := NewCommand("remote", "add")
+	if fetch {
+		cmd.AddArguments("-f")
+	}
+	cmd.AddArguments(name, url)
+
+	_, err := cmd.RunInDir(repo.Path)
+	return err
+}
+
+// RemoveRemote removes a remote from repository.
+func (repo *Repository) RemoveRemote(name string) error {
+	_, err := NewCommand("remote", "remove", name).RunInDir(repo.Path)
+	return err
+}

+ 408 - 0
vendor/github.com/G-Node/git-module/repo_commit.go

@@ -0,0 +1,408 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bytes"
+	"container/list"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/mcuadros/go-version"
+)
+
+const REMOTE_PREFIX = "refs/remotes/"
+
+// getRefCommitID returns the last commit ID string of given reference (branch or tag).
+func (repo *Repository) getRefCommitID(name string) (string, error) {
+	stdout, err := NewCommand("show-ref", "--verify", name).RunInDir(repo.Path)
+	if err != nil {
+		if strings.Contains(err.Error(), "not a valid ref") {
+			return "", ErrNotExist{name, ""}
+		}
+		return "", err
+	}
+	return strings.Split(stdout, " ")[0], nil
+}
+
+// GetBranchCommitID returns last commit ID string of given branch.
+func (repo *Repository) GetBranchCommitID(name string) (string, error) {
+	return repo.getRefCommitID(BRANCH_PREFIX + name)
+}
+
+// GetTagCommitID returns last commit ID string of given tag.
+func (repo *Repository) GetTagCommitID(name string) (string, error) {
+	return repo.getRefCommitID(TAG_PREFIX + name)
+}
+
+// GetRemoteBranchCommitID returns last commit ID string of given remote branch.
+func (repo *Repository) GetRemoteBranchCommitID(name string) (string, error) {
+	return repo.getRefCommitID(REMOTE_PREFIX + name)
+}
+
+// parseCommitData parses commit information from the (uncompressed) raw
+// data from the commit object.
+// \n\n separate headers from message
+func parseCommitData(data []byte) (*Commit, error) {
+	commit := new(Commit)
+	commit.parents = make([]sha1, 0, 1)
+	// we now have the contents of the commit object. Let's investigate...
+	nextline := 0
+l:
+	for {
+		eol := bytes.IndexByte(data[nextline:], '\n')
+		switch {
+		case eol > 0:
+			line := data[nextline : nextline+eol]
+			spacepos := bytes.IndexByte(line, ' ')
+			reftype := line[:spacepos]
+			switch string(reftype) {
+			case "tree", "object":
+				id, err := NewIDFromString(string(line[spacepos+1:]))
+				if err != nil {
+					return nil, err
+				}
+				commit.Tree.ID = id
+			case "parent":
+				// A commit can have one or more parents
+				oid, err := NewIDFromString(string(line[spacepos+1:]))
+				if err != nil {
+					return nil, err
+				}
+				commit.parents = append(commit.parents, oid)
+			case "author", "tagger":
+				sig, err := newSignatureFromCommitline(line[spacepos+1:])
+				if err != nil {
+					return nil, err
+				}
+				commit.Author = sig
+			case "committer":
+				sig, err := newSignatureFromCommitline(line[spacepos+1:])
+				if err != nil {
+					return nil, err
+				}
+				commit.Committer = sig
+			}
+			nextline += eol + 1
+		case eol == 0:
+			commit.CommitMessage = string(data[nextline+1:])
+			break l
+		default:
+			break l
+		}
+	}
+	return commit, nil
+}
+
+func (repo *Repository) getCommit(id sha1) (*Commit, error) {
+	c, ok := repo.commitCache.Get(id.String())
+	if ok {
+		log("Hit cache: %s", id)
+		return c.(*Commit), nil
+	}
+
+	data, err := NewCommand("cat-file", "commit", id.String()).RunInDirBytes(repo.Path)
+	if err != nil {
+		if strings.Contains(err.Error(), "exit status 128") {
+			return nil, ErrNotExist{id.String(), ""}
+		}
+		return nil, err
+	}
+
+	commit, err := parseCommitData(data)
+	if err != nil {
+		return nil, err
+	}
+	commit.repo = repo
+	commit.ID = id
+
+	repo.commitCache.Set(id.String(), commit)
+	return commit, nil
+}
+
+// GetCommit returns commit object of by ID string.
+func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
+	var err error
+	commitID, err = GetFullCommitID(repo.Path, commitID)
+	if err != nil {
+		return nil, err
+	}
+	id, err := NewIDFromString(commitID)
+	if err != nil {
+		return nil, err
+	}
+
+	return repo.getCommit(id)
+}
+
+// GetBranchCommit returns the last commit of given branch.
+func (repo *Repository) GetBranchCommit(name string) (*Commit, error) {
+	commitID, err := repo.GetBranchCommitID(name)
+	if err != nil {
+		return nil, err
+	}
+	return repo.GetCommit(commitID)
+}
+
+// GetTagCommit returns the commit of given tag.
+func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
+	commitID, err := repo.GetTagCommitID(name)
+	if err != nil {
+		return nil, err
+	}
+	return repo.GetCommit(commitID)
+}
+
+// GetRemoteBranchCommit returns the last commit of given remote branch.
+func (repo *Repository) GetRemoteBranchCommit(name string) (*Commit, error) {
+	commitID, err := repo.GetRemoteBranchCommitID(name)
+	if err != nil {
+		return nil, err
+	}
+	return repo.GetCommit(commitID)
+}
+
+func (repo *Repository) getCommitByPathWithID(id sha1, relpath string) (*Commit, error) {
+	// File name starts with ':' must be escaped.
+	if relpath[0] == ':' {
+		relpath = `\` + relpath
+	}
+
+	stdout, err := NewCommand("log", "-1", _PRETTY_LOG_FORMAT, id.String(), "--", relpath).RunInDir(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	id, err = NewIDFromString(stdout)
+	if err != nil {
+		return nil, err
+	}
+
+	return repo.getCommit(id)
+}
+
+// GetCommitByPath returns the last commit of relative path.
+func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
+	stdout, err := NewCommand("log", "-1", _PRETTY_LOG_FORMAT, "--", relpath).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	commits, err := repo.parsePrettyFormatLogToList(stdout)
+	if err != nil {
+		return nil, err
+	}
+	return commits.Front().Value.(*Commit), nil
+}
+
+func (repo *Repository) CommitsByRangeSize(revision string, page, size int) (*list.List, error) {
+	stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*size),
+		"--max-count="+strconv.Itoa(size), _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	return repo.parsePrettyFormatLogToList(stdout)
+}
+
+var DefaultCommitsPageSize = 30
+
+func (repo *Repository) CommitsByRange(revision string, page int) (*list.List, error) {
+	return repo.CommitsByRangeSize(revision, page, DefaultCommitsPageSize)
+}
+
+func (repo *Repository) searchCommits(id sha1, keyword string) (*list.List, error) {
+	stdout, err := NewCommand("log", id.String(), "-100", "-i", "--grep="+keyword, _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	return repo.parsePrettyFormatLogToList(stdout)
+}
+
+func (repo *Repository) getFilesChanged(id1 string, id2 string) ([]string, error) {
+	stdout, err := NewCommand("diff", "--name-only", id1, id2).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	return strings.Split(string(stdout), "\n"), nil
+}
+
+func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
+	return commitsCount(repo.Path, revision, file)
+}
+
+func (repo *Repository) CommitsByFileAndRangeSize(revision, file string, page, size int) (*list.List, error) {
+	stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*size),
+		"--max-count="+strconv.Itoa(size), _PRETTY_LOG_FORMAT, "--", file).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	return repo.parsePrettyFormatLogToList(stdout)
+}
+
+func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (*list.List, error) {
+	return repo.CommitsByFileAndRangeSize(revision, file, page, DefaultCommitsPageSize)
+}
+
+func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
+	stdout, err := NewCommand("diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
+	if err != nil {
+		return 0, err
+	}
+	return len(strings.Split(stdout, "\n")) - 1, nil
+}
+
+// CommitsBetween returns a list that contains commits between [last, before).
+func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) {
+	if version.Compare(gitVersion, "1.8.0", ">=") {
+		stdout, err := NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
+		if err != nil {
+			return nil, err
+		}
+		return repo.parsePrettyFormatLogToList(bytes.TrimSpace(stdout))
+	}
+
+	// Fallback to stupid solution, which iterates all commits of the repository
+	// if before is not an ancestor of last.
+	l := list.New()
+	if last == nil || last.ParentCount() == 0 {
+		return l, nil
+	}
+
+	var err error
+	cur := last
+	for {
+		if cur.ID.Equal(before.ID) {
+			break
+		}
+		l.PushBack(cur)
+		if cur.ParentCount() == 0 {
+			break
+		}
+		cur, err = cur.Parent(0)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return l, nil
+}
+
+func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, error) {
+	lastCommit, err := repo.GetCommit(last)
+	if err != nil {
+		return nil, err
+	}
+	beforeCommit, err := repo.GetCommit(before)
+	if err != nil {
+		return nil, err
+	}
+	return repo.CommitsBetween(lastCommit, beforeCommit)
+}
+
+func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
+	return commitsCount(repo.Path, start+"..."+end, "")
+}
+
+// The limit is depth, not total number of returned commits.
+func (repo *Repository) commitsBefore(l *list.List, parent *list.Element, id sha1, current, limit int) error {
+	// Reach the limit
+	if limit > 0 && current > limit {
+		return nil
+	}
+
+	commit, err := repo.getCommit(id)
+	if err != nil {
+		return fmt.Errorf("getCommit: %v", err)
+	}
+
+	var e *list.Element
+	if parent == nil {
+		e = l.PushBack(commit)
+	} else {
+		var in = parent
+		for {
+			if in == nil {
+				break
+			} else if in.Value.(*Commit).ID.Equal(commit.ID) {
+				return nil
+			} else if in.Next() == nil {
+				break
+			}
+
+			if in.Value.(*Commit).Committer.When.Equal(commit.Committer.When) {
+				break
+			}
+
+			if in.Value.(*Commit).Committer.When.After(commit.Committer.When) &&
+				in.Next().Value.(*Commit).Committer.When.Before(commit.Committer.When) {
+				break
+			}
+
+			in = in.Next()
+		}
+
+		e = l.InsertAfter(commit, in)
+	}
+
+	pr := parent
+	if commit.ParentCount() > 1 {
+		pr = e
+	}
+
+	for i := 0; i < commit.ParentCount(); i++ {
+		id, err := commit.ParentID(i)
+		if err != nil {
+			return err
+		}
+		err = repo.commitsBefore(l, pr, id, current+1, limit)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) {
+	l := list.New()
+	return l, repo.commitsBefore(l, nil, id, 1, 0)
+}
+
+func (repo *Repository) getCommitsBeforeLimit(id sha1, num int) (*list.List, error) {
+	l := list.New()
+	return l, repo.commitsBefore(l, nil, id, 1, num)
+}
+
+// CommitsAfterDate returns a list of commits which committed after given date.
+// The format of date should be in RFC3339.
+func (repo *Repository) CommitsAfterDate(date string) (*list.List, error) {
+	stdout, err := NewCommand("log", _PRETTY_LOG_FORMAT, "--since="+date).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	return repo.parsePrettyFormatLogToList(stdout)
+}
+
+// CommitsCount returns number of total commits of until given revision.
+func CommitsCount(repoPath, revision string) (int64, error) {
+	return commitsCount(repoPath, revision, "")
+}
+
+// GetLatestCommitDate returns the date of latest commit of repository.
+// If branch is empty, it returns the latest commit across all branches.
+func GetLatestCommitDate(repoPath, branch string) (time.Time, error) {
+	cmd := NewCommand("for-each-ref", "--count=1", "--sort=-committerdate", "--format=%(committerdate:iso8601)")
+	if len(branch) > 0 {
+		cmd.AddArguments("refs/heads/" + branch)
+	}
+	stdout, err := cmd.RunInDir(repoPath)
+	if err != nil {
+		return time.Time{}, err
+	}
+
+	return time.Parse("2006-01-02 15:04:05 -0700", strings.TrimSpace(stdout))
+}

+ 400 - 0
vendor/github.com/G-Node/git-module/repo_diff.go

@@ -0,0 +1,400 @@
+// Copyright 2017 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// DiffLineType represents the type of a line in diff.
+type DiffLineType uint8
+
+const (
+	DIFF_LINE_PLAIN DiffLineType = iota + 1
+	DIFF_LINE_ADD
+	DIFF_LINE_DEL
+	DIFF_LINE_SECTION
+)
+
+// DiffFileType represents the file status in diff.
+type DiffFileType uint8
+
+const (
+	DIFF_FILE_ADD DiffFileType = iota + 1
+	DIFF_FILE_CHANGE
+	DIFF_FILE_DEL
+	DIFF_FILE_RENAME
+)
+
+// DiffLine represents a line in diff.
+type DiffLine struct {
+	LeftIdx  int
+	RightIdx int
+	Type     DiffLineType
+	Content  string
+}
+
+func (d *DiffLine) GetType() int {
+	return int(d.Type)
+}
+
+// DiffSection represents a section in diff.
+type DiffSection struct {
+	Name  string
+	Lines []*DiffLine
+}
+
+// Line returns a specific line by type (add or del) and file line number from a section.
+func (diffSection *DiffSection) Line(lineType DiffLineType, idx int) *DiffLine {
+	var (
+		difference    = 0
+		addCount      = 0
+		delCount      = 0
+		matchDiffLine *DiffLine
+	)
+
+LOOP:
+	for _, diffLine := range diffSection.Lines {
+		switch diffLine.Type {
+		case DIFF_LINE_ADD:
+			addCount++
+		case DIFF_LINE_DEL:
+			delCount++
+		default:
+			if matchDiffLine != nil {
+				break LOOP
+			}
+			difference = diffLine.RightIdx - diffLine.LeftIdx
+			addCount = 0
+			delCount = 0
+		}
+
+		switch lineType {
+		case DIFF_LINE_DEL:
+			if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
+				matchDiffLine = diffLine
+			}
+		case DIFF_LINE_ADD:
+			if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
+				matchDiffLine = diffLine
+			}
+		}
+	}
+
+	if addCount == delCount {
+		return matchDiffLine
+	}
+	return nil
+}
+
+// DiffFile represents a file in diff.
+type DiffFile struct {
+	Name               string
+	OldName            string
+	Index              string // 40-byte SHA, Changed/New: new SHA; Deleted: old SHA
+	Addition, Deletion int
+	Type               DiffFileType
+	IsCreated          bool
+	IsDeleted          bool
+	IsBin              bool
+	IsRenamed          bool
+	IsSubmodule        bool
+	Sections           []*DiffSection
+	IsIncomplete       bool
+}
+
+func (diffFile *DiffFile) GetType() int {
+	return int(diffFile.Type)
+}
+
+func (diffFile *DiffFile) NumSections() int {
+	return len(diffFile.Sections)
+}
+
+// Diff contains all information of a specific diff output.
+type Diff struct {
+	TotalAddition, TotalDeletion int
+	Files                        []*DiffFile
+	IsIncomplete                 bool
+}
+
+func (diff *Diff) NumFiles() int {
+	return len(diff.Files)
+}
+
+const _DIFF_HEAD = "diff --git "
+
+// ParsePatch takes a reader and parses everything it receives in diff format.
+func ParsePatch(done chan<- error, maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) *Diff {
+	var (
+		diff = &Diff{Files: make([]*DiffFile, 0)}
+
+		curFile    *DiffFile
+		curSection = &DiffSection{
+			Lines: make([]*DiffLine, 0, 10),
+		}
+
+		leftLine, rightLine int
+		lineCount           int
+		curFileLinesCount   int
+	)
+	input := bufio.NewReader(reader)
+	isEOF := false
+	for !isEOF {
+		// TODO: would input.ReadBytes be more memory-efficient?
+		line, err := input.ReadString('\n')
+		if err != nil {
+			if err == io.EOF {
+				isEOF = true
+			} else {
+				done <- fmt.Errorf("ReadString: %v", err)
+				return nil
+			}
+		}
+
+		if len(line) > 0 && line[len(line)-1] == '\n' {
+			// Remove line break.
+			line = line[:len(line)-1]
+		}
+
+		if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") || len(line) == 0 {
+			continue
+		}
+
+		curFileLinesCount++
+		lineCount++
+
+		// Diff data too large, we only show the first about maxlines lines
+		if curFileLinesCount >= maxLines || len(line) >= maxLineCharacteres {
+			curFile.IsIncomplete = true
+		}
+
+		switch {
+		case line[0] == ' ':
+			diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
+			leftLine++
+			rightLine++
+			curSection.Lines = append(curSection.Lines, diffLine)
+			continue
+		case line[0] == '@':
+			curSection = &DiffSection{}
+			curFile.Sections = append(curFile.Sections, curSection)
+			ss := strings.Split(line, "@@")
+			diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line}
+			curSection.Lines = append(curSection.Lines, diffLine)
+
+			// Parse line number.
+			ranges := strings.Split(ss[1][1:], " ")
+			leftLine, _ = strconv.Atoi(strings.Split(ranges[0], ",")[0][1:])
+			if len(ranges) > 1 {
+				rightLine, _ = strconv.Atoi(strings.Split(ranges[1], ",")[0])
+			} else {
+				rightLine = leftLine
+			}
+			continue
+		case line[0] == '+':
+			curFile.Addition++
+			diff.TotalAddition++
+			diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
+			rightLine++
+			curSection.Lines = append(curSection.Lines, diffLine)
+			continue
+		case line[0] == '-':
+			curFile.Deletion++
+			diff.TotalDeletion++
+			diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine}
+			if leftLine > 0 {
+				leftLine++
+			}
+			curSection.Lines = append(curSection.Lines, diffLine)
+		case strings.HasPrefix(line, "Binary"):
+			curFile.IsBin = true
+			continue
+		}
+
+		// Get new file.
+		if strings.HasPrefix(line, _DIFF_HEAD) {
+			middle := -1
+
+			// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
+			// e.g. diff --git "a/xxx" "b/xxx"
+			hasQuote := line[len(_DIFF_HEAD)] == '"'
+			if hasQuote {
+				middle = strings.Index(line, ` "b/`)
+			} else {
+				middle = strings.Index(line, " b/")
+			}
+
+			beg := len(_DIFF_HEAD)
+			a := line[beg+2 : middle]
+			b := line[middle+3:]
+			if hasQuote {
+				a = string(UnescapeChars([]byte(a[1 : len(a)-1])))
+				b = string(UnescapeChars([]byte(b[1 : len(b)-1])))
+			}
+
+			curFile = &DiffFile{
+				Name:     a,
+				Type:     DIFF_FILE_CHANGE,
+				Sections: make([]*DiffSection, 0, 10),
+			}
+			diff.Files = append(diff.Files, curFile)
+			if len(diff.Files) >= maxFiles {
+				diff.IsIncomplete = true
+				io.Copy(ioutil.Discard, reader)
+				break
+			}
+			curFileLinesCount = 0
+
+			// Check file diff type and submodule.
+		CHECK_TYPE:
+			for {
+				line, err := input.ReadString('\n')
+				if err != nil {
+					if err == io.EOF {
+						isEOF = true
+					} else {
+						done <- fmt.Errorf("ReadString: %v", err)
+						return nil
+					}
+				}
+
+				switch {
+				case strings.HasPrefix(line, "new file"):
+					curFile.Type = DIFF_FILE_ADD
+					curFile.IsCreated = true
+					curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
+				case strings.HasPrefix(line, "deleted"):
+					curFile.Type = DIFF_FILE_DEL
+					curFile.IsDeleted = true
+					curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
+				case strings.HasPrefix(line, "index"):
+					if curFile.IsDeleted {
+						curFile.Index = line[6:46]
+					} else if len(line) >= 88 {
+						curFile.Index = line[49:88]
+					} else {
+						curFile.Index = curFile.Name
+					}
+					break CHECK_TYPE
+				case strings.HasPrefix(line, "similarity index 100%"):
+					curFile.Type = DIFF_FILE_RENAME
+					curFile.IsRenamed = true
+					curFile.OldName = curFile.Name
+					curFile.Name = b
+					curFile.Index = b
+					break CHECK_TYPE
+				case strings.HasPrefix(line, "old mode"):
+					break CHECK_TYPE
+				}
+			}
+		}
+	}
+
+	done <- nil
+	return diff
+}
+
+// GetDiffRange returns a parsed diff object between given commits.
+func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
+	repo, err := OpenRepository(repoPath)
+	if err != nil {
+		return nil, err
+	}
+
+	commit, err := repo.GetCommit(afterCommitID)
+	if err != nil {
+		return nil, err
+	}
+
+	cmd := NewCommand()
+	if len(beforeCommitID) == 0 {
+		// First commit of repository
+		if commit.ParentCount() == 0 {
+			cmd.AddArguments("show", "--full-index", afterCommitID)
+		} else {
+			c, _ := commit.Parent(0)
+			cmd.AddArguments("diff", "--full-index", "-M", c.ID.String(), afterCommitID)
+		}
+	} else {
+		cmd.AddArguments("diff", "--full-index", "-M", beforeCommitID, afterCommitID)
+	}
+
+	stdout, w := io.Pipe()
+	done := make(chan error)
+	var diff *Diff
+	go func() {
+		diff = ParsePatch(done, maxLines, maxLineCharacteres, maxFiles, stdout)
+	}()
+
+	stderr := new(bytes.Buffer)
+	err = cmd.RunInDirTimeoutPipeline(2*time.Minute, repoPath, w, stderr)
+	w.Close() // Close writer to exit parsing goroutine
+	if err != nil {
+		return nil, concatenateError(err, stderr.String())
+	}
+
+	return diff, <-done
+}
+
+// RawDiffType represents the type of raw diff format.
+type RawDiffType string
+
+const (
+	RAW_DIFF_NORMAL RawDiffType = "diff"
+	RAW_DIFF_PATCH  RawDiffType = "patch"
+)
+
+// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
+func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Writer) error {
+	repo, err := OpenRepository(repoPath)
+	if err != nil {
+		return fmt.Errorf("OpenRepository: %v", err)
+	}
+
+	commit, err := repo.GetCommit(commitID)
+	if err != nil {
+		return err
+	}
+
+	cmd := NewCommand()
+	switch diffType {
+	case RAW_DIFF_NORMAL:
+		if commit.ParentCount() == 0 {
+			cmd.AddArguments("show", commitID)
+		} else {
+			c, _ := commit.Parent(0)
+			cmd.AddArguments("diff", "-M", c.ID.String(), commitID)
+		}
+	case RAW_DIFF_PATCH:
+		if commit.ParentCount() == 0 {
+			cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root", commitID)
+		} else {
+			c, _ := commit.Parent(0)
+			query := fmt.Sprintf("%s...%s", commitID, c.ID.String())
+			cmd.AddArguments("format-patch", "--no-signature", "--stdout", query)
+		}
+	default:
+		return fmt.Errorf("invalid diffType: %s", diffType)
+	}
+
+	stderr := new(bytes.Buffer)
+	if err = cmd.RunInDirPipeline(repoPath, writer, stderr); err != nil {
+		return concatenateError(err, stderr.String())
+	}
+	return nil
+}
+
+// GetDiffCommit returns a parsed diff object of given commit.
+func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
+	return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
+}

+ 13 - 0
vendor/github.com/G-Node/git-module/repo_hook.go

@@ -0,0 +1,13 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+func (repo *Repository) GetHook(name string) (*Hook, error) {
+	return GetHook(repo.Path, name)
+}
+
+func (repo *Repository) Hooks() ([]*Hook, error) {
+	return ListHooks(repo.Path)
+}

+ 14 - 0
vendor/github.com/G-Node/git-module/repo_object.go

@@ -0,0 +1,14 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+type ObjectType string
+
+const (
+	OBJECT_COMMIT ObjectType = "commit"
+	OBJECT_TREE   ObjectType = "tree"
+	OBJECT_BLOB   ObjectType = "blob"
+	OBJECT_TAG    ObjectType = "tag"
+)

+ 81 - 0
vendor/github.com/G-Node/git-module/repo_pull.go

@@ -0,0 +1,81 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"container/list"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PullRequestInfo represents needed information for a pull request.
+type PullRequestInfo struct {
+	MergeBase string
+	Commits   *list.List
+	NumFiles  int
+}
+
+// GetMergeBase checks and returns merge base of two branches.
+func (repo *Repository) GetMergeBase(base, head string) (string, error) {
+	stdout, err := NewCommand("merge-base", base, head).RunInDir(repo.Path)
+	if err != nil {
+		if strings.Contains(err.Error(), "exit status 1") {
+			return "", ErrNoMergeBase{}
+		}
+		return "", err
+	}
+	return strings.TrimSpace(stdout), nil
+}
+
+// GetPullRequestInfo generates and returns pull request information
+// between base and head branches of repositories.
+func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch string) (_ *PullRequestInfo, err error) {
+	var remoteBranch string
+
+	// We don't need a temporary remote for same repository.
+	if repo.Path != basePath {
+		// Add a temporary remote
+		tmpRemote := strconv.FormatInt(time.Now().UnixNano(), 10)
+		if err = repo.AddRemote(tmpRemote, basePath, true); err != nil {
+			return nil, fmt.Errorf("AddRemote: %v", err)
+		}
+		defer repo.RemoveRemote(tmpRemote)
+
+		remoteBranch = "remotes/" + tmpRemote + "/" + baseBranch
+	} else {
+		remoteBranch = baseBranch
+	}
+
+	prInfo := new(PullRequestInfo)
+	prInfo.MergeBase, err = repo.GetMergeBase(remoteBranch, headBranch)
+	if err != nil {
+		return nil, err
+	}
+
+	logs, err := NewCommand("log", prInfo.MergeBase+"..."+headBranch, _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	prInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
+	if err != nil {
+		return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
+	}
+
+	// Count number of changed files.
+	stdout, err := NewCommand("diff", "--name-only", remoteBranch+"..."+headBranch).RunInDir(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	prInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1
+
+	return prInfo, nil
+}
+
+// GetPatch generates and returns patch data between given revisions.
+func (repo *Repository) GetPatch(base, head string) ([]byte, error) {
+	return NewCommand("diff", "-p", "--binary", base, head).RunInDirBytes(repo.Path)
+}

+ 209 - 0
vendor/github.com/G-Node/git-module/repo_tag.go

@@ -0,0 +1,209 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/mcuadros/go-version"
+)
+
+const TAG_PREFIX = "refs/tags/"
+
+// IsTagExist returns true if given tag exists in the repository.
+func IsTagExist(repoPath, name string) bool {
+	return IsReferenceExist(repoPath, TAG_PREFIX+name)
+}
+
+func (repo *Repository) IsTagExist(name string) bool {
+	return IsTagExist(repo.Path, name)
+}
+
+func (repo *Repository) CreateTag(name, revision string) error {
+	_, err := NewCommand("tag", name, revision).RunInDir(repo.Path)
+	return err
+}
+
+func (repo *Repository) getTag(id sha1) (*Tag, error) {
+	t, ok := repo.tagCache.Get(id.String())
+	if ok {
+		log("Hit cache: %s", id)
+		return t.(*Tag), nil
+	}
+
+	// Get tag type
+	tp, err := NewCommand("cat-file", "-t", id.String()).RunInDir(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	tp = strings.TrimSpace(tp)
+
+	// Tag is a commit.
+	if ObjectType(tp) == OBJECT_COMMIT {
+		tag := &Tag{
+			ID:     id,
+			Object: id,
+			Type:   string(OBJECT_COMMIT),
+			repo:   repo,
+		}
+
+		repo.tagCache.Set(id.String(), tag)
+		return tag, nil
+	}
+
+	// Tag with message.
+	data, err := NewCommand("cat-file", "-p", id.String()).RunInDirBytes(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	tag, err := parseTagData(data)
+	if err != nil {
+		return nil, err
+	}
+
+	tag.ID = id
+	tag.repo = repo
+
+	repo.tagCache.Set(id.String(), tag)
+	return tag, nil
+}
+
+// GetTag returns a Git tag by given name.
+func (repo *Repository) GetTag(name string) (*Tag, error) {
+	stdout, err := NewCommand("show-ref", "--tags", name).RunInDir(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	id, err := NewIDFromString(strings.Split(stdout, " ")[0])
+	if err != nil {
+		return nil, err
+	}
+
+	tag, err := repo.getTag(id)
+	if err != nil {
+		return nil, err
+	}
+	tag.Name = name
+	return tag, nil
+}
+
+// GetTags returns all tags of the repository.
+func (repo *Repository) GetTags() ([]string, error) {
+	cmd := NewCommand("tag", "-l")
+	if version.Compare(gitVersion, "2.4.9", ">=") {
+		cmd.AddArguments("--sort=-creatordate")
+	}
+
+	stdout, err := cmd.RunInDir(repo.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	tags := strings.Split(stdout, "\n")
+	tags = tags[:len(tags)-1]
+
+	if version.Compare(gitVersion, "2.4.9", "<") {
+		version.Sort(tags)
+
+		// Reverse order
+		for i := 0; i < len(tags)/2; i++ {
+			j := len(tags) - i - 1
+			tags[i], tags[j] = tags[j], tags[i]
+		}
+	}
+
+	return tags, nil
+}
+
+type TagsResult struct {
+	// Indicates whether results include the latest tag.
+	HasLatest bool
+	// If results do not include the latest tag, a indicator 'after' to go back.
+	PreviousAfter string
+	// Indicates whether results include the oldest tag.
+	ReachEnd bool
+	// List of returned tags.
+	Tags []string
+}
+
+// GetTagsAfter returns list of tags 'after' (exlusive) given tag.
+func (repo *Repository) GetTagsAfter(after string, limit int) (*TagsResult, error) {
+	allTags, err := repo.GetTags()
+	if err != nil {
+		return nil, fmt.Errorf("GetTags: %v", err)
+	}
+
+	if limit < 0 {
+		limit = 0
+	}
+
+	numAllTags := len(allTags)
+	if len(after) == 0 && limit == 0 {
+		return &TagsResult{
+			HasLatest: true,
+			ReachEnd:  true,
+			Tags:      allTags,
+		}, nil
+	} else if len(after) == 0 && limit > 0 {
+		endIdx := limit
+		if limit >= numAllTags {
+			endIdx = numAllTags
+		}
+		return &TagsResult{
+			HasLatest: true,
+			ReachEnd:  limit >= numAllTags,
+			Tags:      allTags[:endIdx],
+		}, nil
+	}
+
+	previousAfter := ""
+	hasMatch := false
+	tags := make([]string, 0, len(allTags))
+	for i := range allTags {
+		if hasMatch {
+			tags = allTags[i:]
+			break
+		}
+		if allTags[i] == after {
+			hasMatch = true
+			if limit > 0 && i-limit >= 0 {
+				previousAfter = allTags[i-limit]
+			}
+			continue
+		}
+	}
+
+	if !hasMatch {
+		tags = allTags
+	}
+
+	// If all tags after match is equal to the limit, it reaches the oldest tag as well.
+	if limit == 0 || len(tags) <= limit {
+		return &TagsResult{
+			HasLatest:     !hasMatch,
+			PreviousAfter: previousAfter,
+			ReachEnd:      true,
+			Tags:          tags,
+		}, nil
+	}
+	return &TagsResult{
+		HasLatest:     !hasMatch,
+		PreviousAfter: previousAfter,
+		Tags:          tags[:limit],
+	}, nil
+}
+
+// DeleteTag deletes a tag from the repository
+func (repo *Repository) DeleteTag(name string) error {
+	cmd := NewCommand("tag", "-d")
+
+	cmd.AddArguments(name)
+	_, err := cmd.RunInDir(repo.Path)
+
+	return err
+}

+ 26 - 0
vendor/github.com/G-Node/git-module/repo_tree.go

@@ -0,0 +1,26 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+func (repo *Repository) getTree(id sha1) (*Tree, error) {
+	treePath := filepathFromSHA1(repo.Path, id.String())
+	if isFile(treePath) {
+		_, err := NewCommand("ls-tree", id.String()).RunInDir(repo.Path)
+		if err != nil {
+			return nil, ErrNotExist{id.String(), ""}
+		}
+	}
+
+	return NewTree(repo, id), nil
+}
+
+// Find the tree object in the repository.
+func (repo *Repository) GetTree(idStr string) (*Tree, error) {
+	id, err := NewIDFromString(idStr)
+	if err != nil {
+		return nil, err
+	}
+	return repo.getTree(id)
+}

+ 93 - 0
vendor/github.com/G-Node/git-module/sha1.go

@@ -0,0 +1,93 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"encoding/hex"
+	"fmt"
+	"strings"
+)
+
+const EMPTY_SHA = "0000000000000000000000000000000000000000"
+
+type sha1 [20]byte
+
+// Equal returns true if s has the same sha1 as caller.
+// Support 40-length-string, []byte, sha1.
+func (id sha1) Equal(s2 interface{}) bool {
+	switch v := s2.(type) {
+	case string:
+		if len(v) != 40 {
+			return false
+		}
+		return v == id.String()
+	case []byte:
+		if len(v) != 20 {
+			return false
+		}
+		for i, v := range v {
+			if id[i] != v {
+				return false
+			}
+		}
+	case sha1:
+		for i, v := range v {
+			if id[i] != v {
+				return false
+			}
+		}
+	default:
+		return false
+	}
+	return true
+}
+
+// String returns string (hex) representation of the Oid.
+func (s sha1) String() string {
+	result := make([]byte, 0, 40)
+	hexvalues := []byte("0123456789abcdef")
+	for i := 0; i < 20; i++ {
+		result = append(result, hexvalues[s[i]>>4])
+		result = append(result, hexvalues[s[i]&0xf])
+	}
+	return string(result)
+}
+
+// MustID always creates a new sha1 from a [20]byte array with no validation of input.
+func MustID(b []byte) sha1 {
+	var id sha1
+	for i := 0; i < 20; i++ {
+		id[i] = b[i]
+	}
+	return id
+}
+
+// NewID creates a new sha1 from a [20]byte array.
+func NewID(b []byte) (sha1, error) {
+	if len(b) != 20 {
+		return sha1{}, fmt.Errorf("Length must be 20: %v", b)
+	}
+	return MustID(b), nil
+}
+
+// MustIDFromString always creates a new sha from a ID with no validation of input.
+func MustIDFromString(s string) sha1 {
+	b, _ := hex.DecodeString(s)
+	return MustID(b)
+}
+
+// NewIDFromString creates a new sha1 from a ID string of length 40.
+func NewIDFromString(s string) (sha1, error) {
+	var id sha1
+	s = strings.TrimSpace(s)
+	if len(s) != 40 {
+		return id, fmt.Errorf("Length must be 40: %s", s)
+	}
+	b, err := hex.DecodeString(s)
+	if err != nil {
+		return id, err
+	}
+	return NewID(b)
+}

+ 48 - 0
vendor/github.com/G-Node/git-module/signature.go

@@ -0,0 +1,48 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bytes"
+	"strconv"
+	"time"
+)
+
+// Signature represents the Author or Committer information.
+type Signature struct {
+	Email string
+	Name  string
+	When  time.Time
+}
+
+// Helper to get a signature from the commit line, which looks like these:
+//     author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200
+//     author Patrick Gundlach <gundlach@speedata.de> Thu, 07 Apr 2005 22:13:13 +0200
+// but without the "author " at the beginning (this method should)
+// be used for author and committer.
+//
+// FIXME: include timezone for timestamp!
+func newSignatureFromCommitline(line []byte) (_ *Signature, err error) {
+	sig := new(Signature)
+	emailStart := bytes.IndexByte(line, '<')
+	sig.Name = string(line[:emailStart-1])
+	emailEnd := bytes.IndexByte(line, '>')
+	sig.Email = string(line[emailStart+1 : emailEnd])
+
+	// Check date format.
+	firstChar := line[emailEnd+2]
+	if firstChar >= 48 && firstChar <= 57 {
+		timestop := bytes.IndexByte(line[emailEnd+2:], ' ')
+		timestring := string(line[emailEnd+2 : emailEnd+2+timestop])
+		seconds, _ := strconv.ParseInt(timestring, 10, 64)
+		sig.When = time.Unix(seconds, 0)
+	} else {
+		sig.When, err = time.Parse("Mon Jan _2 15:04:05 2006 -0700", string(line[emailEnd+2:]))
+		if err != nil {
+			return nil, err
+		}
+	}
+	return sig, nil
+}

+ 78 - 0
vendor/github.com/G-Node/git-module/submodule.go

@@ -0,0 +1,78 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import "strings"
+
+type SubModule struct {
+	Name string
+	URL  string
+}
+
+// SubModuleFile represents a file with submodule type.
+type SubModuleFile struct {
+	*Commit
+
+	refURL string
+	refID  string
+}
+
+func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile {
+	return &SubModuleFile{
+		Commit: c,
+		refURL: refURL,
+		refID:  refID,
+	}
+}
+
+// RefURL guesses and returns reference URL.
+func (sf *SubModuleFile) RefURL(urlPrefix string, parentPath string) string {
+	if sf.refURL == "" {
+		return ""
+	}
+
+	url := strings.TrimSuffix(sf.refURL, ".git")
+
+	// git://xxx/user/repo
+	if strings.HasPrefix(url, "git://") {
+		return "http://" + strings.TrimPrefix(url, "git://")
+	}
+
+	// http[s]://xxx/user/repo
+	if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
+		return url
+	}
+
+	// Relative url prefix check (according to git submodule documentation)
+	if strings.HasPrefix(url, "./") || strings.HasPrefix(url, "../") {
+		// ...construct and return correct submodule url here...
+		idx := strings.Index(parentPath, "/src/")
+		if idx == -1 {
+			return url
+		}
+		return strings.TrimSuffix(urlPrefix, "/") + parentPath[:idx] + "/" + url
+	}
+
+	// sysuser@xxx:user/repo
+	i := strings.Index(url, "@")
+	j := strings.LastIndex(url, ":")
+
+	// Only process when i < j because git+ssh://git@git.forwardbias.in/npploader.git
+	if i > -1 && j > -1 && i < j {
+		// fix problem with reverse proxy works only with local server
+		if strings.Contains(urlPrefix, url[i+1:j]) {
+			return urlPrefix + url[j+1:]
+		} else {
+			return "http://" + url[i+1:j] + "/" + url[j+1:]
+		}
+	}
+
+	return url
+}
+
+// RefID returns reference ID.
+func (sf *SubModuleFile) RefID() string {
+	return sf.refID
+}

+ 65 - 0
vendor/github.com/G-Node/git-module/tag.go

@@ -0,0 +1,65 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import "bytes"
+
+// Tag represents a Git tag.
+type Tag struct {
+	Name    string
+	ID      sha1
+	repo    *Repository
+	Object  sha1 // The id of this commit object
+	Type    string
+	Tagger  *Signature
+	Message string
+}
+
+func (tag *Tag) Commit() (*Commit, error) {
+	return tag.repo.getCommit(tag.Object)
+}
+
+// Parse commit information from the (uncompressed) raw
+// data from the commit object.
+// \n\n separate headers from message
+func parseTagData(data []byte) (*Tag, error) {
+	tag := new(Tag)
+	// we now have the contents of the commit object. Let's investigate...
+	nextline := 0
+l:
+	for {
+		eol := bytes.IndexByte(data[nextline:], '\n')
+		switch {
+		case eol > 0:
+			line := data[nextline : nextline+eol]
+			spacepos := bytes.IndexByte(line, ' ')
+			reftype := line[:spacepos]
+			switch string(reftype) {
+			case "object":
+				id, err := NewIDFromString(string(line[spacepos+1:]))
+				if err != nil {
+					return nil, err
+				}
+				tag.Object = id
+			case "type":
+				// A commit can have one or more parents
+				tag.Type = string(line[spacepos+1:])
+			case "tagger":
+				sig, err := newSignatureFromCommitline(line[spacepos+1:])
+				if err != nil {
+					return nil, err
+				}
+				tag.Tagger = sig
+			}
+			nextline += eol + 1
+		case eol == 0:
+			tag.Message = string(data[nextline+1:])
+			break l
+		default:
+			break l
+		}
+	}
+	return tag, nil
+}

+ 149 - 0
vendor/github.com/G-Node/git-module/tree.go

@@ -0,0 +1,149 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+)
+
+// Tree represents a flat directory listing.
+type Tree struct {
+	ID   sha1
+	repo *Repository
+
+	// parent tree
+	ptree *Tree
+
+	entries       Entries
+	entriesParsed bool
+}
+
+func NewTree(repo *Repository, id sha1) *Tree {
+	return &Tree{
+		ID:   id,
+		repo: repo,
+	}
+}
+
+// Predefine []byte variables to avoid runtime allocations.
+var (
+	escapedSlash = []byte(`\\`)
+	regularSlash = []byte(`\`)
+	escapedTab   = []byte(`\t`)
+	regularTab   = []byte("\t")
+)
+
+// UnescapeChars reverses escaped characters.
+func UnescapeChars(in []byte) []byte {
+	// LEGACY [Go 1.7]: use more expressive bytes.ContainsAny
+	if bytes.IndexAny(in, "\\\t") == -1 {
+		return in
+	}
+
+	out := bytes.Replace(in, escapedSlash, regularSlash, -1)
+	out = bytes.Replace(out, escapedTab, regularTab, -1)
+	return out
+}
+
+// parseTreeData parses tree information from the (uncompressed) raw
+// data from the tree object.
+func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
+	entries := make([]*TreeEntry, 0, 10)
+	l := len(data)
+	pos := 0
+	for pos < l {
+		entry := new(TreeEntry)
+		entry.ptree = tree
+		step := 6
+		switch string(data[pos : pos+step]) {
+		case "100644", "100664":
+			entry.mode = ENTRY_MODE_BLOB
+			entry.Type = OBJECT_BLOB
+		case "100755":
+			entry.mode = ENTRY_MODE_EXEC
+			entry.Type = OBJECT_BLOB
+		case "120000":
+			entry.mode = ENTRY_MODE_SYMLINK
+			entry.Type = OBJECT_BLOB
+		case "160000":
+			entry.mode = ENTRY_MODE_COMMIT
+			entry.Type = OBJECT_COMMIT
+
+			step = 8
+		case "040000":
+			entry.mode = ENTRY_MODE_TREE
+			entry.Type = OBJECT_TREE
+		default:
+			return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+step]))
+		}
+		pos += step + 6 // Skip string type of entry type.
+
+		step = 40
+		id, err := NewIDFromString(string(data[pos : pos+step]))
+		if err != nil {
+			return nil, err
+		}
+		entry.ID = id
+		pos += step + 1 // Skip half of sha1.
+
+		step = bytes.IndexByte(data[pos:], '\n')
+
+		// In case entry name is surrounded by double quotes(it happens only in git-shell).
+		if data[pos] == '"' {
+			entry.name = string(UnescapeChars(data[pos+1 : pos+step-1]))
+		} else {
+			entry.name = string(data[pos : pos+step])
+		}
+
+		pos += step + 1
+		entries = append(entries, entry)
+	}
+	return entries, nil
+}
+
+func (t *Tree) SubTree(rpath string) (*Tree, error) {
+	if len(rpath) == 0 {
+		return t, nil
+	}
+
+	paths := strings.Split(rpath, "/")
+	var (
+		err error
+		g   = t
+		p   = t
+		te  *TreeEntry
+	)
+	for _, name := range paths {
+		te, err = p.GetTreeEntryByPath(name)
+		if err != nil {
+			return nil, err
+		}
+
+		g, err = t.repo.getTree(te.ID)
+		if err != nil {
+			return nil, err
+		}
+		g.ptree = p
+		p = g
+	}
+	return g, nil
+}
+
+// ListEntries returns all entries of current tree.
+func (t *Tree) ListEntries() (Entries, error) {
+	if t.entriesParsed {
+		return t.entries, nil
+	}
+	t.entriesParsed = true
+
+	stdout, err := NewCommand("ls-tree", t.ID.String()).RunInDirBytes(t.repo.Path)
+	if err != nil {
+		return nil, err
+	}
+	t.entries, err = parseTreeData(t, stdout)
+	return t.entries, err
+}

+ 57 - 0
vendor/github.com/G-Node/git-module/tree_blob.go

@@ -0,0 +1,57 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"path"
+	"strings"
+)
+
+func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
+	if len(relpath) == 0 {
+		return &TreeEntry{
+			ID:   t.ID,
+			Type: OBJECT_TREE,
+			mode: ENTRY_MODE_TREE,
+		}, nil
+	}
+
+	relpath = path.Clean(relpath)
+	parts := strings.Split(relpath, "/")
+	var err error
+	tree := t
+	for i, name := range parts {
+		if i == len(parts)-1 {
+			entries, err := tree.ListEntries()
+			if err != nil {
+				return nil, err
+			}
+			for _, v := range entries {
+				if v.name == name {
+					return v, nil
+				}
+			}
+		} else {
+			tree, err = tree.SubTree(name)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+	return nil, ErrNotExist{"", relpath}
+}
+
+func (t *Tree) GetBlobByPath(relpath string) (*Blob, error) {
+	entry, err := t.GetTreeEntryByPath(relpath)
+	if err != nil {
+		return nil, err
+	}
+
+	if !entry.IsDir() {
+		return entry.Blob(), nil
+	}
+
+	return nil, ErrNotExist{"", relpath}
+}

+ 231 - 0
vendor/github.com/G-Node/git-module/tree_entry.go

@@ -0,0 +1,231 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"path"
+	"path/filepath"
+	"runtime"
+	"sort"
+	"strconv"
+	"strings"
+)
+
+type EntryMode int
+
+// There are only a few file modes in Git. They look like unix file modes, but they can only be
+// one of these.
+const (
+	ENTRY_MODE_BLOB    EntryMode = 0100644
+	ENTRY_MODE_EXEC    EntryMode = 0100755
+	ENTRY_MODE_SYMLINK EntryMode = 0120000
+	ENTRY_MODE_COMMIT  EntryMode = 0160000
+	ENTRY_MODE_TREE    EntryMode = 0040000
+)
+
+type TreeEntry struct {
+	ID   sha1
+	Type ObjectType
+
+	mode EntryMode
+	name string
+
+	ptree *Tree
+
+	commited bool
+
+	size  int64
+	sized bool
+}
+
+func (te *TreeEntry) SetSize(size int64) {
+	te.size = size
+	te.sized = true
+}
+
+func (te *TreeEntry) Name() string {
+	return te.name
+}
+
+func (te *TreeEntry) Size() int64 {
+	if te.IsDir() {
+		return 0
+	} else if te.sized {
+		return te.size
+	}
+
+	stdout, err := NewCommand("cat-file", "-s", te.ID.String()).RunInDir(te.ptree.repo.Path)
+	if err != nil {
+		return 0
+	}
+
+	te.sized = true
+	te.size, _ = strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
+	return te.size
+}
+
+func (te *TreeEntry) IsSubModule() bool {
+	return te.mode == ENTRY_MODE_COMMIT
+}
+
+func (te *TreeEntry) IsDir() bool {
+	return te.mode == ENTRY_MODE_TREE
+}
+
+func (te *TreeEntry) IsLink() bool {
+	return te.mode == ENTRY_MODE_SYMLINK
+}
+
+func (te *TreeEntry) Blob() *Blob {
+	return &Blob{
+		repo:      te.ptree.repo,
+		TreeEntry: te,
+	}
+}
+
+type Entries []*TreeEntry
+
+var sorter = []func(t1, t2 *TreeEntry) bool{
+	func(t1, t2 *TreeEntry) bool {
+		return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
+	},
+	func(t1, t2 *TreeEntry) bool {
+		return t1.name < t2.name
+	},
+}
+
+func (tes Entries) Len() int      { return len(tes) }
+func (tes Entries) Swap(i, j int) { tes[i], tes[j] = tes[j], tes[i] }
+func (tes Entries) Less(i, j int) bool {
+	t1, t2 := tes[i], tes[j]
+	var k int
+	for k = 0; k < len(sorter)-1; k++ {
+		sort := sorter[k]
+		switch {
+		case sort(t1, t2):
+			return true
+		case sort(t2, t1):
+			return false
+		}
+	}
+	return sorter[k](t1, t2)
+}
+
+func (tes Entries) Sort() {
+	sort.Sort(tes)
+}
+
+var defaultConcurrency = runtime.NumCPU()
+
+type commitInfo struct {
+	entryName string
+	infos     []interface{}
+	err       error
+}
+
+// GetCommitsInfo takes advantages of concurrency to speed up getting information
+// of all commits that are corresponding to these entries. This method will automatically
+// choose the right number of goroutine (concurrency) to use related of the host CPU.
+func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interface{}, error) {
+	return tes.GetCommitsInfoWithCustomConcurrency(commit, treePath, 0)
+}
+
+// GetCommitsInfoWithCustomConcurrency takes advantages of concurrency to speed up getting information
+// of all commits that are corresponding to these entries. If the given maxConcurrency is negative or
+// equal to zero:  the right number of goroutine (concurrency) to use will be choosen related of the
+// host CPU.
+func (tes Entries) GetCommitsInfoWithCustomConcurrency(commit *Commit, treePath string, maxConcurrency int) ([][]interface{}, error) {
+	if len(tes) == 0 {
+		return nil, nil
+	}
+
+	if maxConcurrency <= 0 {
+		maxConcurrency = defaultConcurrency
+	}
+
+	// Length of taskChan determines how many goroutines (subprocesses) can run at the same time.
+	// The length of revChan should be same as taskChan so goroutines whoever finished job can
+	// exit as early as possible, only store data inside channel.
+	taskChan := make(chan bool, maxConcurrency)
+	revChan := make(chan commitInfo, maxConcurrency)
+	doneChan := make(chan error)
+
+	// Receive loop will exit when it collects same number of data pieces as tree entries.
+	// It notifies doneChan before exits or notify early with possible error.
+	infoMap := make(map[string][]interface{}, len(tes))
+	go func() {
+		i := 0
+		for info := range revChan {
+			if info.err != nil {
+				doneChan <- info.err
+				return
+			}
+
+			infoMap[info.entryName] = info.infos
+			i++
+			if i == len(tes) {
+				break
+			}
+		}
+		doneChan <- nil
+	}()
+
+	for i := range tes {
+		// When taskChan is idle (or has empty slots), put operation will not block.
+		// However when taskChan is full, code will block and wait any running goroutines to finish.
+		taskChan <- true
+
+		if tes[i].Type != OBJECT_COMMIT {
+			go func(i int) {
+				cinfo := commitInfo{entryName: tes[i].Name()}
+				c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
+				if err != nil {
+					cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
+				} else {
+					cinfo.infos = []interface{}{tes[i], c}
+				}
+				revChan <- cinfo
+				<-taskChan // Clear one slot from taskChan to allow new goroutines to start.
+			}(i)
+			continue
+		}
+
+		// Handle submodule
+		go func(i int) {
+			cinfo := commitInfo{entryName: tes[i].Name()}
+			sm, err := commit.GetSubModule(path.Join(treePath, tes[i].Name()))
+			if err != nil && !IsErrNotExist(err) {
+				cinfo.err = fmt.Errorf("GetSubModule (%s/%s): %v", treePath, tes[i].Name(), err)
+				revChan <- cinfo
+				return
+			}
+
+			smURL := ""
+			if sm != nil {
+				smURL = sm.URL
+			}
+
+			c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
+			if err != nil {
+				cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
+			} else {
+				cinfo.infos = []interface{}{tes[i], NewSubModuleFile(c, smURL, tes[i].ID.String())}
+			}
+			revChan <- cinfo
+			<-taskChan
+		}(i)
+	}
+
+	if err := <-doneChan; err != nil {
+		return nil, err
+	}
+
+	commitsInfo := make([][]interface{}, len(tes))
+	for i := 0; i < len(tes); i++ {
+		commitsInfo[i] = infoMap[tes[i].Name()]
+	}
+	return commitsInfo, nil
+}

+ 93 - 0
vendor/github.com/G-Node/git-module/utils.go

@@ -0,0 +1,93 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+)
+
+// objectCache provides thread-safe cache opeations.
+type objectCache struct {
+	lock  sync.RWMutex
+	cache map[string]interface{}
+}
+
+func newObjectCache() *objectCache {
+	return &objectCache{
+		cache: make(map[string]interface{}, 10),
+	}
+}
+
+func (oc *objectCache) Set(id string, obj interface{}) {
+	oc.lock.Lock()
+	defer oc.lock.Unlock()
+
+	oc.cache[id] = obj
+}
+
+func (oc *objectCache) Get(id string) (interface{}, bool) {
+	oc.lock.RLock()
+	defer oc.lock.RUnlock()
+
+	obj, has := oc.cache[id]
+	return obj, has
+}
+
+// isDir returns true if given path is a directory,
+// or returns false when it's a file or does not exist.
+func isDir(dir string) bool {
+	f, e := os.Stat(dir)
+	if e != nil {
+		return false
+	}
+	return f.IsDir()
+}
+
+// isFile returns true if given path is a file,
+// or returns false when it's a directory or does not exist.
+func isFile(filePath string) bool {
+	f, e := os.Stat(filePath)
+	if e != nil {
+		return false
+	}
+	return !f.IsDir()
+}
+
+// isExist checks whether a file or directory exists.
+// It returns false when the file or directory does not exist.
+func isExist(path string) bool {
+	_, err := os.Stat(path)
+	return err == nil || os.IsExist(err)
+}
+
+func concatenateError(err error, stderr string) error {
+	if len(stderr) == 0 {
+		return err
+	}
+	return fmt.Errorf("%v - %s", err, stderr)
+}
+
+// If the object is stored in its own file (i.e not in a pack file),
+// this function returns the full path to the object file.
+// It does not test if the file exists.
+func filepathFromSHA1(rootdir, sha1 string) string {
+	return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
+}
+
+func RefEndName(refStr string) string {
+	if strings.HasPrefix(refStr, BRANCH_PREFIX) {
+		return refStr[len(BRANCH_PREFIX):]
+	}
+
+	if strings.HasPrefix(refStr, TAG_PREFIX) {
+		return refStr[len(TAG_PREFIX):]
+	}
+
+	return refStr
+}

+ 29 - 0
vendor/github.com/G-Node/go-annex/LICENSE

@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2017, German Neuroinformatics Node
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 50 - 0
vendor/github.com/G-Node/go-annex/add.go

@@ -0,0 +1,50 @@
+package gannex
+
+import (
+	"github.com/G-Node/git-module"
+)
+
+const (
+	BYTE     = 1.0
+	KILOBYTE = 1024 * BYTE
+	MEGABYTE = 1024 * KILOBYTE
+	GIGABYTE = 1024 * MEGABYTE
+	TERABYTE = 1024 * GIGABYTE
+)
+
+type ACommand struct {
+	git.Command
+	name string
+	args []string
+	env  []string
+}
+
+func AInit(dir string, args ...string) (string, error) {
+	cmd := git.NewACommand("init")
+	return cmd.AddArguments(args...).RunInDir(dir)
+}
+
+func AUInit(dir string, args ...string) (string, error) {
+	cmd := git.NewACommand("uninit")
+	return cmd.AddArguments(args...).RunInDir(dir)
+}
+
+func Worm(dir string) (string, error) {
+	cmd := git.NewCommand("config", "annex.backends", "WORM")
+	return cmd.RunInDir(dir)
+}
+
+func Md5(dir string) (string, error) {
+	cmd := git.NewCommand("config", "annex.backends", "MD5")
+	return cmd.RunInDir(dir)
+}
+
+func ASync(dir string, args ...string) (string, error) {
+	cmd := git.NewACommand("sync")
+	return cmd.AddArguments(args...).RunInDir(dir)
+}
+
+func Add(filename, dir string) (string, error) {
+	cmd := git.NewACommand("add", filename)
+	return cmd.RunInDir(dir)
+}

+ 75 - 0
vendor/github.com/G-Node/go-annex/file.go

@@ -0,0 +1,75 @@
+package gannex
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+	"io"
+	"fmt"
+	"regexp"
+)
+
+var (
+	secPar    = regexp.MustCompile(`(\.\.)`)
+	aFPattern = regexp.MustCompile(`[\\\/]annex[\\\/](.+)`)
+)
+
+
+type AFile struct {
+	Filepath  string
+	OFilename string
+	Info      os.FileInfo
+}
+
+type AnnexFileNotFound struct {
+	error
+}
+
+func NewAFile(annexpath, repopath, Ofilename string, APFileC []byte) (*AFile, error) {
+	nAF := &AFile{OFilename: Ofilename}
+	matches := aFPattern.FindStringSubmatch(string(APFileC))
+	if matches != nil && len(matches) > 1 {
+		filepath := strings.Replace(matches[1], "\\", "/", 0)
+		filepath = fmt.Sprintf("%s/annex/%s", repopath, filepath)
+		if !secPar.MatchString(filepath) {
+			info, err := os.Stat(filepath)
+			if err == nil {
+				nAF.Filepath = filepath
+				nAF.Info = info
+				return nAF, nil
+			}
+		}
+	}
+	pathParts := strings.SplitAfter(string(APFileC), string(os.PathSeparator))
+	filename := strings.TrimSpace(pathParts[len(pathParts)-1])
+	// lets find the annex file
+	filepath.Walk(filepath.Join(annexpath, repopath), func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return filepath.SkipDir
+		}
+		if info.IsDir() {
+			return nil
+		} else if info.Name() == filename {
+			nAF.Filepath = path
+			nAF.Info = info
+			return io.EOF
+		}
+		return nil
+	})
+	if nAF.Filepath != "" {
+		return nAF, nil
+	} else {
+		return nil, AnnexFileNotFound{error: fmt.Errorf("Could not find File: %s anywhere below: %s", filename,
+			filepath.Join(annexpath, repopath))}
+	}
+
+}
+
+func (af *AFile) Open() (*os.File, error) {
+	fp, err := os.Open(af.Filepath)
+	if err != nil {
+		return nil, err
+	}
+	return fp, nil
+
+}

+ 5 - 0
vendor/github.com/G-Node/go-annex/util.go

@@ -0,0 +1,5 @@
+package gannex
+
+func isAnnexed(dir string) (bool, error) {
+	return false, nil
+}

+ 29 - 0
vendor/github.com/G-Node/libgin/LICENSE

@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2018, German Neuroinformatics Node
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 87 - 0
vendor/github.com/G-Node/libgin/libgin/archive.go

@@ -0,0 +1,87 @@
+package libgin
+
+import (
+	"archive/zip"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// MakeZip creates a zip archive called dest from the files specified by source.
+// Any directories listed in source are archived recursively.
+func MakeZip(dest io.Writer, source ...string) error {
+	// check sources
+	for _, src := range source {
+		if _, err := os.Stat(src); err != nil {
+			return fmt.Errorf("Cannot access '%s': %s", src, err.Error())
+		}
+	}
+
+	zipwriter := zip.NewWriter(dest)
+	defer zipwriter.Close()
+
+	walker := func(filepath string, fi os.FileInfo, err error) error {
+
+		// return on any error
+		if err != nil {
+			return err
+		}
+
+		// create a new dir/file header
+		header, err := zip.FileInfoHeader(fi)
+		if err != nil {
+			return err
+		}
+
+		// update the name to correctly reflect the desired destination when unzipping
+		// header.Name = strings.TrimPrefix(strings.Replace(file, src, "", -1), string(filepath.Separator))
+		header.Name = filepath
+
+		if fi.Mode().IsDir() {
+			return nil
+		}
+
+		// write the header
+		w, err := zipwriter.CreateHeader(header)
+		if err != nil {
+			return err
+		}
+
+		// Dereference symlinks
+		if fi.Mode()&os.ModeSymlink != 0 {
+			data, err := os.Readlink(filepath)
+			if err != nil {
+				return err
+			}
+			if _, err := io.Copy(w, strings.NewReader(data)); err != nil {
+				return err
+			}
+			return nil
+		}
+
+		// open files for zipping
+		f, err := os.Open(filepath)
+		defer f.Close()
+		if err != nil {
+			return err
+		}
+
+		// copy file data into zip writer
+		if _, err := io.Copy(w, f); err != nil {
+			return err
+		}
+
+		return nil
+	}
+
+	// walk path
+	for _, src := range source {
+		err := filepath.Walk(src, walker)
+		if err != nil {
+			return fmt.Errorf("Error adding %s to zip file: %s", src, err.Error())
+		}
+	}
+	return nil
+}

+ 62 - 0
vendor/github.com/G-Node/libgin/libgin/dex.go

@@ -0,0 +1,62 @@
+package libgin
+
+import (
+	"time"
+
+	"github.com/G-Node/gig"
+)
+
+// NOTE: TEMPORARY COPY FROM gin-dex
+
+type SearchRequest struct {
+	Token  string
+	CsrfT  string
+	UserID int64
+	Querry string
+	SType  int64
+}
+
+const (
+	SEARCH_MATCH = iota
+	SEARCH_FUZZY
+	SEARCH_WILDCARD
+	SEARCH_QUERRY
+	SEARCH_SUGGEST
+)
+
+type BlobSResult struct {
+	Source    *IndexBlob  `json:"_source"`
+	Score     float64     `json:"_score"`
+	Highlight interface{} `json:"highlight"`
+}
+
+type CommitSResult struct {
+	Source    *IndexCommit `json:"_source"`
+	Score     float64      `json:"_score"`
+	Highlight interface{}  `json:"highlight"`
+}
+
+type SearchResults struct {
+	Blobs   []BlobSResult
+	Commits []CommitSResult
+}
+
+type IndexBlob struct {
+	*gig.Blob
+	GinRepoName  string
+	GinRepoId    string
+	FirstCommit  string
+	Id           int64
+	Oid          gig.SHA1
+	IndexingTime time.Time
+	Content      string
+	Path         string
+}
+
+type IndexCommit struct {
+	*gig.Commit
+	GinRepoId    string
+	Oid          gig.SHA1
+	GinRepoName  string
+	IndexingTime time.Time
+}

+ 134 - 0
vendor/github.com/G-Node/libgin/libgin/doi.go

@@ -0,0 +1,134 @@
+package libgin
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/hex"
+	"encoding/xml"
+	"fmt"
+	"log"
+	"net/http"
+	"regexp"
+	"strings"
+	"time"
+)
+
+// NOTE: TEMPORARY COPIES FROM gin-doi
+
+// uuidMap is a map between registered repositories and their UUIDs for datasets registered before the new UUID generation method was implemented.
+// This map is required because the current method of computing UUIDs differs from the older method and this lookup is used to handle the old-method UUIDs.
+var uuidMap = map[string]string{
+	"INT/multielectrode_grasp":                   "f83565d148510fede8a277f660e1a419",
+	"ajkumaraswamy/HB-PAC_disinhibitory_network": "1090f803258557299d287c4d44a541b2",
+	"steffi/Kleineidam_et_al_2017":               "f53069de4c4921a3cfa8f17d55ef98bb",
+	"Churan/Morris_et_al_Frontiers_2016":         "97bc1456d3f4bca2d945357b3ec92029",
+	"fabee/efish_locking":                        "6953bbf0087ba444b2d549b759de4a06",
+}
+
+// RepoPathToUUID computes a UUID from a repository path.
+func RepoPathToUUID(URI string) string {
+	if doi, ok := uuidMap[URI]; ok {
+		return doi
+	}
+	currMd5 := md5.Sum([]byte(URI))
+	return hex.EncodeToString(currMd5[:])
+}
+
+// DOIRegInfo holds all the metadata and information necessary for a DOI registration request.
+type DOIRegInfo struct {
+	Missing     []string
+	DOI         string
+	UUID        string
+	FileSize    int64
+	Title       string
+	Authors     []Author
+	Description string
+	Keywords    []string
+	References  []Reference
+	Funding     []string
+	License     *License
+	DType       string
+}
+
+func (c *DOIRegInfo) GetType() string {
+	if c.DType != "" {
+		return c.DType
+	}
+	return "Dataset"
+}
+
+func (c *DOIRegInfo) GetCitation() string {
+	var authors string
+	for _, auth := range c.Authors {
+		if len(auth.FirstName) > 0 {
+			authors += fmt.Sprintf("%s %s, ", auth.LastName, string(auth.FirstName[0]))
+		} else {
+			authors += fmt.Sprintf("%s, ", auth.LastName)
+		}
+	}
+	return fmt.Sprintf("%s (%d) %s. G-Node. doi:%s", authors, time.Now().Year(), c.Title, c.DOI)
+}
+
+func (c *DOIRegInfo) EscXML(txt string) string {
+	buf := new(bytes.Buffer)
+	if err := xml.EscapeText(buf, []byte(txt)); err != nil {
+		log.Printf("Could not escape: %s :: %s", txt, err.Error())
+		return ""
+	}
+	return buf.String()
+
+}
+
+type Author struct {
+	FirstName   string
+	LastName    string
+	Affiliation string
+	ID          string
+}
+
+func (c *Author) GetValidID() *NamedIdentifier {
+	if c.ID == "" {
+		return nil
+	}
+	if strings.Contains(strings.ToLower(c.ID), "orcid") {
+		// assume the orcid id is a four block number thing eg. 0000-0002-5947-9939
+		var re = regexp.MustCompile(`(\d+-\d+-\d+-\d+)`)
+		nid := string(re.Find([]byte(c.ID)))
+		return &NamedIdentifier{URI: "https://orcid.org/", Scheme: "ORCID", ID: nid}
+	}
+	return nil
+}
+func (a *Author) RenderAuthor() string {
+	auth := fmt.Sprintf("%s,%s;%s;%s", a.LastName, a.FirstName, a.Affiliation, a.ID)
+	return strings.TrimRight(auth, ";")
+}
+
+type NamedIdentifier struct {
+	URI    string
+	Scheme string
+	ID     string
+}
+
+type Reference struct {
+	Reftype string
+	Name    string
+	DOI     string
+}
+
+type License struct {
+	Name string
+	URL  string
+}
+
+func IsRegisteredDOI(doi string) bool {
+	url := fmt.Sprintf("https://doi.org/%s", doi)
+	resp, err := http.Get(url)
+	if err != nil {
+		log.Printf("Could not query for DOI: %s at %s", doi, url)
+		return false
+	}
+	if resp.StatusCode != http.StatusNotFound {
+		return true
+	}
+	return false
+}

+ 8 - 0
vendor/github.com/G-Node/libgin/libgin/libgin.go

@@ -0,0 +1,8 @@
+/*
+Package libgin provides functionality shared between the G-Node GIN services.
+These include:
+- GIN Web: The G-Node/GOGS fork (https://github.com/G-Node/gogs)
+- GIN DOI: The DOI registration service (https://github.com/G-Node/gin-doi)
+- GIN DEX: The indexing service for GIN (https://github.com/G-Node/gin-dex)
+*/
+package libgin

+ 123 - 0
vendor/github.com/Sirupsen/logrus/CHANGELOG.md

@@ -0,0 +1,123 @@
+# 1.0.5
+
+* Fix hooks race (#707)
+* Fix panic deadlock (#695)
+
+# 1.0.4
+
+* Fix race when adding hooks (#612)
+* Fix terminal check in AppEngine (#635)
+
+# 1.0.3
+
+* Replace example files with testable examples
+
+# 1.0.2
+
+* bug: quote non-string values in text formatter (#583)
+* Make (*Logger) SetLevel a public method
+
+# 1.0.1
+
+* bug: fix escaping in text formatter (#575)
+
+# 1.0.0
+
+* Officially changed name to lower-case
+* bug: colors on Windows 10 (#541)
+* bug: fix race in accessing level (#512)
+
+# 0.11.5
+
+* feature: add writer and writerlevel to entry (#372)
+
+# 0.11.4
+
+* bug: fix undefined variable on solaris (#493)
+
+# 0.11.3
+
+* formatter: configure quoting of empty values (#484)
+* formatter: configure quoting character (default is `"`) (#484)
+* bug: fix not importing io correctly in non-linux environments (#481)
+
+# 0.11.2
+
+* bug: fix windows terminal detection (#476)
+
+# 0.11.1
+
+* bug: fix tty detection with custom out (#471)
+
+# 0.11.0
+
+* performance: Use bufferpool to allocate (#370)
+* terminal: terminal detection for app-engine (#343)
+* feature: exit handler (#375)
+
+# 0.10.0
+
+* feature: Add a test hook (#180)
+* feature: `ParseLevel` is now case-insensitive (#326)
+* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
+* performance: avoid re-allocations on `WithFields` (#335)
+
+# 0.9.0
+
+* logrus/text_formatter: don't emit empty msg
+* logrus/hooks/airbrake: move out of main repository
+* logrus/hooks/sentry: move out of main repository
+* logrus/hooks/papertrail: move out of main repository
+* logrus/hooks/bugsnag: move out of main repository
+* logrus/core: run tests with `-race`
+* logrus/core: detect TTY based on `stderr`
+* logrus/core: support `WithError` on logger
+* logrus/core: Solaris support
+
+# 0.8.7
+
+* logrus/core: fix possible race (#216)
+* logrus/doc: small typo fixes and doc improvements
+
+
+# 0.8.6
+
+* hooks/raven: allow passing an initialized client
+
+# 0.8.5
+
+* logrus/core: revert #208
+
+# 0.8.4
+
+* formatter/text: fix data race (#218)
+
+# 0.8.3
+
+* logrus/core: fix entry log level (#208)
+* logrus/core: improve performance of text formatter by 40%
+* logrus/core: expose `LevelHooks` type
+* logrus/core: add support for DragonflyBSD and NetBSD
+* formatter/text: print structs more verbosely
+
+# 0.8.2
+
+* logrus: fix more Fatal family functions
+
+# 0.8.1
+
+* logrus: fix not exiting on `Fatalf` and `Fatalln`
+
+# 0.8.0
+
+* logrus: defaults to stderr instead of stdout
+* hooks/sentry: add special field for `*http.Request`
+* formatter/text: ignore Windows for colors
+
+# 0.7.3
+
+* formatter/\*: allow configuration of timestamp layout
+
+# 0.7.2
+
+* formatter/text: Add configuration option for time format (#158)

+ 21 - 0
vendor/github.com/Sirupsen/logrus/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Simon Eskildsen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 461 - 0
vendor/github.com/Sirupsen/logrus/README.md

@@ -0,0 +1,461 @@
+# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus)&nbsp;[![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus)
+
+Logrus is a structured logger for Go (golang), completely API compatible with
+the standard library logger.
+
+**Seeing weird case-sensitive problems?** It's in the past been possible to
+import Logrus as both upper- and lower-case. Due to the Go package environment,
+this caused issues in the community and we needed a standard. Some environments
+experienced problems with the upper-case variant, so the lower-case was decided.
+Everything using `logrus` will need to use the lower-case:
+`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
+
+To fix Glide, see [these
+comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
+For an in-depth explanation of the casing issue, see [this
+comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
+
+**Are you interested in assisting in maintaining Logrus?** Currently I have a
+lot of obligations, and I am unable to provide Logrus with the maintainership it
+needs. If you'd like to help, please reach out to me at `simon at author's
+username dot com`.
+
+Nicely color-coded in development (when a TTY is attached, otherwise just
+plain text):
+
+![Colored](http://i.imgur.com/PY7qMwd.png)
+
+With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
+or Splunk:
+
+```json
+{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
+ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
+
+{"level":"warning","msg":"The group's number increased tremendously!",
+"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
+
+{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
+"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
+
+{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
+"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
+
+{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
+"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
+```
+
+With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
+attached, the output is compatible with the
+[logfmt](http://godoc.org/github.com/kr/logfmt) format:
+
+```text
+time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
+time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
+time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
+time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
+time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
+time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
+exit status 1
+```
+
+#### Case-sensitivity
+
+The organization's name was changed to lower-case--and this will not be changed
+back. If you are getting import conflicts due to case sensitivity, please use
+the lower-case import: `github.com/sirupsen/logrus`.
+
+#### Example
+
+The simplest way to use Logrus is simply the package-level exported logger:
+
+```go
+package main
+
+import (
+  log "github.com/sirupsen/logrus"
+)
+
+func main() {
+  log.WithFields(log.Fields{
+    "animal": "walrus",
+  }).Info("A walrus appears")
+}
+```
+
+Note that it's completely api-compatible with the stdlib logger, so you can
+replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
+and you'll now have the flexibility of Logrus. You can customize it all you
+want:
+
+```go
+package main
+
+import (
+  "os"
+  log "github.com/sirupsen/logrus"
+)
+
+func init() {
+  // Log as JSON instead of the default ASCII formatter.
+  log.SetFormatter(&log.JSONFormatter{})
+
+  // Output to stdout instead of the default stderr
+  // Can be any io.Writer, see below for File example
+  log.SetOutput(os.Stdout)
+
+  // Only log the warning severity or above.
+  log.SetLevel(log.WarnLevel)
+}
+
+func main() {
+  log.WithFields(log.Fields{
+    "animal": "walrus",
+    "size":   10,
+  }).Info("A group of walrus emerges from the ocean")
+
+  log.WithFields(log.Fields{
+    "omg":    true,
+    "number": 122,
+  }).Warn("The group's number increased tremendously!")
+
+  log.WithFields(log.Fields{
+    "omg":    true,
+    "number": 100,
+  }).Fatal("The ice breaks!")
+
+  // A common pattern is to re-use fields between logging statements by re-using
+  // the logrus.Entry returned from WithFields()
+  contextLogger := log.WithFields(log.Fields{
+    "common": "this is a common field",
+    "other": "I also should be logged always",
+  })
+
+  contextLogger.Info("I'll be logged with common and other field")
+  contextLogger.Info("Me too")
+}
+```
+
+For more advanced usage such as logging to multiple locations from the same
+application, you can also create an instance of the `logrus` Logger:
+
+```go
+package main
+
+import (
+  "os"
+  "github.com/sirupsen/logrus"
+)
+
+// Create a new instance of the logger. You can have any number of instances.
+var log = logrus.New()
+
+func main() {
+  // The API for setting attributes is a little different than the package level
+  // exported logger. See Godoc.
+  log.Out = os.Stdout
+
+  // You could set this to any `io.Writer` such as a file
+  // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
+  // if err == nil {
+  //  log.Out = file
+  // } else {
+  //  log.Info("Failed to log to file, using default stderr")
+  // }
+
+  log.WithFields(logrus.Fields{
+    "animal": "walrus",
+    "size":   10,
+  }).Info("A group of walrus emerges from the ocean")
+}
+```
+
+#### Fields
+
+Logrus encourages careful, structured logging through logging fields instead of
+long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
+to send event %s to topic %s with key %d")`, you should log the much more
+discoverable:
+
+```go
+log.WithFields(log.Fields{
+  "event": event,
+  "topic": topic,
+  "key": key,
+}).Fatal("Failed to send event")
+```
+
+We've found this API forces you to think about logging in a way that produces
+much more useful logging messages. We've been in countless situations where just
+a single added field to a log statement that was already there would've saved us
+hours. The `WithFields` call is optional.
+
+In general, with Logrus using any of the `printf`-family functions should be
+seen as a hint you should add a field, however, you can still use the
+`printf`-family functions with Logrus.
+
+#### Default Fields
+
+Often it's helpful to have fields _always_ attached to log statements in an
+application or parts of one. For example, you may want to always log the
+`request_id` and `user_ip` in the context of a request. Instead of writing
+`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
+every line, you can create a `logrus.Entry` to pass around instead:
+
+```go
+requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
+requestLogger.Info("something happened on that request") # will log request_id and user_ip
+requestLogger.Warn("something not great happened")
+```
+
+#### Hooks
+
+You can add hooks for logging levels. For example to send errors to an exception
+tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
+multiple places simultaneously, e.g. syslog.
+
+Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
+`init`:
+
+```go
+import (
+  log "github.com/sirupsen/logrus"
+  "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
+  logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
+  "log/syslog"
+)
+
+func init() {
+
+  // Use the Airbrake hook to report errors that have Error severity or above to
+  // an exception tracker. You can create custom hooks, see the Hooks section.
+  log.AddHook(airbrake.NewHook(123, "xyz", "production"))
+
+  hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+  if err != nil {
+    log.Error("Unable to connect to local syslog daemon")
+  } else {
+    log.AddHook(hook)
+  }
+}
+```
+Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
+
+A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
+
+
+#### Level logging
+
+Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
+
+```go
+log.Debug("Useful debugging information.")
+log.Info("Something noteworthy happened!")
+log.Warn("You should probably take a look at this.")
+log.Error("Something failed but I'm not quitting.")
+// Calls os.Exit(1) after logging
+log.Fatal("Bye.")
+// Calls panic() after logging
+log.Panic("I'm bailing.")
+```
+
+You can set the logging level on a `Logger`, then it will only log entries with
+that severity or anything above it:
+
+```go
+// Will log anything that is info or above (warn, error, fatal, panic). Default.
+log.SetLevel(log.InfoLevel)
+```
+
+It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
+environment if your application has that.
+
+#### Entries
+
+Besides the fields added with `WithField` or `WithFields` some fields are
+automatically added to all logging events:
+
+1. `time`. The timestamp when the entry was created.
+2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
+   the `AddFields` call. E.g. `Failed to send event.`
+3. `level`. The logging level. E.g. `info`.
+
+#### Environments
+
+Logrus has no notion of environment.
+
+If you wish for hooks and formatters to only be used in specific environments,
+you should handle that yourself. For example, if your application has a global
+variable `Environment`, which is a string representation of the environment you
+could do:
+
+```go
+import (
+  log "github.com/sirupsen/logrus"
+)
+
+init() {
+  // do something here to set environment depending on an environment variable
+  // or command-line flag
+  if Environment == "production" {
+    log.SetFormatter(&log.JSONFormatter{})
+  } else {
+    // The TextFormatter is default, you don't actually have to do this.
+    log.SetFormatter(&log.TextFormatter{})
+  }
+}
+```
+
+This configuration is how `logrus` was intended to be used, but JSON in
+production is mostly only useful if you do log aggregation with tools like
+Splunk or Logstash.
+
+#### Formatters
+
+The built-in logging formatters are:
+
+* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
+  without colors.
+  * *Note:* to force colored output when there is no TTY, set the `ForceColors`
+    field to `true`.  To force no colored output even if there is a TTY  set the
+    `DisableColors` field to `true`. For Windows, see
+    [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
+  * When colors are enabled, levels are truncated to 4 characters by default. To disable
+    truncation set the `DisableLevelTruncation` field to `true`.
+  * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
+* `logrus.JSONFormatter`. Logs fields as JSON.
+  * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
+
+Third party logging formatters:
+
+* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
+* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
+* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
+* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
+
+You can define your formatter by implementing the `Formatter` interface,
+requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
+`Fields` type (`map[string]interface{}`) with all your fields as well as the
+default ones (see Entries section above):
+
+```go
+type MyJSONFormatter struct {
+}
+
+log.SetFormatter(new(MyJSONFormatter))
+
+func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
+  // Note this doesn't include Time, Level and Message which are available on
+  // the Entry. Consult `godoc` on information about those fields or read the
+  // source of the official loggers.
+  serialized, err := json.Marshal(entry.Data)
+    if err != nil {
+      return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+    }
+  return append(serialized, '\n'), nil
+}
+```
+
+#### Logger as an `io.Writer`
+
+Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
+
+```go
+w := logger.Writer()
+defer w.Close()
+
+srv := http.Server{
+    // create a stdlib log.Logger that writes to
+    // logrus.Logger.
+    ErrorLog: log.New(w, "", 0),
+}
+```
+
+Each line written to that writer will be printed the usual way, using formatters
+and hooks. The level for those entries is `info`.
+
+This means that we can override the standard library logger easily:
+
+```go
+logger := logrus.New()
+logger.Formatter = &logrus.JSONFormatter{}
+
+// Use logrus for standard log output
+// Note that `log` here references stdlib's log
+// Not logrus imported under the name `log`.
+log.SetOutput(logger.Writer())
+```
+
+#### Rotation
+
+Log rotation is not provided with Logrus. Log rotation should be done by an
+external program (like `logrotate(8)`) that can compress and delete old log
+entries. It should not be a feature of the application-level logger.
+
+#### Tools
+
+| Tool | Description |
+| ---- | ----------- |
+|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
+|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
+
+#### Testing
+
+Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
+
+* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
+* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
+
+```go
+import(
+  "github.com/sirupsen/logrus"
+  "github.com/sirupsen/logrus/hooks/test"
+  "github.com/stretchr/testify/assert"
+  "testing"
+)
+
+func TestSomething(t*testing.T){
+  logger, hook := test.NewNullLogger()
+  logger.Error("Helloerror")
+
+  assert.Equal(t, 1, len(hook.Entries))
+  assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
+  assert.Equal(t, "Helloerror", hook.LastEntry().Message)
+
+  hook.Reset()
+  assert.Nil(t, hook.LastEntry())
+}
+```
+
+#### Fatal handlers
+
+Logrus can register one or more functions that will be called when any `fatal`
+level message is logged. The registered handlers will be executed before
+logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
+to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
+
+```
+...
+handler := func() {
+  // gracefully shutdown something...
+}
+logrus.RegisterExitHandler(handler)
+...
+```
+
+#### Thread safety
+
+By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
+If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
+
+Situation when locking is not needed includes:
+
+* You have no hooks registered, or hooks calling is already thread-safe.
+
+* Writing to logger.Out is already thread-safe, for example:
+
+  1) logger.Out is protected by locks.
+
+  2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
+
+     (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)

+ 64 - 0
vendor/github.com/Sirupsen/logrus/alt_exit.go

@@ -0,0 +1,64 @@
+package logrus
+
+// The following code was sourced and modified from the
+// https://github.com/tebeka/atexit package governed by the following license:
+//
+// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import (
+	"fmt"
+	"os"
+)
+
+var handlers = []func(){}
+
+func runHandler(handler func()) {
+	defer func() {
+		if err := recover(); err != nil {
+			fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
+		}
+	}()
+
+	handler()
+}
+
+func runHandlers() {
+	for _, handler := range handlers {
+		runHandler(handler)
+	}
+}
+
+// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
+func Exit(code int) {
+	runHandlers()
+	os.Exit(code)
+}
+
+// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
+// all handlers. The handlers will also be invoked when any Fatal log entry is
+// made.
+//
+// This method is useful when a caller wishes to use logrus to log a fatal
+// message but also needs to gracefully shutdown. An example usecase could be
+// closing database connections, or sending a alert that the application is
+// closing.
+func RegisterExitHandler(handler func()) {
+	handlers = append(handlers, handler)
+}

+ 14 - 0
vendor/github.com/Sirupsen/logrus/appveyor.yml

@@ -0,0 +1,14 @@
+version: "{build}"
+platform: x64
+clone_folder: c:\gopath\src\github.com\sirupsen\logrus
+environment:  
+  GOPATH: c:\gopath
+branches:  
+  only:
+    - master
+install:  
+  - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
+  - go version
+build_script:  
+  - go get -t
+  - go test

+ 26 - 0
vendor/github.com/Sirupsen/logrus/doc.go

@@ -0,0 +1,26 @@
+/*
+Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
+
+
+The simplest way to use Logrus is simply the package-level exported logger:
+
+  package main
+
+  import (
+    log "github.com/sirupsen/logrus"
+  )
+
+  func main() {
+    log.WithFields(log.Fields{
+      "animal": "walrus",
+      "number": 1,
+      "size":   10,
+    }).Info("A walrus appears")
+  }
+
+Output:
+  time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
+
+For a full guide visit https://github.com/sirupsen/logrus
+*/
+package logrus

+ 288 - 0
vendor/github.com/Sirupsen/logrus/entry.go

@@ -0,0 +1,288 @@
+package logrus
+
+import (
+	"bytes"
+	"fmt"
+	"os"
+	"sync"
+	"time"
+)
+
+var bufferPool *sync.Pool
+
+func init() {
+	bufferPool = &sync.Pool{
+		New: func() interface{} {
+			return new(bytes.Buffer)
+		},
+	}
+}
+
+// Defines the key when adding errors using WithError.
+var ErrorKey = "error"
+
+// An entry is the final or intermediate Logrus logging entry. It contains all
+// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
+// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
+// passed around as much as you wish to avoid field duplication.
+type Entry struct {
+	Logger *Logger
+
+	// Contains all the fields set by the user.
+	Data Fields
+
+	// Time at which the log entry was created
+	Time time.Time
+
+	// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
+	// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
+	Level Level
+
+	// Message passed to Debug, Info, Warn, Error, Fatal or Panic
+	Message string
+
+	// When formatter is called in entry.log(), an Buffer may be set to entry
+	Buffer *bytes.Buffer
+}
+
+func NewEntry(logger *Logger) *Entry {
+	return &Entry{
+		Logger: logger,
+		// Default is five fields, give a little extra room
+		Data: make(Fields, 5),
+	}
+}
+
+// Returns the string representation from the reader and ultimately the
+// formatter.
+func (entry *Entry) String() (string, error) {
+	serialized, err := entry.Logger.Formatter.Format(entry)
+	if err != nil {
+		return "", err
+	}
+	str := string(serialized)
+	return str, nil
+}
+
+// Add an error as single field (using the key defined in ErrorKey) to the Entry.
+func (entry *Entry) WithError(err error) *Entry {
+	return entry.WithField(ErrorKey, err)
+}
+
+// Add a single field to the Entry.
+func (entry *Entry) WithField(key string, value interface{}) *Entry {
+	return entry.WithFields(Fields{key: value})
+}
+
+// Add a map of fields to the Entry.
+func (entry *Entry) WithFields(fields Fields) *Entry {
+	data := make(Fields, len(entry.Data)+len(fields))
+	for k, v := range entry.Data {
+		data[k] = v
+	}
+	for k, v := range fields {
+		data[k] = v
+	}
+	return &Entry{Logger: entry.Logger, Data: data}
+}
+
+// This function is not declared with a pointer value because otherwise
+// race conditions will occur when using multiple goroutines
+func (entry Entry) log(level Level, msg string) {
+	var buffer *bytes.Buffer
+	entry.Time = time.Now()
+	entry.Level = level
+	entry.Message = msg
+
+	entry.fireHooks()
+
+	buffer = bufferPool.Get().(*bytes.Buffer)
+	buffer.Reset()
+	defer bufferPool.Put(buffer)
+	entry.Buffer = buffer
+
+	entry.write()
+
+	entry.Buffer = nil
+
+	// To avoid Entry#log() returning a value that only would make sense for
+	// panic() to use in Entry#Panic(), we avoid the allocation by checking
+	// directly here.
+	if level <= PanicLevel {
+		panic(&entry)
+	}
+}
+
+// This function is not declared with a pointer value because otherwise
+// race conditions will occur when using multiple goroutines
+func (entry Entry) fireHooks() {
+	entry.Logger.mu.Lock()
+	defer entry.Logger.mu.Unlock()
+	err := entry.Logger.Hooks.Fire(entry.Level, &entry)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
+	}
+}
+
+func (entry *Entry) write() {
+	serialized, err := entry.Logger.Formatter.Format(entry)
+	entry.Logger.mu.Lock()
+	defer entry.Logger.mu.Unlock()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
+	} else {
+		_, err = entry.Logger.Out.Write(serialized)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
+		}
+	}
+}
+
+func (entry *Entry) Debug(args ...interface{}) {
+	if entry.Logger.level() >= DebugLevel {
+		entry.log(DebugLevel, fmt.Sprint(args...))
+	}
+}
+
+func (entry *Entry) Print(args ...interface{}) {
+	entry.Info(args...)
+}
+
+func (entry *Entry) Info(args ...interface{}) {
+	if entry.Logger.level() >= InfoLevel {
+		entry.log(InfoLevel, fmt.Sprint(args...))
+	}
+}
+
+func (entry *Entry) Warn(args ...interface{}) {
+	if entry.Logger.level() >= WarnLevel {
+		entry.log(WarnLevel, fmt.Sprint(args...))
+	}
+}
+
+func (entry *Entry) Warning(args ...interface{}) {
+	entry.Warn(args...)
+}
+
+func (entry *Entry) Error(args ...interface{}) {
+	if entry.Logger.level() >= ErrorLevel {
+		entry.log(ErrorLevel, fmt.Sprint(args...))
+	}
+}
+
+func (entry *Entry) Fatal(args ...interface{}) {
+	if entry.Logger.level() >= FatalLevel {
+		entry.log(FatalLevel, fmt.Sprint(args...))
+	}
+	Exit(1)
+}
+
+func (entry *Entry) Panic(args ...interface{}) {
+	if entry.Logger.level() >= PanicLevel {
+		entry.log(PanicLevel, fmt.Sprint(args...))
+	}
+	panic(fmt.Sprint(args...))
+}
+
+// Entry Printf family functions
+
+func (entry *Entry) Debugf(format string, args ...interface{}) {
+	if entry.Logger.level() >= DebugLevel {
+		entry.Debug(fmt.Sprintf(format, args...))
+	}
+}
+
+func (entry *Entry) Infof(format string, args ...interface{}) {
+	if entry.Logger.level() >= InfoLevel {
+		entry.Info(fmt.Sprintf(format, args...))
+	}
+}
+
+func (entry *Entry) Printf(format string, args ...interface{}) {
+	entry.Infof(format, args...)
+}
+
+func (entry *Entry) Warnf(format string, args ...interface{}) {
+	if entry.Logger.level() >= WarnLevel {
+		entry.Warn(fmt.Sprintf(format, args...))
+	}
+}
+
+func (entry *Entry) Warningf(format string, args ...interface{}) {
+	entry.Warnf(format, args...)
+}
+
+func (entry *Entry) Errorf(format string, args ...interface{}) {
+	if entry.Logger.level() >= ErrorLevel {
+		entry.Error(fmt.Sprintf(format, args...))
+	}
+}
+
+func (entry *Entry) Fatalf(format string, args ...interface{}) {
+	if entry.Logger.level() >= FatalLevel {
+		entry.Fatal(fmt.Sprintf(format, args...))
+	}
+	Exit(1)
+}
+
+func (entry *Entry) Panicf(format string, args ...interface{}) {
+	if entry.Logger.level() >= PanicLevel {
+		entry.Panic(fmt.Sprintf(format, args...))
+	}
+}
+
+// Entry Println family functions
+
+func (entry *Entry) Debugln(args ...interface{}) {
+	if entry.Logger.level() >= DebugLevel {
+		entry.Debug(entry.sprintlnn(args...))
+	}
+}
+
+func (entry *Entry) Infoln(args ...interface{}) {
+	if entry.Logger.level() >= InfoLevel {
+		entry.Info(entry.sprintlnn(args...))
+	}
+}
+
+func (entry *Entry) Println(args ...interface{}) {
+	entry.Infoln(args...)
+}
+
+func (entry *Entry) Warnln(args ...interface{}) {
+	if entry.Logger.level() >= WarnLevel {
+		entry.Warn(entry.sprintlnn(args...))
+	}
+}
+
+func (entry *Entry) Warningln(args ...interface{}) {
+	entry.Warnln(args...)
+}
+
+func (entry *Entry) Errorln(args ...interface{}) {
+	if entry.Logger.level() >= ErrorLevel {
+		entry.Error(entry.sprintlnn(args...))
+	}
+}
+
+func (entry *Entry) Fatalln(args ...interface{}) {
+	if entry.Logger.level() >= FatalLevel {
+		entry.Fatal(entry.sprintlnn(args...))
+	}
+	Exit(1)
+}
+
+func (entry *Entry) Panicln(args ...interface{}) {
+	if entry.Logger.level() >= PanicLevel {
+		entry.Panic(entry.sprintlnn(args...))
+	}
+}
+
+// Sprintlnn => Sprint no newline. This is to get the behavior of how
+// fmt.Sprintln where spaces are always added between operands, regardless of
+// their type. Instead of vendoring the Sprintln implementation to spare a
+// string allocation, we do the simplest thing.
+func (entry *Entry) sprintlnn(args ...interface{}) string {
+	msg := fmt.Sprintln(args...)
+	return msg[:len(msg)-1]
+}

+ 191 - 0
vendor/github.com/Sirupsen/logrus/exported.go

@@ -0,0 +1,191 @@
+package logrus
+
+import (
+	"io"
+)
+
+var (
+	// std is the name of the standard logger in stdlib `log`
+	std = New()
+)
+
+func StandardLogger() *Logger {
+	return std
+}
+
+// SetOutput sets the standard logger output.
+func SetOutput(out io.Writer) {
+	std.SetOutput(out)
+}
+
+// SetFormatter sets the standard logger formatter.
+func SetFormatter(formatter Formatter) {
+	std.mu.Lock()
+	defer std.mu.Unlock()
+	std.Formatter = formatter
+}
+
+// SetLevel sets the standard logger level.
+func SetLevel(level Level) {
+	std.mu.Lock()
+	defer std.mu.Unlock()
+	std.SetLevel(level)
+}
+
+// GetLevel returns the standard logger level.
+func GetLevel() Level {
+	std.mu.Lock()
+	defer std.mu.Unlock()
+	return std.level()
+}
+
+// AddHook adds a hook to the standard logger hooks.
+func AddHook(hook Hook) {
+	std.mu.Lock()
+	defer std.mu.Unlock()
+	std.Hooks.Add(hook)
+}
+
+// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
+func WithError(err error) *Entry {
+	return std.WithField(ErrorKey, err)
+}
+
+// WithField creates an entry from the standard logger and adds a field to
+// it. If you want multiple fields, use `WithFields`.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithField(key string, value interface{}) *Entry {
+	return std.WithField(key, value)
+}
+
+// WithFields creates an entry from the standard logger and adds multiple
+// fields to it. This is simply a helper for `WithField`, invoking it
+// once for each field.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithFields(fields Fields) *Entry {
+	return std.WithFields(fields)
+}
+
+// Debug logs a message at level Debug on the standard logger.
+func Debug(args ...interface{}) {
+	std.Debug(args...)
+}
+
+// Print logs a message at level Info on the standard logger.
+func Print(args ...interface{}) {
+	std.Print(args...)
+}
+
+// Info logs a message at level Info on the standard logger.
+func Info(args ...interface{}) {
+	std.Info(args...)
+}
+
+// Warn logs a message at level Warn on the standard logger.
+func Warn(args ...interface{}) {
+	std.Warn(args...)
+}
+
+// Warning logs a message at level Warn on the standard logger.
+func Warning(args ...interface{}) {
+	std.Warning(args...)
+}
+
+// Error logs a message at level Error on the standard logger.
+func Error(args ...interface{}) {
+	std.Error(args...)
+}
+
+// Panic logs a message at level Panic on the standard logger.
+func Panic(args ...interface{}) {
+	std.Panic(args...)
+}
+
+// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
+func Fatal(args ...interface{}) {
+	std.Fatal(args...)
+}
+
+// Debugf logs a message at level Debug on the standard logger.
+func Debugf(format string, args ...interface{}) {
+	std.Debugf(format, args...)
+}
+
+// Printf logs a message at level Info on the standard logger.
+func Printf(format string, args ...interface{}) {
+	std.Printf(format, args...)
+}
+
+// Infof logs a message at level Info on the standard logger.
+func Infof(format string, args ...interface{}) {
+	std.Infof(format, args...)
+}
+
+// Warnf logs a message at level Warn on the standard logger.
+func Warnf(format string, args ...interface{}) {
+	std.Warnf(format, args...)
+}
+
+// Warningf logs a message at level Warn on the standard logger.
+func Warningf(format string, args ...interface{}) {
+	std.Warningf(format, args...)
+}
+
+// Errorf logs a message at level Error on the standard logger.
+func Errorf(format string, args ...interface{}) {
+	std.Errorf(format, args...)
+}
+
+// Panicf logs a message at level Panic on the standard logger.
+func Panicf(format string, args ...interface{}) {
+	std.Panicf(format, args...)
+}
+
+// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
+func Fatalf(format string, args ...interface{}) {
+	std.Fatalf(format, args...)
+}
+
+// Debugln logs a message at level Debug on the standard logger.
+func Debugln(args ...interface{}) {
+	std.Debugln(args...)
+}
+
+// Println logs a message at level Info on the standard logger.
+func Println(args ...interface{}) {
+	std.Println(args...)
+}
+
+// Infoln logs a message at level Info on the standard logger.
+func Infoln(args ...interface{}) {
+	std.Infoln(args...)
+}
+
+// Warnln logs a message at level Warn on the standard logger.
+func Warnln(args ...interface{}) {
+	std.Warnln(args...)
+}
+
+// Warningln logs a message at level Warn on the standard logger.
+func Warningln(args ...interface{}) {
+	std.Warningln(args...)
+}
+
+// Errorln logs a message at level Error on the standard logger.
+func Errorln(args ...interface{}) {
+	std.Errorln(args...)
+}
+
+// Panicln logs a message at level Panic on the standard logger.
+func Panicln(args ...interface{}) {
+	std.Panicln(args...)
+}
+
+// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
+func Fatalln(args ...interface{}) {
+	std.Fatalln(args...)
+}

+ 51 - 0
vendor/github.com/Sirupsen/logrus/formatter.go

@@ -0,0 +1,51 @@
+package logrus
+
+import "time"
+
+const defaultTimestampFormat = time.RFC3339
+
+// The Formatter interface is used to implement a custom Formatter. It takes an
+// `Entry`. It exposes all the fields, including the default ones:
+//
+// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
+// * `entry.Data["time"]`. The timestamp.
+// * `entry.Data["level"]. The level the entry was logged at.
+//
+// Any additional fields added with `WithField` or `WithFields` are also in
+// `entry.Data`. Format is expected to return an array of bytes which are then
+// logged to `logger.Out`.
+type Formatter interface {
+	Format(*Entry) ([]byte, error)
+}
+
+// This is to not silently overwrite `time`, `msg` and `level` fields when
+// dumping it. If this code wasn't there doing:
+//
+//  logrus.WithField("level", 1).Info("hello")
+//
+// Would just silently drop the user provided level. Instead with this code
+// it'll logged as:
+//
+//  {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
+//
+// It's not exported because it's still using Data in an opinionated way. It's to
+// avoid code duplication between the two default formatters.
+func prefixFieldClashes(data Fields, fieldMap FieldMap) {
+	timeKey := fieldMap.resolve(FieldKeyTime)
+	if t, ok := data[timeKey]; ok {
+		data["fields."+timeKey] = t
+		delete(data, timeKey)
+	}
+
+	msgKey := fieldMap.resolve(FieldKeyMsg)
+	if m, ok := data[msgKey]; ok {
+		data["fields."+msgKey] = m
+		delete(data, msgKey)
+	}
+
+	levelKey := fieldMap.resolve(FieldKeyLevel)
+	if l, ok := data[levelKey]; ok {
+		data["fields."+levelKey] = l
+		delete(data, levelKey)
+	}
+}

+ 34 - 0
vendor/github.com/Sirupsen/logrus/hooks.go

@@ -0,0 +1,34 @@
+package logrus
+
+// A hook to be fired when logging on the logging levels returned from
+// `Levels()` on your implementation of the interface. Note that this is not
+// fired in a goroutine or a channel with workers, you should handle such
+// functionality yourself if your call is non-blocking and you don't wish for
+// the logging calls for levels returned from `Levels()` to block.
+type Hook interface {
+	Levels() []Level
+	Fire(*Entry) error
+}
+
+// Internal type for storing the hooks on a logger instance.
+type LevelHooks map[Level][]Hook
+
+// Add a hook to an instance of logger. This is called with
+// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
+func (hooks LevelHooks) Add(hook Hook) {
+	for _, level := range hook.Levels() {
+		hooks[level] = append(hooks[level], hook)
+	}
+}
+
+// Fire all the hooks for the passed level. Used by `entry.log` to fire
+// appropriate hooks for a log entry.
+func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
+	for _, hook := range hooks[level] {
+		if err := hook.Fire(entry); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 89 - 0
vendor/github.com/Sirupsen/logrus/json_formatter.go

@@ -0,0 +1,89 @@
+package logrus
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+type fieldKey string
+
+// FieldMap allows customization of the key names for default fields.
+type FieldMap map[fieldKey]string
+
+// Default key names for the default fields
+const (
+	FieldKeyMsg   = "msg"
+	FieldKeyLevel = "level"
+	FieldKeyTime  = "time"
+)
+
+func (f FieldMap) resolve(key fieldKey) string {
+	if k, ok := f[key]; ok {
+		return k
+	}
+
+	return string(key)
+}
+
+// JSONFormatter formats logs into parsable json
+type JSONFormatter struct {
+	// TimestampFormat sets the format used for marshaling timestamps.
+	TimestampFormat string
+
+	// DisableTimestamp allows disabling automatic timestamps in output
+	DisableTimestamp bool
+
+	// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
+	DataKey string
+
+	// FieldMap allows users to customize the names of keys for default fields.
+	// As an example:
+	// formatter := &JSONFormatter{
+	//   	FieldMap: FieldMap{
+	// 		 FieldKeyTime: "@timestamp",
+	// 		 FieldKeyLevel: "@level",
+	// 		 FieldKeyMsg: "@message",
+	//    },
+	// }
+	FieldMap FieldMap
+}
+
+// Format renders a single log entry
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
+	data := make(Fields, len(entry.Data)+3)
+	for k, v := range entry.Data {
+		switch v := v.(type) {
+		case error:
+			// Otherwise errors are ignored by `encoding/json`
+			// https://github.com/sirupsen/logrus/issues/137
+			data[k] = v.Error()
+		default:
+			data[k] = v
+		}
+	}
+
+	if f.DataKey != "" {
+		newData := make(Fields, 4)
+		newData[f.DataKey] = data
+		data = newData
+	}
+
+	prefixFieldClashes(data, f.FieldMap)
+
+	timestampFormat := f.TimestampFormat
+	if timestampFormat == "" {
+		timestampFormat = defaultTimestampFormat
+	}
+
+	if !f.DisableTimestamp {
+		data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
+	}
+	data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
+	data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
+
+	serialized, err := json.Marshal(data)
+	if err != nil {
+		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+	}
+	return append(serialized, '\n'), nil
+}

+ 329 - 0
vendor/github.com/Sirupsen/logrus/logger.go

@@ -0,0 +1,329 @@
+package logrus
+
+import (
+	"io"
+	"os"
+	"sync"
+	"sync/atomic"
+)
+
+type Logger struct {
+	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
+	// file, or leave it default which is `os.Stderr`. You can also set this to
+	// something more adventorous, such as logging to Kafka.
+	Out io.Writer
+	// Hooks for the logger instance. These allow firing events based on logging
+	// levels and log entries. For example, to send errors to an error tracking
+	// service, log to StatsD or dump the core on fatal errors.
+	Hooks LevelHooks
+	// All log entries pass through the formatter before logged to Out. The
+	// included formatters are `TextFormatter` and `JSONFormatter` for which
+	// TextFormatter is the default. In development (when a TTY is attached) it
+	// logs with colors, but to a file it wouldn't. You can easily implement your
+	// own that implements the `Formatter` interface, see the `README` or included
+	// formatters for examples.
+	Formatter Formatter
+	// The logging level the logger should log at. This is typically (and defaults
+	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
+	// logged.
+	Level Level
+	// Used to sync writing to the log. Locking is enabled by Default
+	mu MutexWrap
+	// Reusable empty entry
+	entryPool sync.Pool
+}
+
+type MutexWrap struct {
+	lock     sync.Mutex
+	disabled bool
+}
+
+func (mw *MutexWrap) Lock() {
+	if !mw.disabled {
+		mw.lock.Lock()
+	}
+}
+
+func (mw *MutexWrap) Unlock() {
+	if !mw.disabled {
+		mw.lock.Unlock()
+	}
+}
+
+func (mw *MutexWrap) Disable() {
+	mw.disabled = true
+}
+
+// Creates a new logger. Configuration should be set by changing `Formatter`,
+// `Out` and `Hooks` directly on the default logger instance. You can also just
+// instantiate your own:
+//
+//    var log = &Logger{
+//      Out: os.Stderr,
+//      Formatter: new(JSONFormatter),
+//      Hooks: make(LevelHooks),
+//      Level: logrus.DebugLevel,
+//    }
+//
+// It's recommended to make this a global instance called `log`.
+func New() *Logger {
+	return &Logger{
+		Out:       os.Stderr,
+		Formatter: new(TextFormatter),
+		Hooks:     make(LevelHooks),
+		Level:     InfoLevel,
+	}
+}
+
+func (logger *Logger) newEntry() *Entry {
+	entry, ok := logger.entryPool.Get().(*Entry)
+	if ok {
+		return entry
+	}
+	return NewEntry(logger)
+}
+
+func (logger *Logger) releaseEntry(entry *Entry) {
+	logger.entryPool.Put(entry)
+}
+
+// Adds a field to the log entry, note that it doesn't log until you call
+// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry.
+// If you want multiple fields, use `WithFields`.
+func (logger *Logger) WithField(key string, value interface{}) *Entry {
+	entry := logger.newEntry()
+	defer logger.releaseEntry(entry)
+	return entry.WithField(key, value)
+}
+
+// Adds a struct of fields to the log entry. All it does is call `WithField` for
+// each `Field`.
+func (logger *Logger) WithFields(fields Fields) *Entry {
+	entry := logger.newEntry()
+	defer logger.releaseEntry(entry)
+	return entry.WithFields(fields)
+}
+
+// Add an error as single field to the log entry.  All it does is call
+// `WithError` for the given `error`.
+func (logger *Logger) WithError(err error) *Entry {
+	entry := logger.newEntry()
+	defer logger.releaseEntry(entry)
+	return entry.WithError(err)
+}
+
+func (logger *Logger) Debugf(format string, args ...interface{}) {
+	if logger.level() >= DebugLevel {
+		entry := logger.newEntry()
+		entry.Debugf(format, args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Infof(format string, args ...interface{}) {
+	if logger.level() >= InfoLevel {
+		entry := logger.newEntry()
+		entry.Infof(format, args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Printf(format string, args ...interface{}) {
+	entry := logger.newEntry()
+	entry.Printf(format, args...)
+	logger.releaseEntry(entry)
+}
+
+func (logger *Logger) Warnf(format string, args ...interface{}) {
+	if logger.level() >= WarnLevel {
+		entry := logger.newEntry()
+		entry.Warnf(format, args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Warningf(format string, args ...interface{}) {
+	if logger.level() >= WarnLevel {
+		entry := logger.newEntry()
+		entry.Warnf(format, args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Errorf(format string, args ...interface{}) {
+	if logger.level() >= ErrorLevel {
+		entry := logger.newEntry()
+		entry.Errorf(format, args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Fatalf(format string, args ...interface{}) {
+	if logger.level() >= FatalLevel {
+		entry := logger.newEntry()
+		entry.Fatalf(format, args...)
+		logger.releaseEntry(entry)
+	}
+	Exit(1)
+}
+
+func (logger *Logger) Panicf(format string, args ...interface{}) {
+	if logger.level() >= PanicLevel {
+		entry := logger.newEntry()
+		entry.Panicf(format, args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Debug(args ...interface{}) {
+	if logger.level() >= DebugLevel {
+		entry := logger.newEntry()
+		entry.Debug(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Info(args ...interface{}) {
+	if logger.level() >= InfoLevel {
+		entry := logger.newEntry()
+		entry.Info(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Print(args ...interface{}) {
+	entry := logger.newEntry()
+	entry.Info(args...)
+	logger.releaseEntry(entry)
+}
+
+func (logger *Logger) Warn(args ...interface{}) {
+	if logger.level() >= WarnLevel {
+		entry := logger.newEntry()
+		entry.Warn(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Warning(args ...interface{}) {
+	if logger.level() >= WarnLevel {
+		entry := logger.newEntry()
+		entry.Warn(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Error(args ...interface{}) {
+	if logger.level() >= ErrorLevel {
+		entry := logger.newEntry()
+		entry.Error(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Fatal(args ...interface{}) {
+	if logger.level() >= FatalLevel {
+		entry := logger.newEntry()
+		entry.Fatal(args...)
+		logger.releaseEntry(entry)
+	}
+	Exit(1)
+}
+
+func (logger *Logger) Panic(args ...interface{}) {
+	if logger.level() >= PanicLevel {
+		entry := logger.newEntry()
+		entry.Panic(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Debugln(args ...interface{}) {
+	if logger.level() >= DebugLevel {
+		entry := logger.newEntry()
+		entry.Debugln(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Infoln(args ...interface{}) {
+	if logger.level() >= InfoLevel {
+		entry := logger.newEntry()
+		entry.Infoln(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Println(args ...interface{}) {
+	entry := logger.newEntry()
+	entry.Println(args...)
+	logger.releaseEntry(entry)
+}
+
+func (logger *Logger) Warnln(args ...interface{}) {
+	if logger.level() >= WarnLevel {
+		entry := logger.newEntry()
+		entry.Warnln(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Warningln(args ...interface{}) {
+	if logger.level() >= WarnLevel {
+		entry := logger.newEntry()
+		entry.Warnln(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Errorln(args ...interface{}) {
+	if logger.level() >= ErrorLevel {
+		entry := logger.newEntry()
+		entry.Errorln(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+func (logger *Logger) Fatalln(args ...interface{}) {
+	if logger.level() >= FatalLevel {
+		entry := logger.newEntry()
+		entry.Fatalln(args...)
+		logger.releaseEntry(entry)
+	}
+	Exit(1)
+}
+
+func (logger *Logger) Panicln(args ...interface{}) {
+	if logger.level() >= PanicLevel {
+		entry := logger.newEntry()
+		entry.Panicln(args...)
+		logger.releaseEntry(entry)
+	}
+}
+
+//When file is opened with appending mode, it's safe to
+//write concurrently to a file (within 4k message on Linux).
+//In these cases user can choose to disable the lock.
+func (logger *Logger) SetNoLock() {
+	logger.mu.Disable()
+}
+
+func (logger *Logger) level() Level {
+	return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
+}
+
+func (logger *Logger) SetLevel(level Level) {
+	atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
+}
+
+func (logger *Logger) SetOutput(out io.Writer) {
+	logger.mu.Lock()
+	defer logger.mu.Unlock()
+	logger.Out = out
+}
+
+func (logger *Logger) AddHook(hook Hook) {
+	logger.mu.Lock()
+	defer logger.mu.Unlock()
+	logger.Hooks.Add(hook)
+}

+ 143 - 0
vendor/github.com/Sirupsen/logrus/logrus.go

@@ -0,0 +1,143 @@
+package logrus
+
+import (
+	"fmt"
+	"log"
+	"strings"
+)
+
+// Fields type, used to pass to `WithFields`.
+type Fields map[string]interface{}
+
+// Level type
+type Level uint32
+
+// Convert the Level to a string. E.g. PanicLevel becomes "panic".
+func (level Level) String() string {
+	switch level {
+	case DebugLevel:
+		return "debug"
+	case InfoLevel:
+		return "info"
+	case WarnLevel:
+		return "warning"
+	case ErrorLevel:
+		return "error"
+	case FatalLevel:
+		return "fatal"
+	case PanicLevel:
+		return "panic"
+	}
+
+	return "unknown"
+}
+
+// ParseLevel takes a string level and returns the Logrus log level constant.
+func ParseLevel(lvl string) (Level, error) {
+	switch strings.ToLower(lvl) {
+	case "panic":
+		return PanicLevel, nil
+	case "fatal":
+		return FatalLevel, nil
+	case "error":
+		return ErrorLevel, nil
+	case "warn", "warning":
+		return WarnLevel, nil
+	case "info":
+		return InfoLevel, nil
+	case "debug":
+		return DebugLevel, nil
+	}
+
+	var l Level
+	return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
+}
+
+// A constant exposing all logging levels
+var AllLevels = []Level{
+	PanicLevel,
+	FatalLevel,
+	ErrorLevel,
+	WarnLevel,
+	InfoLevel,
+	DebugLevel,
+}
+
+// These are the different logging levels. You can set the logging level to log
+// on your instance of logger, obtained with `logrus.New()`.
+const (
+	// PanicLevel level, highest level of severity. Logs and then calls panic with the
+	// message passed to Debug, Info, ...
+	PanicLevel Level = iota
+	// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
+	// logging level is set to Panic.
+	FatalLevel
+	// ErrorLevel level. Logs. Used for errors that should definitely be noted.
+	// Commonly used for hooks to send errors to an error tracking service.
+	ErrorLevel
+	// WarnLevel level. Non-critical entries that deserve eyes.
+	WarnLevel
+	// InfoLevel level. General operational entries about what's going on inside the
+	// application.
+	InfoLevel
+	// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
+	DebugLevel
+)
+
+// Won't compile if StdLogger can't be realized by a log.Logger
+var (
+	_ StdLogger = &log.Logger{}
+	_ StdLogger = &Entry{}
+	_ StdLogger = &Logger{}
+)
+
+// StdLogger is what your logrus-enabled library should take, that way
+// it'll accept a stdlib logger and a logrus logger. There's no standard
+// interface, this is the closest we get, unfortunately.
+type StdLogger interface {
+	Print(...interface{})
+	Printf(string, ...interface{})
+	Println(...interface{})
+
+	Fatal(...interface{})
+	Fatalf(string, ...interface{})
+	Fatalln(...interface{})
+
+	Panic(...interface{})
+	Panicf(string, ...interface{})
+	Panicln(...interface{})
+}
+
+// The FieldLogger interface generalizes the Entry and Logger types
+type FieldLogger interface {
+	WithField(key string, value interface{}) *Entry
+	WithFields(fields Fields) *Entry
+	WithError(err error) *Entry
+
+	Debugf(format string, args ...interface{})
+	Infof(format string, args ...interface{})
+	Printf(format string, args ...interface{})
+	Warnf(format string, args ...interface{})
+	Warningf(format string, args ...interface{})
+	Errorf(format string, args ...interface{})
+	Fatalf(format string, args ...interface{})
+	Panicf(format string, args ...interface{})
+
+	Debug(args ...interface{})
+	Info(args ...interface{})
+	Print(args ...interface{})
+	Warn(args ...interface{})
+	Warning(args ...interface{})
+	Error(args ...interface{})
+	Fatal(args ...interface{})
+	Panic(args ...interface{})
+
+	Debugln(args ...interface{})
+	Infoln(args ...interface{})
+	Println(args ...interface{})
+	Warnln(args ...interface{})
+	Warningln(args ...interface{})
+	Errorln(args ...interface{})
+	Fatalln(args ...interface{})
+	Panicln(args ...interface{})
+}

+ 10 - 0
vendor/github.com/Sirupsen/logrus/terminal_bsd.go

@@ -0,0 +1,10 @@
+// +build darwin freebsd openbsd netbsd dragonfly
+// +build !appengine,!gopherjs
+
+package logrus
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TIOCGETA
+
+type Termios unix.Termios

+ 11 - 0
vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go

@@ -0,0 +1,11 @@
+// +build appengine gopherjs
+
+package logrus
+
+import (
+	"io"
+)
+
+func checkIfTerminal(w io.Writer) bool {
+	return true
+}

+ 19 - 0
vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go

@@ -0,0 +1,19 @@
+// +build !appengine,!gopherjs
+
+package logrus
+
+import (
+	"io"
+	"os"
+
+	"golang.org/x/crypto/ssh/terminal"
+)
+
+func checkIfTerminal(w io.Writer) bool {
+	switch v := w.(type) {
+	case *os.File:
+		return terminal.IsTerminal(int(v.Fd()))
+	default:
+		return false
+	}
+}

+ 14 - 0
vendor/github.com/Sirupsen/logrus/terminal_linux.go

@@ -0,0 +1,14 @@
+// Based on ssh/terminal:
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine,!gopherjs
+
+package logrus
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+
+type Termios unix.Termios

+ 195 - 0
vendor/github.com/Sirupsen/logrus/text_formatter.go

@@ -0,0 +1,195 @@
+package logrus
+
+import (
+	"bytes"
+	"fmt"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+)
+
+const (
+	nocolor = 0
+	red     = 31
+	green   = 32
+	yellow  = 33
+	blue    = 36
+	gray    = 37
+)
+
+var (
+	baseTimestamp time.Time
+	emptyFieldMap FieldMap
+)
+
+func init() {
+	baseTimestamp = time.Now()
+}
+
+// TextFormatter formats logs into text
+type TextFormatter struct {
+	// Set to true to bypass checking for a TTY before outputting colors.
+	ForceColors bool
+
+	// Force disabling colors.
+	DisableColors bool
+
+	// Disable timestamp logging. useful when output is redirected to logging
+	// system that already adds timestamps.
+	DisableTimestamp bool
+
+	// Enable logging the full timestamp when a TTY is attached instead of just
+	// the time passed since beginning of execution.
+	FullTimestamp bool
+
+	// TimestampFormat to use for display when a full timestamp is printed
+	TimestampFormat string
+
+	// The fields are sorted by default for a consistent output. For applications
+	// that log extremely frequently and don't use the JSON formatter this may not
+	// be desired.
+	DisableSorting bool
+
+	// Disables the truncation of the level text to 4 characters.
+	DisableLevelTruncation bool
+
+	// QuoteEmptyFields will wrap empty fields in quotes if true
+	QuoteEmptyFields bool
+
+	// Whether the logger's out is to a terminal
+	isTerminal bool
+
+	// FieldMap allows users to customize the names of keys for default fields.
+	// As an example:
+	// formatter := &TextFormatter{
+	//     FieldMap: FieldMap{
+	//         FieldKeyTime:  "@timestamp",
+	//         FieldKeyLevel: "@level",
+	//         FieldKeyMsg:   "@message"}}
+	FieldMap FieldMap
+
+	sync.Once
+}
+
+func (f *TextFormatter) init(entry *Entry) {
+	if entry.Logger != nil {
+		f.isTerminal = checkIfTerminal(entry.Logger.Out)
+	}
+}
+
+// Format renders a single log entry
+func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
+	prefixFieldClashes(entry.Data, f.FieldMap)
+
+	keys := make([]string, 0, len(entry.Data))
+	for k := range entry.Data {
+		keys = append(keys, k)
+	}
+
+	if !f.DisableSorting {
+		sort.Strings(keys)
+	}
+
+	var b *bytes.Buffer
+	if entry.Buffer != nil {
+		b = entry.Buffer
+	} else {
+		b = &bytes.Buffer{}
+	}
+
+	f.Do(func() { f.init(entry) })
+
+	isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
+
+	timestampFormat := f.TimestampFormat
+	if timestampFormat == "" {
+		timestampFormat = defaultTimestampFormat
+	}
+	if isColored {
+		f.printColored(b, entry, keys, timestampFormat)
+	} else {
+		if !f.DisableTimestamp {
+			f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyTime), entry.Time.Format(timestampFormat))
+		}
+		f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyLevel), entry.Level.String())
+		if entry.Message != "" {
+			f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyMsg), entry.Message)
+		}
+		for _, key := range keys {
+			f.appendKeyValue(b, key, entry.Data[key])
+		}
+	}
+
+	b.WriteByte('\n')
+	return b.Bytes(), nil
+}
+
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
+	var levelColor int
+	switch entry.Level {
+	case DebugLevel:
+		levelColor = gray
+	case WarnLevel:
+		levelColor = yellow
+	case ErrorLevel, FatalLevel, PanicLevel:
+		levelColor = red
+	default:
+		levelColor = blue
+	}
+
+	levelText := strings.ToUpper(entry.Level.String())
+	if !f.DisableLevelTruncation {
+		levelText = levelText[0:4]
+	}
+
+	if f.DisableTimestamp {
+		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
+	} else if !f.FullTimestamp {
+		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
+	} else {
+		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
+	}
+	for _, k := range keys {
+		v := entry.Data[k]
+		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
+		f.appendValue(b, v)
+	}
+}
+
+func (f *TextFormatter) needsQuoting(text string) bool {
+	if f.QuoteEmptyFields && len(text) == 0 {
+		return true
+	}
+	for _, ch := range text {
+		if !((ch >= 'a' && ch <= 'z') ||
+			(ch >= 'A' && ch <= 'Z') ||
+			(ch >= '0' && ch <= '9') ||
+			ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
+			return true
+		}
+	}
+	return false
+}
+
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
+	if b.Len() > 0 {
+		b.WriteByte(' ')
+	}
+	b.WriteString(key)
+	b.WriteByte('=')
+	f.appendValue(b, value)
+}
+
+func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
+	stringVal, ok := value.(string)
+	if !ok {
+		stringVal = fmt.Sprint(value)
+	}
+
+	if !f.needsQuoting(stringVal) {
+		b.WriteString(stringVal)
+	} else {
+		b.WriteString(fmt.Sprintf("%q", stringVal))
+	}
+}

+ 62 - 0
vendor/github.com/Sirupsen/logrus/writer.go

@@ -0,0 +1,62 @@
+package logrus
+
+import (
+	"bufio"
+	"io"
+	"runtime"
+)
+
+func (logger *Logger) Writer() *io.PipeWriter {
+	return logger.WriterLevel(InfoLevel)
+}
+
+func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
+	return NewEntry(logger).WriterLevel(level)
+}
+
+func (entry *Entry) Writer() *io.PipeWriter {
+	return entry.WriterLevel(InfoLevel)
+}
+
+func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
+	reader, writer := io.Pipe()
+
+	var printFunc func(args ...interface{})
+
+	switch level {
+	case DebugLevel:
+		printFunc = entry.Debug
+	case InfoLevel:
+		printFunc = entry.Info
+	case WarnLevel:
+		printFunc = entry.Warn
+	case ErrorLevel:
+		printFunc = entry.Error
+	case FatalLevel:
+		printFunc = entry.Fatal
+	case PanicLevel:
+		printFunc = entry.Panic
+	default:
+		printFunc = entry.Print
+	}
+
+	go entry.writerScanner(reader, printFunc)
+	runtime.SetFinalizer(writer, writerFinalizer)
+
+	return writer
+}
+
+func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
+	scanner := bufio.NewScanner(reader)
+	for scanner.Scan() {
+		printFunc(scanner.Text())
+	}
+	if err := scanner.Err(); err != nil {
+		entry.Errorf("Error while reading from Writer: %s", err)
+	}
+	reader.Close()
+}
+
+func writerFinalizer(writer *io.PipeWriter) {
+	writer.Close()
+}

+ 951 - 0
vendor/golang.org/x/crypto/ssh/terminal/terminal.go

@@ -0,0 +1,951 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package terminal
+
+import (
+	"bytes"
+	"io"
+	"sync"
+	"unicode/utf8"
+)
+
+// EscapeCodes contains escape sequences that can be written to the terminal in
+// order to achieve different styles of text.
+type EscapeCodes struct {
+	// Foreground colors
+	Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
+
+	// Reset all attributes
+	Reset []byte
+}
+
+var vt100EscapeCodes = EscapeCodes{
+	Black:   []byte{keyEscape, '[', '3', '0', 'm'},
+	Red:     []byte{keyEscape, '[', '3', '1', 'm'},
+	Green:   []byte{keyEscape, '[', '3', '2', 'm'},
+	Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
+	Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
+	Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
+	Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
+	White:   []byte{keyEscape, '[', '3', '7', 'm'},
+
+	Reset: []byte{keyEscape, '[', '0', 'm'},
+}
+
+// Terminal contains the state for running a VT100 terminal that is capable of
+// reading lines of input.
+type Terminal struct {
+	// AutoCompleteCallback, if non-null, is called for each keypress with
+	// the full input line and the current position of the cursor (in
+	// bytes, as an index into |line|). If it returns ok=false, the key
+	// press is processed normally. Otherwise it returns a replacement line
+	// and the new cursor position.
+	AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
+
+	// Escape contains a pointer to the escape codes for this terminal.
+	// It's always a valid pointer, although the escape codes themselves
+	// may be empty if the terminal doesn't support them.
+	Escape *EscapeCodes
+
+	// lock protects the terminal and the state in this object from
+	// concurrent processing of a key press and a Write() call.
+	lock sync.Mutex
+
+	c      io.ReadWriter
+	prompt []rune
+
+	// line is the current line being entered.
+	line []rune
+	// pos is the logical position of the cursor in line
+	pos int
+	// echo is true if local echo is enabled
+	echo bool
+	// pasteActive is true iff there is a bracketed paste operation in
+	// progress.
+	pasteActive bool
+
+	// cursorX contains the current X value of the cursor where the left
+	// edge is 0. cursorY contains the row number where the first row of
+	// the current line is 0.
+	cursorX, cursorY int
+	// maxLine is the greatest value of cursorY so far.
+	maxLine int
+
+	termWidth, termHeight int
+
+	// outBuf contains the terminal data to be sent.
+	outBuf []byte
+	// remainder contains the remainder of any partial key sequences after
+	// a read. It aliases into inBuf.
+	remainder []byte
+	inBuf     [256]byte
+
+	// history contains previously entered commands so that they can be
+	// accessed with the up and down keys.
+	history stRingBuffer
+	// historyIndex stores the currently accessed history entry, where zero
+	// means the immediately previous entry.
+	historyIndex int
+	// When navigating up and down the history it's possible to return to
+	// the incomplete, initial line. That value is stored in
+	// historyPending.
+	historyPending string
+}
+
+// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
+// a local terminal, that terminal must first have been put into raw mode.
+// prompt is a string that is written at the start of each input line (i.e.
+// "> ").
+func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
+	return &Terminal{
+		Escape:       &vt100EscapeCodes,
+		c:            c,
+		prompt:       []rune(prompt),
+		termWidth:    80,
+		termHeight:   24,
+		echo:         true,
+		historyIndex: -1,
+	}
+}
+
+const (
+	keyCtrlD     = 4
+	keyCtrlU     = 21
+	keyEnter     = '\r'
+	keyEscape    = 27
+	keyBackspace = 127
+	keyUnknown   = 0xd800 /* UTF-16 surrogate area */ + iota
+	keyUp
+	keyDown
+	keyLeft
+	keyRight
+	keyAltLeft
+	keyAltRight
+	keyHome
+	keyEnd
+	keyDeleteWord
+	keyDeleteLine
+	keyClearScreen
+	keyPasteStart
+	keyPasteEnd
+)
+
+var (
+	crlf       = []byte{'\r', '\n'}
+	pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
+	pasteEnd   = []byte{keyEscape, '[', '2', '0', '1', '~'}
+)
+
+// bytesToKey tries to parse a key sequence from b. If successful, it returns
+// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
+func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
+	if len(b) == 0 {
+		return utf8.RuneError, nil
+	}
+
+	if !pasteActive {
+		switch b[0] {
+		case 1: // ^A
+			return keyHome, b[1:]
+		case 5: // ^E
+			return keyEnd, b[1:]
+		case 8: // ^H
+			return keyBackspace, b[1:]
+		case 11: // ^K
+			return keyDeleteLine, b[1:]
+		case 12: // ^L
+			return keyClearScreen, b[1:]
+		case 23: // ^W
+			return keyDeleteWord, b[1:]
+		}
+	}
+
+	if b[0] != keyEscape {
+		if !utf8.FullRune(b) {
+			return utf8.RuneError, b
+		}
+		r, l := utf8.DecodeRune(b)
+		return r, b[l:]
+	}
+
+	if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
+		switch b[2] {
+		case 'A':
+			return keyUp, b[3:]
+		case 'B':
+			return keyDown, b[3:]
+		case 'C':
+			return keyRight, b[3:]
+		case 'D':
+			return keyLeft, b[3:]
+		case 'H':
+			return keyHome, b[3:]
+		case 'F':
+			return keyEnd, b[3:]
+		}
+	}
+
+	if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
+		switch b[5] {
+		case 'C':
+			return keyAltRight, b[6:]
+		case 'D':
+			return keyAltLeft, b[6:]
+		}
+	}
+
+	if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
+		return keyPasteStart, b[6:]
+	}
+
+	if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
+		return keyPasteEnd, b[6:]
+	}
+
+	// If we get here then we have a key that we don't recognise, or a
+	// partial sequence. It's not clear how one should find the end of a
+	// sequence without knowing them all, but it seems that [a-zA-Z~] only
+	// appears at the end of a sequence.
+	for i, c := range b[0:] {
+		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
+			return keyUnknown, b[i+1:]
+		}
+	}
+
+	return utf8.RuneError, b
+}
+
+// queue appends data to the end of t.outBuf
+func (t *Terminal) queue(data []rune) {
+	t.outBuf = append(t.outBuf, []byte(string(data))...)
+}
+
+var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
+var space = []rune{' '}
+
+func isPrintable(key rune) bool {
+	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
+	return key >= 32 && !isInSurrogateArea
+}
+
+// moveCursorToPos appends data to t.outBuf which will move the cursor to the
+// given, logical position in the text.
+func (t *Terminal) moveCursorToPos(pos int) {
+	if !t.echo {
+		return
+	}
+
+	x := visualLength(t.prompt) + pos
+	y := x / t.termWidth
+	x = x % t.termWidth
+
+	up := 0
+	if y < t.cursorY {
+		up = t.cursorY - y
+	}
+
+	down := 0
+	if y > t.cursorY {
+		down = y - t.cursorY
+	}
+
+	left := 0
+	if x < t.cursorX {
+		left = t.cursorX - x
+	}
+
+	right := 0
+	if x > t.cursorX {
+		right = x - t.cursorX
+	}
+
+	t.cursorX = x
+	t.cursorY = y
+	t.move(up, down, left, right)
+}
+
+func (t *Terminal) move(up, down, left, right int) {
+	movement := make([]rune, 3*(up+down+left+right))
+	m := movement
+	for i := 0; i < up; i++ {
+		m[0] = keyEscape
+		m[1] = '['
+		m[2] = 'A'
+		m = m[3:]
+	}
+	for i := 0; i < down; i++ {
+		m[0] = keyEscape
+		m[1] = '['
+		m[2] = 'B'
+		m = m[3:]
+	}
+	for i := 0; i < left; i++ {
+		m[0] = keyEscape
+		m[1] = '['
+		m[2] = 'D'
+		m = m[3:]
+	}
+	for i := 0; i < right; i++ {
+		m[0] = keyEscape
+		m[1] = '['
+		m[2] = 'C'
+		m = m[3:]
+	}
+
+	t.queue(movement)
+}
+
+func (t *Terminal) clearLineToRight() {
+	op := []rune{keyEscape, '[', 'K'}
+	t.queue(op)
+}
+
+const maxLineLength = 4096
+
+func (t *Terminal) setLine(newLine []rune, newPos int) {
+	if t.echo {
+		t.moveCursorToPos(0)
+		t.writeLine(newLine)
+		for i := len(newLine); i < len(t.line); i++ {
+			t.writeLine(space)
+		}
+		t.moveCursorToPos(newPos)
+	}
+	t.line = newLine
+	t.pos = newPos
+}
+
+func (t *Terminal) advanceCursor(places int) {
+	t.cursorX += places
+	t.cursorY += t.cursorX / t.termWidth
+	if t.cursorY > t.maxLine {
+		t.maxLine = t.cursorY
+	}
+	t.cursorX = t.cursorX % t.termWidth
+
+	if places > 0 && t.cursorX == 0 {
+		// Normally terminals will advance the current position
+		// when writing a character. But that doesn't happen
+		// for the last character in a line. However, when
+		// writing a character (except a new line) that causes
+		// a line wrap, the position will be advanced two
+		// places.
+		//
+		// So, if we are stopping at the end of a line, we
+		// need to write a newline so that our cursor can be
+		// advanced to the next line.
+		t.outBuf = append(t.outBuf, '\r', '\n')
+	}
+}
+
+func (t *Terminal) eraseNPreviousChars(n int) {
+	if n == 0 {
+		return
+	}
+
+	if t.pos < n {
+		n = t.pos
+	}
+	t.pos -= n
+	t.moveCursorToPos(t.pos)
+
+	copy(t.line[t.pos:], t.line[n+t.pos:])
+	t.line = t.line[:len(t.line)-n]
+	if t.echo {
+		t.writeLine(t.line[t.pos:])
+		for i := 0; i < n; i++ {
+			t.queue(space)
+		}
+		t.advanceCursor(n)
+		t.moveCursorToPos(t.pos)
+	}
+}
+
+// countToLeftWord returns then number of characters from the cursor to the
+// start of the previous word.
+func (t *Terminal) countToLeftWord() int {
+	if t.pos == 0 {
+		return 0
+	}
+
+	pos := t.pos - 1
+	for pos > 0 {
+		if t.line[pos] != ' ' {
+			break
+		}
+		pos--
+	}
+	for pos > 0 {
+		if t.line[pos] == ' ' {
+			pos++
+			break
+		}
+		pos--
+	}
+
+	return t.pos - pos
+}
+
+// countToRightWord returns then number of characters from the cursor to the
+// start of the next word.
+func (t *Terminal) countToRightWord() int {
+	pos := t.pos
+	for pos < len(t.line) {
+		if t.line[pos] == ' ' {
+			break
+		}
+		pos++
+	}
+	for pos < len(t.line) {
+		if t.line[pos] != ' ' {
+			break
+		}
+		pos++
+	}
+	return pos - t.pos
+}
+
+// visualLength returns the number of visible glyphs in s.
+func visualLength(runes []rune) int {
+	inEscapeSeq := false
+	length := 0
+
+	for _, r := range runes {
+		switch {
+		case inEscapeSeq:
+			if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
+				inEscapeSeq = false
+			}
+		case r == '\x1b':
+			inEscapeSeq = true
+		default:
+			length++
+		}
+	}
+
+	return length
+}
+
+// handleKey processes the given key and, optionally, returns a line of text
+// that the user has entered.
+func (t *Terminal) handleKey(key rune) (line string, ok bool) {
+	if t.pasteActive && key != keyEnter {
+		t.addKeyToLine(key)
+		return
+	}
+
+	switch key {
+	case keyBackspace:
+		if t.pos == 0 {
+			return
+		}
+		t.eraseNPreviousChars(1)
+	case keyAltLeft:
+		// move left by a word.
+		t.pos -= t.countToLeftWord()
+		t.moveCursorToPos(t.pos)
+	case keyAltRight:
+		// move right by a word.
+		t.pos += t.countToRightWord()
+		t.moveCursorToPos(t.pos)
+	case keyLeft:
+		if t.pos == 0 {
+			return
+		}
+		t.pos--
+		t.moveCursorToPos(t.pos)
+	case keyRight:
+		if t.pos == len(t.line) {
+			return
+		}
+		t.pos++
+		t.moveCursorToPos(t.pos)
+	case keyHome:
+		if t.pos == 0 {
+			return
+		}
+		t.pos = 0
+		t.moveCursorToPos(t.pos)
+	case keyEnd:
+		if t.pos == len(t.line) {
+			return
+		}
+		t.pos = len(t.line)
+		t.moveCursorToPos(t.pos)
+	case keyUp:
+		entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
+		if !ok {
+			return "", false
+		}
+		if t.historyIndex == -1 {
+			t.historyPending = string(t.line)
+		}
+		t.historyIndex++
+		runes := []rune(entry)
+		t.setLine(runes, len(runes))
+	case keyDown:
+		switch t.historyIndex {
+		case -1:
+			return
+		case 0:
+			runes := []rune(t.historyPending)
+			t.setLine(runes, len(runes))
+			t.historyIndex--
+		default:
+			entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
+			if ok {
+				t.historyIndex--
+				runes := []rune(entry)
+				t.setLine(runes, len(runes))
+			}
+		}
+	case keyEnter:
+		t.moveCursorToPos(len(t.line))
+		t.queue([]rune("\r\n"))
+		line = string(t.line)
+		ok = true
+		t.line = t.line[:0]
+		t.pos = 0
+		t.cursorX = 0
+		t.cursorY = 0
+		t.maxLine = 0
+	case keyDeleteWord:
+		// Delete zero or more spaces and then one or more characters.
+		t.eraseNPreviousChars(t.countToLeftWord())
+	case keyDeleteLine:
+		// Delete everything from the current cursor position to the
+		// end of line.
+		for i := t.pos; i < len(t.line); i++ {
+			t.queue(space)
+			t.advanceCursor(1)
+		}
+		t.line = t.line[:t.pos]
+		t.moveCursorToPos(t.pos)
+	case keyCtrlD:
+		// Erase the character under the current position.
+		// The EOF case when the line is empty is handled in
+		// readLine().
+		if t.pos < len(t.line) {
+			t.pos++
+			t.eraseNPreviousChars(1)
+		}
+	case keyCtrlU:
+		t.eraseNPreviousChars(t.pos)
+	case keyClearScreen:
+		// Erases the screen and moves the cursor to the home position.
+		t.queue([]rune("\x1b[2J\x1b[H"))
+		t.queue(t.prompt)
+		t.cursorX, t.cursorY = 0, 0
+		t.advanceCursor(visualLength(t.prompt))
+		t.setLine(t.line, t.pos)
+	default:
+		if t.AutoCompleteCallback != nil {
+			prefix := string(t.line[:t.pos])
+			suffix := string(t.line[t.pos:])
+
+			t.lock.Unlock()
+			newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
+			t.lock.Lock()
+
+			if completeOk {
+				t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
+				return
+			}
+		}
+		if !isPrintable(key) {
+			return
+		}
+		if len(t.line) == maxLineLength {
+			return
+		}
+		t.addKeyToLine(key)
+	}
+	return
+}
+
+// addKeyToLine inserts the given key at the current position in the current
+// line.
+func (t *Terminal) addKeyToLine(key rune) {
+	if len(t.line) == cap(t.line) {
+		newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
+		copy(newLine, t.line)
+		t.line = newLine
+	}
+	t.line = t.line[:len(t.line)+1]
+	copy(t.line[t.pos+1:], t.line[t.pos:])
+	t.line[t.pos] = key
+	if t.echo {
+		t.writeLine(t.line[t.pos:])
+	}
+	t.pos++
+	t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) writeLine(line []rune) {
+	for len(line) != 0 {
+		remainingOnLine := t.termWidth - t.cursorX
+		todo := len(line)
+		if todo > remainingOnLine {
+			todo = remainingOnLine
+		}
+		t.queue(line[:todo])
+		t.advanceCursor(visualLength(line[:todo]))
+		line = line[todo:]
+	}
+}
+
+// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
+func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
+	for len(buf) > 0 {
+		i := bytes.IndexByte(buf, '\n')
+		todo := len(buf)
+		if i >= 0 {
+			todo = i
+		}
+
+		var nn int
+		nn, err = w.Write(buf[:todo])
+		n += nn
+		if err != nil {
+			return n, err
+		}
+		buf = buf[todo:]
+
+		if i >= 0 {
+			if _, err = w.Write(crlf); err != nil {
+				return n, err
+			}
+			n++
+			buf = buf[1:]
+		}
+	}
+
+	return n, nil
+}
+
+func (t *Terminal) Write(buf []byte) (n int, err error) {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	if t.cursorX == 0 && t.cursorY == 0 {
+		// This is the easy case: there's nothing on the screen that we
+		// have to move out of the way.
+		return writeWithCRLF(t.c, buf)
+	}
+
+	// We have a prompt and possibly user input on the screen. We
+	// have to clear it first.
+	t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
+	t.cursorX = 0
+	t.clearLineToRight()
+
+	for t.cursorY > 0 {
+		t.move(1 /* up */, 0, 0, 0)
+		t.cursorY--
+		t.clearLineToRight()
+	}
+
+	if _, err = t.c.Write(t.outBuf); err != nil {
+		return
+	}
+	t.outBuf = t.outBuf[:0]
+
+	if n, err = writeWithCRLF(t.c, buf); err != nil {
+		return
+	}
+
+	t.writeLine(t.prompt)
+	if t.echo {
+		t.writeLine(t.line)
+	}
+
+	t.moveCursorToPos(t.pos)
+
+	if _, err = t.c.Write(t.outBuf); err != nil {
+		return
+	}
+	t.outBuf = t.outBuf[:0]
+	return
+}
+
+// ReadPassword temporarily changes the prompt and reads a password, without
+// echo, from the terminal.
+func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	oldPrompt := t.prompt
+	t.prompt = []rune(prompt)
+	t.echo = false
+
+	line, err = t.readLine()
+
+	t.prompt = oldPrompt
+	t.echo = true
+
+	return
+}
+
+// ReadLine returns a line of input from the terminal.
+func (t *Terminal) ReadLine() (line string, err error) {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	return t.readLine()
+}
+
+func (t *Terminal) readLine() (line string, err error) {
+	// t.lock must be held at this point
+
+	if t.cursorX == 0 && t.cursorY == 0 {
+		t.writeLine(t.prompt)
+		t.c.Write(t.outBuf)
+		t.outBuf = t.outBuf[:0]
+	}
+
+	lineIsPasted := t.pasteActive
+
+	for {
+		rest := t.remainder
+		lineOk := false
+		for !lineOk {
+			var key rune
+			key, rest = bytesToKey(rest, t.pasteActive)
+			if key == utf8.RuneError {
+				break
+			}
+			if !t.pasteActive {
+				if key == keyCtrlD {
+					if len(t.line) == 0 {
+						return "", io.EOF
+					}
+				}
+				if key == keyPasteStart {
+					t.pasteActive = true
+					if len(t.line) == 0 {
+						lineIsPasted = true
+					}
+					continue
+				}
+			} else if key == keyPasteEnd {
+				t.pasteActive = false
+				continue
+			}
+			if !t.pasteActive {
+				lineIsPasted = false
+			}
+			line, lineOk = t.handleKey(key)
+		}
+		if len(rest) > 0 {
+			n := copy(t.inBuf[:], rest)
+			t.remainder = t.inBuf[:n]
+		} else {
+			t.remainder = nil
+		}
+		t.c.Write(t.outBuf)
+		t.outBuf = t.outBuf[:0]
+		if lineOk {
+			if t.echo {
+				t.historyIndex = -1
+				t.history.Add(line)
+			}
+			if lineIsPasted {
+				err = ErrPasteIndicator
+			}
+			return
+		}
+
+		// t.remainder is a slice at the beginning of t.inBuf
+		// containing a partial key sequence
+		readBuf := t.inBuf[len(t.remainder):]
+		var n int
+
+		t.lock.Unlock()
+		n, err = t.c.Read(readBuf)
+		t.lock.Lock()
+
+		if err != nil {
+			return
+		}
+
+		t.remainder = t.inBuf[:n+len(t.remainder)]
+	}
+}
+
+// SetPrompt sets the prompt to be used when reading subsequent lines.
+func (t *Terminal) SetPrompt(prompt string) {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	t.prompt = []rune(prompt)
+}
+
+func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
+	// Move cursor to column zero at the start of the line.
+	t.move(t.cursorY, 0, t.cursorX, 0)
+	t.cursorX, t.cursorY = 0, 0
+	t.clearLineToRight()
+	for t.cursorY < numPrevLines {
+		// Move down a line
+		t.move(0, 1, 0, 0)
+		t.cursorY++
+		t.clearLineToRight()
+	}
+	// Move back to beginning.
+	t.move(t.cursorY, 0, 0, 0)
+	t.cursorX, t.cursorY = 0, 0
+
+	t.queue(t.prompt)
+	t.advanceCursor(visualLength(t.prompt))
+	t.writeLine(t.line)
+	t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) SetSize(width, height int) error {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	if width == 0 {
+		width = 1
+	}
+
+	oldWidth := t.termWidth
+	t.termWidth, t.termHeight = width, height
+
+	switch {
+	case width == oldWidth:
+		// If the width didn't change then nothing else needs to be
+		// done.
+		return nil
+	case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
+		// If there is nothing on current line and no prompt printed,
+		// just do nothing
+		return nil
+	case width < oldWidth:
+		// Some terminals (e.g. xterm) will truncate lines that were
+		// too long when shinking. Others, (e.g. gnome-terminal) will
+		// attempt to wrap them. For the former, repainting t.maxLine
+		// works great, but that behaviour goes badly wrong in the case
+		// of the latter because they have doubled every full line.
+
+		// We assume that we are working on a terminal that wraps lines
+		// and adjust the cursor position based on every previous line
+		// wrapping and turning into two. This causes the prompt on
+		// xterms to move upwards, which isn't great, but it avoids a
+		// huge mess with gnome-terminal.
+		if t.cursorX >= t.termWidth {
+			t.cursorX = t.termWidth - 1
+		}
+		t.cursorY *= 2
+		t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
+	case width > oldWidth:
+		// If the terminal expands then our position calculations will
+		// be wrong in the future because we think the cursor is
+		// |t.pos| chars into the string, but there will be a gap at
+		// the end of any wrapped line.
+		//
+		// But the position will actually be correct until we move, so
+		// we can move back to the beginning and repaint everything.
+		t.clearAndRepaintLinePlusNPrevious(t.maxLine)
+	}
+
+	_, err := t.c.Write(t.outBuf)
+	t.outBuf = t.outBuf[:0]
+	return err
+}
+
+type pasteIndicatorError struct{}
+
+func (pasteIndicatorError) Error() string {
+	return "terminal: ErrPasteIndicator not correctly handled"
+}
+
+// ErrPasteIndicator may be returned from ReadLine as the error, in addition
+// to valid line data. It indicates that bracketed paste mode is enabled and
+// that the returned line consists only of pasted data. Programs may wish to
+// interpret pasted data more literally than typed data.
+var ErrPasteIndicator = pasteIndicatorError{}
+
+// SetBracketedPasteMode requests that the terminal bracket paste operations
+// with markers. Not all terminals support this but, if it is supported, then
+// enabling this mode will stop any autocomplete callback from running due to
+// pastes. Additionally, any lines that are completely pasted will be returned
+// from ReadLine with the error set to ErrPasteIndicator.
+func (t *Terminal) SetBracketedPasteMode(on bool) {
+	if on {
+		io.WriteString(t.c, "\x1b[?2004h")
+	} else {
+		io.WriteString(t.c, "\x1b[?2004l")
+	}
+}
+
+// stRingBuffer is a ring buffer of strings.
+type stRingBuffer struct {
+	// entries contains max elements.
+	entries []string
+	max     int
+	// head contains the index of the element most recently added to the ring.
+	head int
+	// size contains the number of elements in the ring.
+	size int
+}
+
+func (s *stRingBuffer) Add(a string) {
+	if s.entries == nil {
+		const defaultNumEntries = 100
+		s.entries = make([]string, defaultNumEntries)
+		s.max = defaultNumEntries
+	}
+
+	s.head = (s.head + 1) % s.max
+	s.entries[s.head] = a
+	if s.size < s.max {
+		s.size++
+	}
+}
+
+// NthPreviousEntry returns the value passed to the nth previous call to Add.
+// If n is zero then the immediately prior value is returned, if one, then the
+// next most recent, and so on. If such an element doesn't exist then ok is
+// false.
+func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
+	if n >= s.size {
+		return "", false
+	}
+	index := s.head - n
+	if index < 0 {
+		index += s.max
+	}
+	return s.entries[index], true
+}
+
+// readPasswordLine reads from reader until it finds \n or io.EOF.
+// The slice returned does not include the \n.
+// readPasswordLine also ignores any \r it finds.
+func readPasswordLine(reader io.Reader) ([]byte, error) {
+	var buf [1]byte
+	var ret []byte
+
+	for {
+		n, err := reader.Read(buf[:])
+		if n > 0 {
+			switch buf[0] {
+			case '\n':
+				return ret, nil
+			case '\r':
+				// remove \r from passwords on Windows
+			default:
+				ret = append(ret, buf[0])
+			}
+			continue
+		}
+		if err != nil {
+			if err == io.EOF && len(ret) > 0 {
+				return ret, nil
+			}
+			return ret, err
+		}
+	}
+}

+ 114 - 0
vendor/golang.org/x/crypto/ssh/terminal/util.go

@@ -0,0 +1,114 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// 	oldState, err := terminal.MakeRaw(0)
+// 	if err != nil {
+// 	        panic(err)
+// 	}
+// 	defer terminal.Restore(0, oldState)
+package terminal // import "golang.org/x/crypto/ssh/terminal"
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+// State contains the state of a terminal.
+type State struct {
+	termios unix.Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+	return err == nil
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+	if err != nil {
+		return nil, err
+	}
+
+	oldState := State{termios: *termios}
+
+	// This attempts to replicate the behaviour documented for cfmakeraw in
+	// the termios(3) manpage.
+	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
+	termios.Oflag &^= unix.OPOST
+	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
+	termios.Cflag &^= unix.CSIZE | unix.PARENB
+	termios.Cflag |= unix.CS8
+	termios.Cc[unix.VMIN] = 1
+	termios.Cc[unix.VTIME] = 0
+	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
+		return nil, err
+	}
+
+	return &oldState, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+	if err != nil {
+		return nil, err
+	}
+
+	return &State{termios: *termios}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+	return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+	if err != nil {
+		return -1, -1, err
+	}
+	return int(ws.Col), int(ws.Row), nil
+}
+
+// passwordReader is an io.Reader that reads from a specific file descriptor.
+type passwordReader int
+
+func (r passwordReader) Read(buf []byte) (int, error) {
+	return unix.Read(int(r), buf)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+	if err != nil {
+		return nil, err
+	}
+
+	newState := *termios
+	newState.Lflag &^= unix.ECHO
+	newState.Lflag |= unix.ICANON | unix.ISIG
+	newState.Iflag |= unix.ICRNL
+	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
+		return nil, err
+	}
+
+	defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
+
+	return readPasswordLine(passwordReader(fd))
+}

+ 12 - 0
vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go

@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package terminal
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TIOCGETA
+const ioctlWriteTermios = unix.TIOCSETA

+ 10 - 0
vendor/golang.org/x/crypto/ssh/terminal/util_linux.go

@@ -0,0 +1,10 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package terminal
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+const ioctlWriteTermios = unix.TCSETS

+ 58 - 0
vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go

@@ -0,0 +1,58 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// 	oldState, err := terminal.MakeRaw(0)
+// 	if err != nil {
+// 	        panic(err)
+// 	}
+// 	defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+	"fmt"
+	"runtime"
+)
+
+type State struct{}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	return false
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+	return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+	return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+	return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+	return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}

+ 124 - 0
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go

@@ -0,0 +1,124 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package terminal // import "golang.org/x/crypto/ssh/terminal"
+
+import (
+	"golang.org/x/sys/unix"
+	"io"
+	"syscall"
+)
+
+// State contains the state of a terminal.
+type State struct {
+	termios unix.Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
+	return err == nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
+	val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+	if err != nil {
+		return nil, err
+	}
+	oldState := *val
+
+	newState := oldState
+	newState.Lflag &^= syscall.ECHO
+	newState.Lflag |= syscall.ICANON | syscall.ISIG
+	newState.Iflag |= syscall.ICRNL
+	err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
+	if err != nil {
+		return nil, err
+	}
+
+	defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
+
+	var buf [16]byte
+	var ret []byte
+	for {
+		n, err := syscall.Read(fd, buf[:])
+		if err != nil {
+			return nil, err
+		}
+		if n == 0 {
+			if len(ret) == 0 {
+				return nil, io.EOF
+			}
+			break
+		}
+		if buf[n-1] == '\n' {
+			n--
+		}
+		ret = append(ret, buf[:n]...)
+		if n < len(buf) {
+			break
+		}
+	}
+
+	return ret, nil
+}
+
+// MakeRaw puts the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+// see http://cr.illumos.org/~webrev/andy_js/1060/
+func MakeRaw(fd int) (*State, error) {
+	termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+	if err != nil {
+		return nil, err
+	}
+
+	oldState := State{termios: *termios}
+
+	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
+	termios.Oflag &^= unix.OPOST
+	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
+	termios.Cflag &^= unix.CSIZE | unix.PARENB
+	termios.Cflag |= unix.CS8
+	termios.Cc[unix.VMIN] = 1
+	termios.Cc[unix.VTIME] = 0
+
+	if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
+		return nil, err
+	}
+
+	return &oldState, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, oldState *State) error {
+	return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+	termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+	if err != nil {
+		return nil, err
+	}
+
+	return &State{termios: *termios}, nil
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+	if err != nil {
+		return 0, 0, err
+	}
+	return int(ws.Col), int(ws.Row), nil
+}

+ 103 - 0
vendor/golang.org/x/crypto/ssh/terminal/util_windows.go

@@ -0,0 +1,103 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// 	oldState, err := terminal.MakeRaw(0)
+// 	if err != nil {
+// 	        panic(err)
+// 	}
+// 	defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+	"os"
+
+	"golang.org/x/sys/windows"
+)
+
+type State struct {
+	mode uint32
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	var st uint32
+	err := windows.GetConsoleMode(windows.Handle(fd), &st)
+	return err == nil
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+	var st uint32
+	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+		return nil, err
+	}
+	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
+	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
+		return nil, err
+	}
+	return &State{st}, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+	var st uint32
+	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+		return nil, err
+	}
+	return &State{st}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+	var info windows.ConsoleScreenBufferInfo
+	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
+		return 0, 0, err
+	}
+	return int(info.Size.X), int(info.Size.Y), nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	var st uint32
+	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+		return nil, err
+	}
+	old := st
+
+	st &^= (windows.ENABLE_ECHO_INPUT)
+	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
+	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
+		return nil, err
+	}
+
+	defer windows.SetConsoleMode(windows.Handle(fd), old)
+
+	var h windows.Handle
+	p, _ := windows.GetCurrentProcess()
+	if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
+		return nil, err
+	}
+
+	f := os.NewFile(uintptr(h), "stdin")
+	defer f.Close()
+	return readPasswordLine(f)
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä