mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	Compare commits
	
		
			135 Commits
		
	
	
		
			v1.16.0-de
			...
			release/v1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3c3823dc7f | ||
| 
						 | 
					2f73fff053 | ||
| 
						 | 
					fb5af37b3e | ||
| 
						 | 
					2ef37522b6 | ||
| 
						 | 
					46ff639a13 | ||
| 
						 | 
					0b91aaf62a | ||
| 
						 | 
					4a08d574cf | ||
| 
						 | 
					f104898989 | ||
| 
						 | 
					83d04df365 | ||
| 
						 | 
					9614bb1b9f | ||
| 
						 | 
					b2c3a7d79f | ||
| 
						 | 
					76bbcf1387 | ||
| 
						 | 
					1bcbc02045 | ||
| 
						 | 
					66ceee08dc | ||
| 
						 | 
					ffff835b73 | ||
| 
						 | 
					63c54f7e1f | ||
| 
						 | 
					f9845454cf | ||
| 
						 | 
					1e1211c194 | ||
| 
						 | 
					10e549df7d | ||
| 
						 | 
					519f69eb41 | ||
| 
						 | 
					d4501ece55 | ||
| 
						 | 
					c1152b15fe | ||
| 
						 | 
					cb31f88383 | ||
| 
						 | 
					6cb9ce1367 | ||
| 
						 | 
					d93d5d7906 | ||
| 
						 | 
					5c3863c319 | ||
| 
						 | 
					80b50afe1f | ||
| 
						 | 
					20a28b785a | ||
| 
						 | 
					d330b2f52b | ||
| 
						 | 
					6ab1d5a8a0 | ||
| 
						 | 
					05f6566786 | ||
| 
						 | 
					0ea4b786cb | ||
| 
						 | 
					30718ce4ac | ||
| 
						 | 
					aeec78543b | ||
| 
						 | 
					c9b8c12bc9 | ||
| 
						 | 
					b0dcf417ea | ||
| 
						 | 
					797194d2d0 | ||
| 
						 | 
					5bac1a692a | ||
| 
						 | 
					b6fba5b459 | ||
| 
						 | 
					e90d88d5d5 | ||
| 
						 | 
					7156e2a71a | ||
| 
						 | 
					e51d91a258 | ||
| 
						 | 
					0ed5a647dd | ||
| 
						 | 
					3fc42add16 | ||
| 
						 | 
					4b9786ba62 | ||
| 
						 | 
					aaf975bff1 | ||
| 
						 | 
					267fbbf201 | ||
| 
						 | 
					9e8df4b838 | ||
| 
						 | 
					d78aa189ec | ||
| 
						 | 
					6883c007d3 | ||
| 
						 | 
					91ea086ebe | ||
| 
						 | 
					b228d22736 | ||
| 
						 | 
					c4d8d53a6d | ||
| 
						 | 
					2f71571305 | ||
| 
						 | 
					a182a80f7c | ||
| 
						 | 
					89c57487cd | ||
| 
						 | 
					bb609cacee | ||
| 
						 | 
					e7f6da386f | ||
| 
						 | 
					7727f84fe8 | ||
| 
						 | 
					5d2676089e | ||
| 
						 | 
					9bea8d825b | ||
| 
						 | 
					3cc728870a | ||
| 
						 | 
					0793b5e9c0 | ||
| 
						 | 
					bb423f9350 | ||
| 
						 | 
					4d6c8d9b13 | ||
| 
						 | 
					b6b1560701 | ||
| 
						 | 
					30dbddcc4d | ||
| 
						 | 
					c491c22279 | ||
| 
						 | 
					5649f0d2b3 | ||
| 
						 | 
					7dd726faeb | ||
| 
						 | 
					14c979c1b2 | ||
| 
						 | 
					6b84a1d72b | ||
| 
						 | 
					68424eddf0 | ||
| 
						 | 
					ab23e4b7f4 | ||
| 
						 | 
					1bb88dad20 | ||
| 
						 | 
					94f0151789 | ||
| 
						 | 
					1e2fe9f0b4 | ||
| 
						 | 
					f3496c88b2 | ||
| 
						 | 
					89915ca8a0 | ||
| 
						 | 
					24fa56830f | ||
| 
						 | 
					0fa9ea516a | ||
| 
						 | 
					9b95b41aa8 | ||
| 
						 | 
					38e799779f | ||
| 
						 | 
					4f39e56795 | ||
| 
						 | 
					7b92f91e88 | ||
| 
						 | 
					aea49d0b92 | ||
| 
						 | 
					1b5908fb6a | ||
| 
						 | 
					65a76b7cb0 | ||
| 
						 | 
					c6f1825fe9 | ||
| 
						 | 
					4f5dbc4d00 | ||
| 
						 | 
					4ee8982e91 | ||
| 
						 | 
					0d10482168 | ||
| 
						 | 
					6d441de2bd | ||
| 
						 | 
					d15e49f7ff | ||
| 
						 | 
					39da4ac6d4 | ||
| 
						 | 
					8f29011723 | ||
| 
						 | 
					ee43d20d3b | ||
| 
						 | 
					99ffd826e5 | ||
| 
						 | 
					93bac4e10d | ||
| 
						 | 
					9fbb898058 | ||
| 
						 | 
					0a9794a6bc | ||
| 
						 | 
					d4044b9c98 | ||
| 
						 | 
					1e6d2e47e9 | ||
| 
						 | 
					d827b0bfb7 | ||
| 
						 | 
					d789170e31 | ||
| 
						 | 
					9bbe3eb0b4 | ||
| 
						 | 
					650fdceb5a | ||
| 
						 | 
					4c69e158e5 | ||
| 
						 | 
					b7e41f7b8f | ||
| 
						 | 
					5a3d9861ba | ||
| 
						 | 
					adb43358bc | ||
| 
						 | 
					d6a980501b | ||
| 
						 | 
					103a66ae83 | ||
| 
						 | 
					426fd2a816 | ||
| 
						 | 
					337f2625ac | ||
| 
						 | 
					5ebf4990a5 | ||
| 
						 | 
					3fd07a0be6 | ||
| 
						 | 
					d372539f79 | ||
| 
						 | 
					91e24a3a10 | ||
| 
						 | 
					a29e667eff | ||
| 
						 | 
					92b993c91f | ||
| 
						 | 
					33b1027c76 | ||
| 
						 | 
					b45f9260bf | ||
| 
						 | 
					ee1a8d7b41 | ||
| 
						 | 
					ba19a35b6b | ||
| 
						 | 
					cc8e7dd355 | ||
| 
						 | 
					f52840623c | ||
| 
						 | 
					97d4a38e01 | ||
| 
						 | 
					60ccd87d6e | ||
| 
						 | 
					2477737fff | ||
| 
						 | 
					a360daeff9 | ||
| 
						 | 
					82d4d725ae | ||
| 
						 | 
					1e585d7991 | ||
| 
						 | 
					f849766998 | ||
| 
						 | 
					f4818671e4 | 
							
								
								
									
										164
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								.drone.yml
									
									
									
									
									
								
							@@ -114,6 +114,17 @@ steps:
 | 
			
		||||
        - push
 | 
			
		||||
        - pull_request
 | 
			
		||||
 | 
			
		||||
  - name: tag-pre-condition
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: alpine/git
 | 
			
		||||
    commands:
 | 
			
		||||
      - git update-ref refs/heads/tag_test ${DRONE_COMMIT_SHA}
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - build
 | 
			
		||||
    when:
 | 
			
		||||
      event:
 | 
			
		||||
        - tag
 | 
			
		||||
 | 
			
		||||
  - name: tag-test
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: golang:1.12
 | 
			
		||||
@@ -122,7 +133,7 @@ steps:
 | 
			
		||||
    environment:
 | 
			
		||||
      TAGS: bindata
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - build
 | 
			
		||||
      - tag-pre-condition
 | 
			
		||||
    when:
 | 
			
		||||
      event:
 | 
			
		||||
        - tag
 | 
			
		||||
@@ -351,7 +362,7 @@ steps:
 | 
			
		||||
 | 
			
		||||
  - name: static
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: techknowlogick/xgo:latest
 | 
			
		||||
    image: techknowlogick/xgo:go-1.12.x
 | 
			
		||||
    commands:
 | 
			
		||||
      - export PATH=$PATH:$GOPATH/bin
 | 
			
		||||
      - make generate
 | 
			
		||||
@@ -442,7 +453,6 @@ trigger:
 | 
			
		||||
 | 
			
		||||
depends_on:
 | 
			
		||||
  - testing
 | 
			
		||||
  - translations
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
  - name: fetch-tags
 | 
			
		||||
@@ -453,7 +463,7 @@ steps:
 | 
			
		||||
 | 
			
		||||
  - name: static
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: techknowlogick/xgo:latest
 | 
			
		||||
    image: techknowlogick/xgo:go-1.12.x
 | 
			
		||||
    commands:
 | 
			
		||||
      - export PATH=$PATH:$GOPATH/bin
 | 
			
		||||
      - make generate
 | 
			
		||||
@@ -548,7 +558,7 @@ steps:
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
kind: pipeline
 | 
			
		||||
name: docker
 | 
			
		||||
name: docker-linux-amd64
 | 
			
		||||
 | 
			
		||||
platform:
 | 
			
		||||
  os: linux
 | 
			
		||||
@@ -558,6 +568,15 @@ workspace:
 | 
			
		||||
  base: /go
 | 
			
		||||
  path: src/code.gitea.io/gitea
 | 
			
		||||
 | 
			
		||||
depends_on:
 | 
			
		||||
  - testing
 | 
			
		||||
 | 
			
		||||
trigger:
 | 
			
		||||
  ref:
 | 
			
		||||
  - refs/heads/master
 | 
			
		||||
  - "refs/tags/**"
 | 
			
		||||
  - "refs/pull/**"
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
  - name: fetch-tags
 | 
			
		||||
    pull: default
 | 
			
		||||
@@ -571,56 +590,121 @@ steps:
 | 
			
		||||
 | 
			
		||||
  - name: dryrun
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: plugins/docker:18.09
 | 
			
		||||
    image: plugins/docker:linux-amd64
 | 
			
		||||
    settings:
 | 
			
		||||
      cache_from: gitea/gitea
 | 
			
		||||
      dry_run: true
 | 
			
		||||
      repo: gitea/gitea
 | 
			
		||||
      tags: linux-amd64
 | 
			
		||||
    when:
 | 
			
		||||
      event:
 | 
			
		||||
        - pull_request
 | 
			
		||||
 | 
			
		||||
  - name: release
 | 
			
		||||
  - name: publish
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: plugins/docker:18.09
 | 
			
		||||
    image: plugins/docker:linux-amd64
 | 
			
		||||
    settings:
 | 
			
		||||
      cache_from: gitea/gitea
 | 
			
		||||
      auto_tag: true
 | 
			
		||||
      auto_tag_suffix: linux-amd64
 | 
			
		||||
      repo: gitea/gitea
 | 
			
		||||
      tags:
 | 
			
		||||
        - "${DRONE_BRANCH##release/v}"
 | 
			
		||||
    environment:
 | 
			
		||||
      DOCKER_PASSWORD:
 | 
			
		||||
      password:
 | 
			
		||||
        from_secret: docker_password
 | 
			
		||||
      DOCKER_USERNAME:
 | 
			
		||||
      username:
 | 
			
		||||
        from_secret: docker_username
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - dryrun
 | 
			
		||||
    when:
 | 
			
		||||
      branch:
 | 
			
		||||
        - "release/*"
 | 
			
		||||
      event:
 | 
			
		||||
        - push
 | 
			
		||||
        exclude:
 | 
			
		||||
        - pull_request
 | 
			
		||||
 | 
			
		||||
  - name: latest
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: plugins/docker:18.09
 | 
			
		||||
    settings:
 | 
			
		||||
      cache_from: gitea/gitea
 | 
			
		||||
      default_tags: true
 | 
			
		||||
      repo: gitea/gitea
 | 
			
		||||
    environment:
 | 
			
		||||
      DOCKER_PASSWORD:
 | 
			
		||||
        from_secret: docker_password
 | 
			
		||||
      DOCKER_USERNAME:
 | 
			
		||||
        from_secret: docker_username
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - dryrun
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
kind: pipeline
 | 
			
		||||
name: docker-linux-arm64
 | 
			
		||||
 | 
			
		||||
platform:
 | 
			
		||||
  os: linux
 | 
			
		||||
  arch: arm64
 | 
			
		||||
 | 
			
		||||
workspace:
 | 
			
		||||
  base: /go
 | 
			
		||||
  path: src/code.gitea.io/gitea
 | 
			
		||||
 | 
			
		||||
depends_on:
 | 
			
		||||
  - testing
 | 
			
		||||
 | 
			
		||||
trigger:
 | 
			
		||||
  ref:
 | 
			
		||||
  - refs/heads/master
 | 
			
		||||
  - "refs/tags/**"
 | 
			
		||||
  - "refs/pull/**"
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
  - name: fetch-tags
 | 
			
		||||
    pull: default
 | 
			
		||||
    image: docker:git
 | 
			
		||||
    commands:
 | 
			
		||||
      - git fetch --tags --force
 | 
			
		||||
    when:
 | 
			
		||||
      branch:
 | 
			
		||||
        - master
 | 
			
		||||
      event:
 | 
			
		||||
        - push
 | 
			
		||||
        - tag
 | 
			
		||||
        exclude:
 | 
			
		||||
          - pull_request
 | 
			
		||||
 | 
			
		||||
  - name: dryrun
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: plugins/docker:linux-arm64
 | 
			
		||||
    settings:
 | 
			
		||||
      dry_run: true
 | 
			
		||||
      repo: gitea/gitea
 | 
			
		||||
      tags: linux-arm64
 | 
			
		||||
    when:
 | 
			
		||||
      event:
 | 
			
		||||
        - pull_request
 | 
			
		||||
 | 
			
		||||
  - name: publish
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: plugins/docker:linux-arm64
 | 
			
		||||
    settings:
 | 
			
		||||
      auto_tag: true
 | 
			
		||||
      auto_tag_suffix: linux-arm64
 | 
			
		||||
      repo: gitea/gitea
 | 
			
		||||
      password:
 | 
			
		||||
        from_secret: docker_password
 | 
			
		||||
      username:
 | 
			
		||||
        from_secret: docker_username
 | 
			
		||||
    when:
 | 
			
		||||
      event:
 | 
			
		||||
        exclude:
 | 
			
		||||
        - pull_request
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
kind: pipeline
 | 
			
		||||
name: docker-manifest
 | 
			
		||||
 | 
			
		||||
platform:
 | 
			
		||||
  os: linux
 | 
			
		||||
  arch: amd64
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
  - name: manifest
 | 
			
		||||
    pull: always
 | 
			
		||||
    image: plugins/manifest
 | 
			
		||||
    settings:
 | 
			
		||||
      auto_tag: true
 | 
			
		||||
      ignore_missing: true
 | 
			
		||||
      spec: docker/manifest.tmpl
 | 
			
		||||
      password:
 | 
			
		||||
        from_secret: docker_password
 | 
			
		||||
      username:
 | 
			
		||||
        from_secret: docker_username
 | 
			
		||||
 | 
			
		||||
trigger:
 | 
			
		||||
  ref:
 | 
			
		||||
  - refs/heads/master
 | 
			
		||||
  - "refs/tags/**"
 | 
			
		||||
 | 
			
		||||
depends_on:
 | 
			
		||||
  - docker-linux-amd64
 | 
			
		||||
  - docker-linux-arm64
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
kind: pipeline
 | 
			
		||||
@@ -644,7 +728,9 @@ depends_on:
 | 
			
		||||
  - translations
 | 
			
		||||
  - release-version
 | 
			
		||||
  - release-master
 | 
			
		||||
  - docker
 | 
			
		||||
  - docker-linux-amd64
 | 
			
		||||
  - docker-linux-arm64
 | 
			
		||||
  - docker-manifest
 | 
			
		||||
  - docs
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										158
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -4,9 +4,129 @@ This changelog goes through all the changes that have been made in each release
 | 
			
		||||
without substantial changes to our git log; to see the highlights of what has
 | 
			
		||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
			
		||||
 | 
			
		||||
## [1.9.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.9.0-rc1) - 2019-07-06
 | 
			
		||||
## [1.9.6](https://github.com/go-gitea/gitea/releases/tag/v1.9.6) - 2019-11-13
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Allow to merge if file path contains " or \ (#8629) (#8772)
 | 
			
		||||
  * Fix 500 when edit hook (#8782) (#8790)
 | 
			
		||||
  * Fix issue with user.fullname (#8904)
 | 
			
		||||
  * Update Github Migration Test (#8897) (#8946)
 | 
			
		||||
  * Add Close() method to gogitRepository (#8901) (#8958)
 | 
			
		||||
 | 
			
		||||
## [1.9.5](https://github.com/go-gitea/gitea/releases/tag/v1.9.5) - 2019-10-30
 | 
			
		||||
* BREAKING
 | 
			
		||||
  * Hide some user information via API if user doesn't have enough permission (#8655) (#8658)
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Fix milestone close timestamp (#8728) (#8731)
 | 
			
		||||
  * Fix deadline on update issue or PR via API (#8699)
 | 
			
		||||
  * Fix 'New Issue Missing Milestone Comment' (#8678) (#8682)
 | 
			
		||||
  * Fix 500 when getting user as unauthenticated user (#8653) (#8662)
 | 
			
		||||
  * Use AppSubUrl for more redirections (#8647) (#8652)
 | 
			
		||||
  * Add SubURL to redirect path (#8632) (#8634) (#8640)
 | 
			
		||||
  * Fix #8582 by handling empty repos (#8587) (#8593)
 | 
			
		||||
  * Fix bug on pull requests when transfer head repository (#8571)
 | 
			
		||||
  * Add missed close in ServeBlobLFS (#8527) (#8543)
 | 
			
		||||
  * Return false if provided branch name is empty for IsBranchExist (#8485) (#8492)
 | 
			
		||||
  * Create .ssh dir as necessary (#8369) (#8486) (#8489)
 | 
			
		||||
  * Restore functionality for early gits (#7775) (#8476)
 | 
			
		||||
  * Add check for empty set when dropping indexes during migration (#8475)
 | 
			
		||||
  * Ensure Request Body Readers are closed in LFS server (#8454) (#8459)
 | 
			
		||||
  * Ensure that LFS files are relative to the LFS content path (#8455) (#8458)
 | 
			
		||||
* SECURITY
 | 
			
		||||
  * Ignore mentions for users with no access (#8395) (#8484)
 | 
			
		||||
* TESTING
 | 
			
		||||
  * Update heatmap fixtures to restore tests (#8615) (#8617)
 | 
			
		||||
 | 
			
		||||
## [1.9.4](https://github.com/go-gitea/gitea/releases/tag/v1.9.4) - 2019-10-08
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Highlight issue references (#8101) (#8404)
 | 
			
		||||
  * Fix bug when migrating a private repository #7917 (#8403)
 | 
			
		||||
  * Change general form binding to gogs form (#8334) (#8402)
 | 
			
		||||
  * Fix editor commit to new branch if PR disabled (#8375) (#8401)
 | 
			
		||||
  * Fix milestone num_issues (#8221) (#8400)
 | 
			
		||||
  * Allow users with explicit read access to give approvals (#8398)
 | 
			
		||||
  * Fix commit status in PR #8316 and PR #8321 (#8339)
 | 
			
		||||
  * Fix API for edit and delete release attachment (#8290)
 | 
			
		||||
  * Fix assets on release webhook (#8283)
 | 
			
		||||
  * Fix release API URL generation (#8239)
 | 
			
		||||
  * Allow registration when button is hidden (#8238)
 | 
			
		||||
  * MS Teams webhook misses commit messages (backport v1.9) (#8225)
 | 
			
		||||
  * Fix data race (#8206)
 | 
			
		||||
  * Fix pull merge 500 error caused by git-fetch breaking behaviors (#8194)
 | 
			
		||||
  * Fix the SSH config specification in the authorized_keys template (#8193)
 | 
			
		||||
  * Fix reading git notes from nested trees (#8189)
 | 
			
		||||
  * Fix team user api (#8172) (#8188)
 | 
			
		||||
  * Add reviewers as participants (#8124)
 | 
			
		||||
* BUILD
 | 
			
		||||
  * Use vendored go-swagger (#8087) (#8165)
 | 
			
		||||
  * Fix version-validation for GO 1.13 (go-macaron/cors) (#8389)
 | 
			
		||||
* MISC
 | 
			
		||||
  * Make show private icon when repo avatar set (#8144) (#8175)
 | 
			
		||||
 | 
			
		||||
## [1.9.3](https://github.com/go-gitea/gitea/releases/tag/v1.9.3) - 2019-09-06
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Fix go get from a private repository with Go 1.13 (#8100)
 | 
			
		||||
  * Strict name matching for Repository.GetTagID() (#8082)
 | 
			
		||||
  * Avoid ambiguity of branch/directory names for the git-diff-tree command (#8070)
 | 
			
		||||
  * Add change title notification for issues (#8064)
 | 
			
		||||
  * Run CORS handler first for /api routes (#7967) (#8053)
 | 
			
		||||
  * Evaluate emojis in commit messages in list view (#8044)
 | 
			
		||||
  * Fix failed to synchronize tags to releases for repository (#7990) (#7994)
 | 
			
		||||
  * Fix adding default Telegram webhook (#7972) (#7992)
 | 
			
		||||
  * Abort synchronization from LDAP source if there is some error (#7965)
 | 
			
		||||
  * Fix deformed emoji in commit message (#8071)
 | 
			
		||||
* ENHANCEMENT
 | 
			
		||||
  * Keep blame view buttons sequence consistent with normal view when viewing a file (#8007) (#8009)
 | 
			
		||||
 | 
			
		||||
## [1.9.2](https://github.com/go-gitea/gitea/releases/tag/v1.9.2) - 2019-08-22
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Fix wrong sender when send slack webhook (#7918) (#7924)
 | 
			
		||||
  * Upload support text/plain; charset=utf8 (#7899)
 | 
			
		||||
  * Lfs/lock: round locked_at timestamp to second (#7872) (#7875)
 | 
			
		||||
  * Fix non existent milestone with 500 error (#7867) (#7873)
 | 
			
		||||
* SECURITY
 | 
			
		||||
  * Fix No PGP signature on 1.9.1 tag (#7874)
 | 
			
		||||
  * Release built with go 1.12.9 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!msg/golang-announce/oeMaeUnkvVE/a49yvTLqAAAJ
 | 
			
		||||
* ENHANCEMENT
 | 
			
		||||
  * Fix pull creation with empty changes (#7920) (#7926)
 | 
			
		||||
* BUILD
 | 
			
		||||
  * Drone/docker: prepare multi-arch release + provide arm64 image (#7571) (#7884)
 | 
			
		||||
 | 
			
		||||
## [1.9.1](https://github.com/go-gitea/gitea/releases/tag/v1.9.1) - 2019-08-14
 | 
			
		||||
* BREAKING
 | 
			
		||||
  * Add pagination for admin api get orgs and fix only list public orgs bug (#7742) (#7752)
 | 
			
		||||
* SECURITY
 | 
			
		||||
  * Be more strict with git arguments (#7715) (#7762)
 | 
			
		||||
  * Release built with go 1.12.8 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!topic/golang-nuts/fCQWxqxP8aA
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Fix local runs of ssh-requiring integration tests (#7855) (#7857)
 | 
			
		||||
  * Fix hook problem (#7856) (#7754)
 | 
			
		||||
  * Use .ExpiredUnix.IsZero to display green color of forever valid gpg key (#7850) (#7846)
 | 
			
		||||
  * Do not fetch all refs (#7797) (#7837)
 | 
			
		||||
  * Fix duplicate call of webhook (#7824) (#7821)
 | 
			
		||||
  * Enable switching to a different source branch when PR already exists (#7823)
 | 
			
		||||
  * Rewrite existing repo units if setting is not included in api body (#7811)
 | 
			
		||||
  * Prevent Commit Status and Message From Overflowing On Branch Page (#7800) (#7808)
 | 
			
		||||
  * API: fix multiple bugs with statuses endpoints (Backport #7785) (#7807)
 | 
			
		||||
  * Fix Slack webhook fork message (1.9 release backport) (#7783)
 | 
			
		||||
  * Fix approvals counting (#7757) (#7777)
 | 
			
		||||
  * Fix rename failed when rewrite public keys (#7761) (#7769)
 | 
			
		||||
  * Fix dropTableColumns sqlite implementation (#7710) (#7765)
 | 
			
		||||
  * Fix repo_index_status lingering when deleting a repository (#7738)
 | 
			
		||||
  * Fix milestone completness calculation when migrating (#7725) (#7732)
 | 
			
		||||
  * Fixes indexed repos keeping outdated indexes when files grow too large (#7731)
 | 
			
		||||
  * Skip non-regular files (e.g. submodules) on repo indexing (#7717)
 | 
			
		||||
  * Improve branches list performance and fix protected branch icon when no-login (#7695) (#7704)
 | 
			
		||||
  * Correct wrong datetime format for git (#7689) (#7690)
 | 
			
		||||
 | 
			
		||||
## [1.9.0](https://github.com/go-gitea/gitea/releases/tag/v1.9.0) - 2019-07-30
 | 
			
		||||
* BREAKING
 | 
			
		||||
  * Better logging (#6038) (#6095)
 | 
			
		||||
* SECURITY
 | 
			
		||||
  * Shadow the password on cache and session config on admin panel (#7300)
 | 
			
		||||
  * Fix markdown invoke sequence (#7513) (#7560)
 | 
			
		||||
  * Reserve .well-known username (#7638)
 | 
			
		||||
  * Do not leak secrets via timing side channel (#7364)
 | 
			
		||||
  * Ensure that decryption of cookie actually suceeds (#7363)
 | 
			
		||||
* FEATURE
 | 
			
		||||
  * Content API for Creating, Updating, Deleting Files (#6314)
 | 
			
		||||
  * Enable tls-alpn-01: Use certmanager provided TLSConfig for LetsEncrypt (#7229)
 | 
			
		||||
@@ -29,6 +149,39 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
			
		||||
  * Implement Default Webhooks (#4299)
 | 
			
		||||
  * Telegram webhook (#4227)
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Send webhook after commit when creating issue with assignees (#7681) (#7684)
 | 
			
		||||
  * Upgrade macaron/captcha to fix random error problem (#7407) (#7683)
 | 
			
		||||
  * Move add to hook queue for created repo to outside xorm session. (#7682) (#7675)
 | 
			
		||||
  * Show protection symbol if needed on default branch (#7660) (#7668)
 | 
			
		||||
  * Hide delete/restore button on archived repos (#7660)
 | 
			
		||||
  * Fix bug on migrating milestone from github (#7665) (#7666) 
 | 
			
		||||
  * Use flex to fix floating paginate (#7656) (#7662)
 | 
			
		||||
  * Change length of some repository's columns (#7652) (#7655)
 | 
			
		||||
  * Fix wrong email when use gitea as OAuth2 provider (#7640) (#7647)
 | 
			
		||||
  * Fix syntax highlight initialization (#7617) (#7626) 
 | 
			
		||||
  * Fix bug create/edit wiki pages when code master branch protected (#7580) (#7623)
 | 
			
		||||
  * Fix panic on push at #7611 (#7615) (#7618)
 | 
			
		||||
  * Handle ErrUserProhibitLogin in http git (#7586, #7591) (#7590) 
 | 
			
		||||
  * Fix color of split-diff view in dark theme (#7587) (#7589)
 | 
			
		||||
  * Fix file header overflow in file and blame views (#7562) (#7579) 
 | 
			
		||||
  * Malformed URLs in API git/commits response (#7565) (#7567)
 | 
			
		||||
  * Fix empty commits now showing in repo overview (#7521) (#7563)
 | 
			
		||||
  * Fix repository's pull request count error (#7518) (#7524) 
 | 
			
		||||
  * Remove duplicated webhook trigger (#7511) (#7516) 
 | 
			
		||||
  * Handles all redirects for Web UI File CRUD (#7478) (#7507)
 | 
			
		||||
  * Fix regex for issues in commit messages (#7444) (#7466)
 | 
			
		||||
  * cmd/serv: actually exit after fatal errors (#7458) (#7460)
 | 
			
		||||
  * Fix an issue with some pages throwing 'not defined' js exceptions #7450 (#7453)
 | 
			
		||||
  * Fix Dropzone.js integration (#7445) (#7448)
 | 
			
		||||
  * Create class for inline positioned lists (#7439) (#7393)
 | 
			
		||||
  * Diff: Fix indentation on unhighlighted code (#7435) (#7443)
 | 
			
		||||
  * jQuery 3 (#7442) (#7425)
 | 
			
		||||
  * Only show "New Pull Request" button if repo allows pulls (#7426) (#7432)
 | 
			
		||||
  * Fix vendor references (#7394) (#7396)
 | 
			
		||||
  * Only return head: null if source branch was deleted (#6705) (#7376)
 | 
			
		||||
  * Add missing template variable on organisation settings (#7386) (#7385)
 | 
			
		||||
  * Fix post parameter on issue list which had unset assignee (#7380) (#7383)
 | 
			
		||||
  * Fix migration tests due to issue 7 being resolved (#7375) (#7381)
 | 
			
		||||
  * Correctly adjust mirror url (#6593)
 | 
			
		||||
  * Handle early git version's lack of get-url (#7065)
 | 
			
		||||
  * Fix icon position in issue view (#7354)
 | 
			
		||||
@@ -166,6 +319,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
			
		||||
  * Disable benchmarking during tag events on DroneIO (#6365)
 | 
			
		||||
  * Comments list performance optimization (#5305)
 | 
			
		||||
* ENHANCEMENT
 | 
			
		||||
  * Update Drone docker generation to standard format (#7480) (#7496) (#7504)
 | 
			
		||||
  * Add API Endpoint for Repo Edit (#7006)
 | 
			
		||||
  * Add state param to milestone listing API (#7131)
 | 
			
		||||
  * Make captcha and password optional for external accounts (#6606)
 | 
			
		||||
@@ -285,8 +439,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
			
		||||
  * Refactor: append, build variable and type switch (#4940)
 | 
			
		||||
  * Git statistics in Activity tab (#4724)
 | 
			
		||||
  * Drop the bits argument when generating an ed25519 key (#6504)
 | 
			
		||||
* SECURITY
 | 
			
		||||
  * Shadow the password on cache and session config on admin panel (#7300)
 | 
			
		||||
* TESTING
 | 
			
		||||
  * Exclude pull_request from fetch-tags step, fixes #7108 (#7120)
 | 
			
		||||
  * Refactor and improve git test (#7086)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Makefile
									
									
									
									
									
								
							@@ -48,6 +48,8 @@ TAGS ?=
 | 
			
		||||
 | 
			
		||||
TMPDIR := $(shell mktemp -d 2>/dev/null || mktemp -d -t 'gitea-temp')
 | 
			
		||||
 | 
			
		||||
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger@v0.20.1
 | 
			
		||||
SWAGGER := GO111MODULE=on $(GO) run -mod=vendor github.com/go-swagger/go-swagger/cmd/swagger
 | 
			
		||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
 | 
			
		||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl}}/api/v1"|g
 | 
			
		||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl}}/api/v1"|"basePath": "/api/v1"|g
 | 
			
		||||
@@ -101,10 +103,7 @@ generate:
 | 
			
		||||
 | 
			
		||||
.PHONY: generate-swagger
 | 
			
		||||
generate-swagger:
 | 
			
		||||
	@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
			
		||||
		GO111MODULE="on" $(GO) get -u github.com/go-swagger/go-swagger/cmd/swagger@v0.19.0; \
 | 
			
		||||
	fi
 | 
			
		||||
	swagger generate spec -o './$(SWAGGER_SPEC)'
 | 
			
		||||
	$(SWAGGER) generate spec -o './$(SWAGGER_SPEC)'
 | 
			
		||||
	$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
 | 
			
		||||
	$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
 | 
			
		||||
 | 
			
		||||
@@ -119,11 +118,8 @@ swagger-check: generate-swagger
 | 
			
		||||
 | 
			
		||||
.PHONY: swagger-validate
 | 
			
		||||
swagger-validate:
 | 
			
		||||
	@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
			
		||||
		$(GO) get -u github.com/go-swagger/go-swagger/cmd/swagger; \
 | 
			
		||||
	fi
 | 
			
		||||
	$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
 | 
			
		||||
	swagger validate './$(SWAGGER_SPEC)'
 | 
			
		||||
	$(SWAGGER) validate './$(SWAGGER_SPEC)'
 | 
			
		||||
	$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
 | 
			
		||||
 | 
			
		||||
.PHONY: errcheck
 | 
			
		||||
 
 | 
			
		||||
@@ -374,17 +374,20 @@ func runRepoSyncReleases(c *cli.Context) error {
 | 
			
		||||
 | 
			
		||||
			if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
 | 
			
		||||
				log.Warn(" SyncReleasesWithTags: %v", err)
 | 
			
		||||
				gitRepo.Close()
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			count, err = getReleaseCount(repo.ID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Warn(" GetReleaseCountByRepoID: %v", err)
 | 
			
		||||
				gitRepo.Close()
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			log.Trace(" repo %s releases synchronized to tags: from %d to %d",
 | 
			
		||||
				repo.FullName(), oldnum, count)
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -96,6 +96,7 @@ func runHookPreReceive(c *cli.Context) error {
 | 
			
		||||
				UserID:                          userID,
 | 
			
		||||
				GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
 | 
			
		||||
				GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 | 
			
		||||
				GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
 | 
			
		||||
				ProtectedBranchID:               prID,
 | 
			
		||||
			})
 | 
			
		||||
			switch statusCode {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,6 @@ func fail(userMessage, logMessage string, args ...interface{}) {
 | 
			
		||||
		if !setting.ProdMode {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								docker/manifest.tmpl
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								docker/manifest.tmpl
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
 | 
			
		||||
{{#if build.tags}}
 | 
			
		||||
tags:
 | 
			
		||||
{{#each build.tags}}
 | 
			
		||||
  - {{this}}
 | 
			
		||||
{{/each}}
 | 
			
		||||
{{/if}}
 | 
			
		||||
manifests:
 | 
			
		||||
  -
 | 
			
		||||
    image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
 | 
			
		||||
    platform:
 | 
			
		||||
      architecture: amd64
 | 
			
		||||
      os: linux
 | 
			
		||||
  -
 | 
			
		||||
    image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
 | 
			
		||||
    platform:
 | 
			
		||||
      architecture: arm64
 | 
			
		||||
      os: linux
 | 
			
		||||
      variant: v8
 | 
			
		||||
@@ -67,6 +67,7 @@ type Uploader interface {
 | 
			
		||||
	CreateComment(issueNumber int64, comment *Comment) error
 | 
			
		||||
	CreatePullRequest(pr *PullRequest) error
 | 
			
		||||
	Rollback() error
 | 
			
		||||
	Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										55
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								go.mod
									
									
									
									
									
								
							@@ -3,7 +3,6 @@ module code.gitea.io/gitea
 | 
			
		||||
go 1.12
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/BurntSushi/toml v0.3.1 // indirect
 | 
			
		||||
	github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34
 | 
			
		||||
	github.com/RoaringBitmap/roaring v0.4.7 // indirect
 | 
			
		||||
	github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca
 | 
			
		||||
@@ -17,18 +16,18 @@ require (
 | 
			
		||||
	github.com/blevesearch/go-porterstemmer v0.0.0-20141230013033-23a2c8e5cf1f // indirect
 | 
			
		||||
	github.com/blevesearch/segment v0.0.0-20160105220820-db70c57796cc // indirect
 | 
			
		||||
	github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26 // indirect
 | 
			
		||||
	github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a // indirect
 | 
			
		||||
	github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 // indirect
 | 
			
		||||
	github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f
 | 
			
		||||
	github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c // indirect
 | 
			
		||||
	github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a // indirect
 | 
			
		||||
	github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d // indirect
 | 
			
		||||
	github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b // indirect
 | 
			
		||||
	github.com/couchbase/vellum v0.0.0-20190111184608-e91b68ff3efe // indirect
 | 
			
		||||
	github.com/couchbaselabs/go-couchbase v0.0.0-20190117181324-d904413d884d // indirect
 | 
			
		||||
	github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 // indirect
 | 
			
		||||
	github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
 | 
			
		||||
	github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
 | 
			
		||||
	github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
 | 
			
		||||
	github.com/denisenkom/go-mssqldb v0.0.0-20190121005146-b04fd42d9952
 | 
			
		||||
	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
			
		||||
	github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect
 | 
			
		||||
	github.com/edsrzf/mmap-go v1.0.0 // indirect
 | 
			
		||||
	github.com/emirpasic/gods v1.12.0
 | 
			
		||||
	github.com/etcd-io/bbolt v1.3.2 // indirect
 | 
			
		||||
	github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a
 | 
			
		||||
@@ -40,13 +39,13 @@ require (
 | 
			
		||||
	github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
 | 
			
		||||
	github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 // indirect
 | 
			
		||||
	github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
 | 
			
		||||
	github.com/gliderlabs/ssh v0.1.4 // indirect
 | 
			
		||||
	github.com/gliderlabs/ssh v0.2.2 // indirect
 | 
			
		||||
	github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd // indirect
 | 
			
		||||
	github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e // indirect
 | 
			
		||||
	github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443
 | 
			
		||||
	github.com/go-macaron/cache v0.0.0-20151013081102-561735312776
 | 
			
		||||
	github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab
 | 
			
		||||
	github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9
 | 
			
		||||
	github.com/go-macaron/captcha v0.0.0-20190710000913-8dc5911259df
 | 
			
		||||
	github.com/go-macaron/cors v0.0.0-20190418220122-6fd6a9bfe14e
 | 
			
		||||
	github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372
 | 
			
		||||
	github.com/go-macaron/i18n v0.0.0-20160612092837-ef57533c3b0f
 | 
			
		||||
	github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191
 | 
			
		||||
@@ -54,12 +53,13 @@ require (
 | 
			
		||||
	github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90
 | 
			
		||||
	github.com/go-redis/redis v6.15.2+incompatible
 | 
			
		||||
	github.com/go-sql-driver/mysql v1.4.1
 | 
			
		||||
	github.com/go-swagger/go-swagger v0.20.1
 | 
			
		||||
	github.com/go-xorm/core v0.6.0 // indirect
 | 
			
		||||
	github.com/go-xorm/xorm v0.7.3-0.20190620151208-f1b4f8368459
 | 
			
		||||
	github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561
 | 
			
		||||
	github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183
 | 
			
		||||
	github.com/google/go-cmp v0.3.0 // indirect
 | 
			
		||||
	github.com/google/go-github/v24 v24.0.1
 | 
			
		||||
	github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect
 | 
			
		||||
	github.com/gorilla/context v1.1.1
 | 
			
		||||
	github.com/issue9/assert v1.3.2 // indirect
 | 
			
		||||
	github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c
 | 
			
		||||
@@ -72,7 +72,7 @@ require (
 | 
			
		||||
	github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc // indirect
 | 
			
		||||
	github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 // indirect
 | 
			
		||||
	github.com/lafriks/xormstore v1.0.0
 | 
			
		||||
	github.com/lib/pq v1.1.0
 | 
			
		||||
	github.com/lib/pq v1.2.0
 | 
			
		||||
	github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
 | 
			
		||||
	github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e
 | 
			
		||||
	github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de // indirect
 | 
			
		||||
@@ -80,16 +80,19 @@ require (
 | 
			
		||||
	github.com/markbates/goth v1.49.0
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.7
 | 
			
		||||
	github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.10.0
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.11.0
 | 
			
		||||
	github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75
 | 
			
		||||
	github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a
 | 
			
		||||
	github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae // indirect
 | 
			
		||||
	github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc
 | 
			
		||||
	github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5
 | 
			
		||||
	github.com/oliamb/cutter v0.2.2
 | 
			
		||||
	github.com/onsi/ginkgo v1.8.0 // indirect
 | 
			
		||||
	github.com/onsi/gomega v1.5.0 // indirect
 | 
			
		||||
	github.com/philhofer/fwd v1.0.0 // indirect
 | 
			
		||||
	github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e
 | 
			
		||||
	github.com/prometheus/client_golang v0.9.3
 | 
			
		||||
	github.com/prometheus/client_golang v1.1.0
 | 
			
		||||
	github.com/prometheus/procfs v0.0.4 // indirect
 | 
			
		||||
	github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
 | 
			
		||||
	github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff
 | 
			
		||||
	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
 | 
			
		||||
@@ -99,28 +102,31 @@ require (
 | 
			
		||||
	github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc // indirect
 | 
			
		||||
	github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
 | 
			
		||||
	github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d // indirect
 | 
			
		||||
	github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff // indirect
 | 
			
		||||
	github.com/smartystreets/assertions v1.0.1 // indirect
 | 
			
		||||
	github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
 | 
			
		||||
	github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.3.0
 | 
			
		||||
	github.com/stretchr/testify v1.4.0
 | 
			
		||||
	github.com/syndtr/goleveldb v1.0.0 // indirect
 | 
			
		||||
	github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect
 | 
			
		||||
	github.com/tinylib/msgp v0.0.0-20180516164116-c8cf64dff200 // indirect
 | 
			
		||||
	github.com/tstranex/u2f v1.0.0
 | 
			
		||||
	github.com/urfave/cli v1.20.0
 | 
			
		||||
	github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 // indirect
 | 
			
		||||
	github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
 | 
			
		||||
	go.etcd.io/bbolt v1.3.2 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443
 | 
			
		||||
	golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759
 | 
			
		||||
	golang.org/x/sys v0.0.0-20190618155005-516e3c20635f
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472
 | 
			
		||||
	golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
			
		||||
	golang.org/x/sys v0.0.0-20190904154756-749cb33beabd
 | 
			
		||||
	golang.org/x/text v0.3.2
 | 
			
		||||
	golang.org/x/tools v0.0.0-20190618163018-fdf1049a943a // indirect
 | 
			
		||||
	golang.org/x/tools v0.0.0-20190903163617-be0da057c5e3 // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.2 // indirect
 | 
			
		||||
	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 | 
			
		||||
	gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
 | 
			
		||||
	gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect
 | 
			
		||||
	gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0
 | 
			
		||||
	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
 | 
			
		||||
	gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0
 | 
			
		||||
	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 | 
			
		||||
	gopkg.in/ini.v1 v1.42.0
 | 
			
		||||
	gopkg.in/ini.v1 v1.46.0
 | 
			
		||||
	gopkg.in/ldap.v3 v3.0.2
 | 
			
		||||
	gopkg.in/macaron.v1 v1.3.2
 | 
			
		||||
	gopkg.in/redis.v2 v2.3.2 // indirect
 | 
			
		||||
@@ -128,11 +134,10 @@ require (
 | 
			
		||||
	gopkg.in/src-d/go-git.v4 v4.12.0
 | 
			
		||||
	gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
 | 
			
		||||
	gopkg.in/testfixtures.v2 v2.5.0
 | 
			
		||||
	gopkg.in/yaml.v2 v2.2.2 // indirect
 | 
			
		||||
	mvdan.cc/xurls/v2 v2.0.0
 | 
			
		||||
	strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
 | 
			
		||||
	xorm.io/builder v0.3.5
 | 
			
		||||
	xorm.io/core v0.6.3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
replace github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20180314172330-6a30f4e59a44
 | 
			
		||||
replace github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20180315180555-6a30f4e59a44
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										387
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										387
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,11 +1,23 @@
 | 
			
		||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 | 
			
		||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 | 
			
		||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 | 
			
		||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
 | 
			
		||||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8=
 | 
			
		||||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 | 
			
		||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
			
		||||
github.com/RoaringBitmap/roaring v0.4.7 h1:eGUudvFzvF7Kxh7JjYvXfI1f7l22/2duFby7r5+d4oc=
 | 
			
		||||
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
 | 
			
		||||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca h1:xU8R31tsvj6TesCBog973+UgI3TXjh/LqN5clki6hcc=
 | 
			
		||||
@@ -24,12 +36,18 @@ github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470 h1:4jHLmof+Hb
 | 
			
		||||
github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470/go.mod h1:3I+3V7B6gTBYfdpYgIG2ymALS9H+5VDKUl3lHH7ToM4=
 | 
			
		||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 | 
			
		||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 | 
			
		||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 | 
			
		||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
			
		||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
			
		||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
 | 
			
		||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 | 
			
		||||
github.com/blevesearch/bleve v0.0.0-20190214220507-05d86ea8f6e3 h1:vinCy/rcjbtxWnMiw11CbMKcuyNi+y4L4MbZUpk7m4M=
 | 
			
		||||
@@ -42,21 +60,35 @@ github.com/blevesearch/segment v0.0.0-20160105220820-db70c57796cc h1:7OfDAkuAGx7
 | 
			
		||||
github.com/blevesearch/segment v0.0.0-20160105220820-db70c57796cc/go.mod h1:IInt5XRvpiGE09KOk9mmCMLjHhydIhNPKPPFLFBB7L8=
 | 
			
		||||
github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26 h1:NGpwhs9FOwddM6TptNrq2ycby4s24TcppSe5uG4DA/Q=
 | 
			
		||||
github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
 | 
			
		||||
github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a h1:k5TuEkqEYCRs8+66WdOkswWOj+L/YbP5ruainvn94wg=
 | 
			
		||||
github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
 | 
			
		||||
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK+hNk4tyD+nuGjpVLPEHuJSFXMw11/HPA=
 | 
			
		||||
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 | 
			
		||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 | 
			
		||||
github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f h1:REH9VH5ubNR0skLaOxK7TRJeRbE2dDfvaouQo8FsRcA=
 | 
			
		||||
github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f/go.mod h1:6QaC0vFoKWYDth94dHFNgRT2YkT5FHdQp/Yx15aAAi0=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
 | 
			
		||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 | 
			
		||||
github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c h1:K4FIibkr4//ziZKOKmt4RL0YImuTjLLBtwElf+F2lSQ=
 | 
			
		||||
github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
 | 
			
		||||
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a h1:Y5XsLCEhtEI8qbD9RP3Qlv5FXdTDHxZM9UPUnMRgBp8=
 | 
			
		||||
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 | 
			
		||||
github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU=
 | 
			
		||||
github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs=
 | 
			
		||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 | 
			
		||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 | 
			
		||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d h1:XMf4E1U+b9E3ElF0mjvfXZdflBRZz4gLp16nQ/QSHQM=
 | 
			
		||||
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
 | 
			
		||||
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b h1:bZ9rKU2/V8sY+NulSfxDOnXTWcs1rySqdF1sVepihvo=
 | 
			
		||||
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 | 
			
		||||
github.com/couchbase/vellum v0.0.0-20190111184608-e91b68ff3efe h1:2o6Y7KMjJNsuMTF8f2H2eTKRhqH7+bQbjr+D+LnhE5M=
 | 
			
		||||
github.com/couchbase/vellum v0.0.0-20190111184608-e91b68ff3efe/go.mod h1:prYTC8EgTu3gwbqJihkud9zRXISvyulAplQ6exdCo1g=
 | 
			
		||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190117181324-d904413d884d h1:lsBRLJe/ET6DjCaRblGwls80dOcOzhFVNJrO6uaMrMQ=
 | 
			
		||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190117181324-d904413d884d/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
 | 
			
		||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 h1:1XjEY/gnjQ+AfXef2U6dxCquhiRzkEpxZuWqs+QxTL8=
 | 
			
		||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
 | 
			
		||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 | 
			
		||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
 | 
			
		||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
 | 
			
		||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
 | 
			
		||||
@@ -66,13 +98,16 @@ github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20180314172330-6a30f4e59a44 h1:x0uHqLQTSEL9LKic8sWDt3ASkq07ve5ojIIUl5uF64M=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20180314172330-6a30f4e59a44/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20180315180555-6a30f4e59a44 h1:DWxZh2sImfCFn/79OUBhzFkPTKnsdDzXH/JTxpw5n6w=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20180315180555-6a30f4e59a44/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
			
		||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
 | 
			
		||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 | 
			
		||||
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
			
		||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
 | 
			
		||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 | 
			
		||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 | 
			
		||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
			
		||||
github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0=
 | 
			
		||||
@@ -99,24 +134,28 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr
 | 
			
		||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.4 h1:5N8AYXpaQAPy0L7linKa5aI+WRfyYagAhjksVzxh+mI=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.4/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 | 
			
		||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 | 
			
		||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd h1:r04MMPyLHj/QwZuMJ5+7tJcBr1AQjpiAK/rZWRrQT7o=
 | 
			
		||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 | 
			
		||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e h1:SiEs4J3BKVIeaWrH3tKaz3QLZhJ68iJ/A4xrzIoE5+Y=
 | 
			
		||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443 h1:i801KPR7j76uRMLLlGVyb0hiYbgX1FM5+ur81TJWzIw=
 | 
			
		||||
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443/go.mod h1:u+H6rwW+HQwUL+w5uaEJSpIlVZDye1o9MB4Su0JfRfM=
 | 
			
		||||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776 h1:UYIHS1r0WotqB5cIa0PAiV0m6GzD9rDBcn4alp5JgCw=
 | 
			
		||||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776/go.mod h1:hHAsZm/oBZVcY+S7qdQL6Vbg5VrXF6RuKGuqsszt3Ok=
 | 
			
		||||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab h1:4VFhsA3GE5Wwq1Ymr8KWCmrOWi1wRLEgdj48LPfQjxI=
 | 
			
		||||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab/go.mod h1:j9TJ+0nwUOWBvNnm0bheHIPFf3cC62EQo7n7O6PbjZA=
 | 
			
		||||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9 h1:A0QGzY6UHHEil0I2e7C21JenNNG0mmrj5d9SFWTlgr8=
 | 
			
		||||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9/go.mod h1:utmMRnVIrXPSfA9MFcpIYKEpKawjKxf62vv62k4707E=
 | 
			
		||||
github.com/go-macaron/captcha v0.0.0-20190710000913-8dc5911259df h1:MdgvtI3Y1u/DHNj7xUGOqAv+KGoTikjy8xQtCm12L78=
 | 
			
		||||
github.com/go-macaron/captcha v0.0.0-20190710000913-8dc5911259df/go.mod h1:j9TJ+0nwUOWBvNnm0bheHIPFf3cC62EQo7n7O6PbjZA=
 | 
			
		||||
github.com/go-macaron/cors v0.0.0-20190418220122-6fd6a9bfe14e h1:auESkcVctNZnNl4EH0TuoCSJMJ7Q7ShU8FS6lDEsAC4=
 | 
			
		||||
github.com/go-macaron/cors v0.0.0-20190418220122-6fd6a9bfe14e/go.mod h1:utmMRnVIrXPSfA9MFcpIYKEpKawjKxf62vv62k4707E=
 | 
			
		||||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372 h1:acrx8CnDmlKl+BPoOOLEK9Ko+SrWFB5pxRuGkKj4iqo=
 | 
			
		||||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372/go.mod h1:oZGMxI7MBnicI0jJqJvH4qQzyrWKhtiKxLSJKHC+ydc=
 | 
			
		||||
github.com/go-macaron/i18n v0.0.0-20160612092837-ef57533c3b0f h1:wDKrZFc9pYJlqFOf7EzGbFMrSFFtyHt3plr2uTdo8Rg=
 | 
			
		||||
@@ -127,12 +166,62 @@ github.com/go-macaron/session v0.0.0-20190131233854-0a0a789bf193 h1:z/nqwd+ql/r6
 | 
			
		||||
github.com/go-macaron/session v0.0.0-20190131233854-0a0a789bf193/go.mod h1:ScEJm9Gk+ez5JJTml5WlBIqavAfuE5nF8e4Gvyz/X+A=
 | 
			
		||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90 h1:3wYKrRg9IjUMfaf3H0Hh7M5Li9ge79Y7aw2yujHa2jQ=
 | 
			
		||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90/go.mod h1:Ut/NmkIMGVYlEdJBzEZgWVWG5ZpYS9BLmUgXfAgi+qM=
 | 
			
		||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
 | 
			
		||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.4 h1:1TjOzrWkj+9BrjnM1yPAICbaoC0FyfD49oVkTBrSSa0=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 | 
			
		||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
			
		||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
			
		||||
github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
 | 
			
		||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
 | 
			
		||||
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
 | 
			
		||||
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 | 
			
		||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI=
 | 
			
		||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
 | 
			
		||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 | 
			
		||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
 | 
			
		||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 | 
			
		||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.2 h1:clPGfBnJohokno0e+d7hs6Yocrzjlgz6EsQSDncCRnE=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
 | 
			
		||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
			
		||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
			
		||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
			
		||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
 | 
			
		||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
			
		||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 | 
			
		||||
github.com/go-openapi/validate v0.19.2 h1:ky5l57HjyVRrsJfd2+Ro5Z9PjGuKbsmftwyMtk8H7js=
 | 
			
		||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
 | 
			
		||||
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
 | 
			
		||||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 | 
			
		||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 | 
			
		||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/go-swagger/go-swagger v0.20.1 h1:37XFujv7lYHLOKawfzLDg4STwwgB5zhPjodN33asJto=
 | 
			
		||||
github.com/go-swagger/go-swagger v0.20.1/go.mod h1:LoTpv6FHYXUvYnECHNLvi/qYNybk0d9wkJGH1cTANWE=
 | 
			
		||||
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0=
 | 
			
		||||
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0=
 | 
			
		||||
github.com/go-xorm/core v0.6.0 h1:tp6hX+ku4OD9khFZS8VGBDRY3kfVCtelPfmkgCyHxL0=
 | 
			
		||||
github.com/go-xorm/core v0.6.0/go.mod h1:d8FJ9Br8OGyQl12MCclmYBuBqqxsyeedpXciV5Myih8=
 | 
			
		||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
 | 
			
		||||
@@ -144,28 +233,58 @@ github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:YgYOrVn3N
 | 
			
		||||
github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183 h1:EBTlva3AOSb80G3JSwY6ZMdILEZJ1JKuewrbqrNjWuE=
 | 
			
		||||
github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183/go.mod h1:pX+V62FFmklia2fhP3P4YSY6iJdPO5jIDKFQ5fEd5QE=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 | 
			
		||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 | 
			
		||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
 | 
			
		||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 | 
			
		||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 | 
			
		||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
 | 
			
		||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
 | 
			
		||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 | 
			
		||||
github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4=
 | 
			
		||||
github.com/google/go-github/v24 v24.0.1/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvKU/XSS44u6X74M=
 | 
			
		||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 | 
			
		||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
			
		||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 | 
			
		||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 | 
			
		||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
 | 
			
		||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 | 
			
		||||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
 | 
			
		||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
 | 
			
		||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
 | 
			
		||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
 | 
			
		||||
@@ -174,6 +293,16 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
 | 
			
		||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
 | 
			
		||||
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE=
 | 
			
		||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.9.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
github.com/issue9/assert v1.3.2 h1:IaTa37u4m1fUuTH9K9ldO5IONKVDXjLiUO1T9vj0OF0=
 | 
			
		||||
@@ -195,6 +324,10 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U
 | 
			
		||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
 | 
			
		||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
 | 
			
		||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 | 
			
		||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
			
		||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
@@ -205,6 +338,9 @@ github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6 h1:9mszGwKDxHEY2cy+9XxCQKWIfkGPSAEFrcN8ghzyAKg=
 | 
			
		||||
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/klauspost/compress v0.0.0-20161025140425-8df558b6cb6f h1:tCnZKEmDovgV4jmsclh6CuKk9AMzTzyVWfejgkgccVg=
 | 
			
		||||
github.com/klauspost/compress v0.0.0-20161025140425-8df558b6cb6f/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
			
		||||
github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc h1:WW8B7p7QBnFlqRVv/k6ro/S8Z7tCnYjJHcQNScx9YVs=
 | 
			
		||||
@@ -212,18 +348,21 @@ github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc/go.mod h1:Pj4uuM52
 | 
			
		||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 h1:KAZ1BW2TCmT6PRihDPpocIy1QTtsAsrx6TneU/4+CMg=
 | 
			
		||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
			
		||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 | 
			
		||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/lafriks/xormstore v1.0.0 h1:P/IJzNSIpjXl/Up3o2Td5ZU/x4v6DEKLMaPQJGtmJCk=
 | 
			
		||||
github.com/lafriks/xormstore v1.0.0/go.mod h1:dD8vHNRfEp3Uy+JvX9cMi2SXcRKJ0x4pYKsZuy843Ic=
 | 
			
		||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 | 
			
		||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
 | 
			
		||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 | 
			
		||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
 | 
			
		||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
 | 
			
		||||
github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e h1:GSprKUrG9wNgwQgROvjPGXmcZrg4OLslOuZGB0uJjx8=
 | 
			
		||||
@@ -232,6 +371,14 @@ github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de h1:nyxwRdWHAVxpFcDThedEg
 | 
			
		||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
 | 
			
		||||
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af h1:UaWHNBdukWrSG3DRvHFR/hyfg681fceqQDYVTBncKfQ=
 | 
			
		||||
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
 | 
			
		||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 | 
			
		||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
 | 
			
		||||
github.com/markbates/goth v1.49.0 h1:qQ4Ti4WaqAxNAggOC+4s5M85sMVfMJwQn/Xkp73wfgI=
 | 
			
		||||
github.com/markbates/goth v1.49.0/go.mod h1:zZmAw0Es0Dpm7TT/4AdN14QrkiWLMrrU9Xei1o+/mdA=
 | 
			
		||||
@@ -241,6 +388,8 @@ github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZh
 | 
			
		||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 h1:Pijfgr7ZuvX7QIQiEwLdRVr3RoMG+i0SbBO1Qu+7yVk=
 | 
			
		||||
@@ -249,6 +398,12 @@ github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a h1:d18LCO3
 | 
			
		||||
github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA=
 | 
			
		||||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
 | 
			
		||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
 | 
			
		||||
@@ -256,6 +411,7 @@ github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE
 | 
			
		||||
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc h1:z1PgdCCmYYVL0BoJTUgmAq1p7ca8fzYIPsNyfsN3xAU=
 | 
			
		||||
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc/go.mod h1:np1wUFZ6tyoke22qDJZY40URn9Ae51gX7ljIWXN5TJs=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
 | 
			
		||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
 | 
			
		||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
			
		||||
@@ -264,10 +420,18 @@ github.com/oliamb/cutter v0.2.2/go.mod h1:4BenG2/4GuRBDbVm/OPahDVqbrOemzpPiG5mi1
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
 | 
			
		||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
 | 
			
		||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
 | 
			
		||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
			
		||||
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
 | 
			
		||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
 | 
			
		||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
			
		||||
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
 | 
			
		||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
 | 
			
		||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
@@ -275,25 +439,42 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 | 
			
		||||
github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e h1:ApqncJ84HYN8x8x5WV1T1YWDuPRF/0aXZhr91LnRMCQ=
 | 
			
		||||
github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
			
		||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 | 
			
		||||
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
 | 
			
		||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 | 
			
		||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
 | 
			
		||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
 | 
			
		||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
 | 
			
		||||
github.com/prometheus/procfs v0.0.4 h1:w8DjqFMJDjuVwdZBQoOozr4MVWOnwF7RcL/7uxBjY78=
 | 
			
		||||
github.com/prometheus/procfs v0.0.4/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
 | 
			
		||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 | 
			
		||||
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
 | 
			
		||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
 | 
			
		||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
			
		||||
github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff h1:g9ZlAHmkc/h5So+OjNCkZWh+FjuKEOOOoyRkqlGA8+c=
 | 
			
		||||
github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
			
		||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
 | 
			
		||||
@@ -313,14 +494,31 @@ github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJV
 | 
			
		||||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d h1:qQWKKOvHN7Q9c6GdmUteCef2F9ubxMpxY1IKwpIKz68=
 | 
			
		||||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
 | 
			
		||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff h1:86HlEv0yBCry9syNuylzqznKXDK11p6D0DT596yNMys=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 | 
			
		||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 | 
			
		||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
			
		||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
			
		||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 | 
			
		||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
 | 
			
		||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 | 
			
		||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
 | 
			
		||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
 | 
			
		||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 h1:JNEGSiWg6D3lcBCMCBqN3ELniXujt+0QNHLhNnO0w3s=
 | 
			
		||||
@@ -328,60 +526,121 @@ github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2/go.mod h1:mjqs7N0Q
 | 
			
		||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
			
		||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66 h1:AwmkkZT+TucFotNCL+aNJ/0KCMsRtlXN9fs8uoOMSRk=
 | 
			
		||||
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 | 
			
		||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
 | 
			
		||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 | 
			
		||||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 h1:HOxvxvnntLiPn123Fk+twfUhCQdMDaqmb0cclArW0T0=
 | 
			
		||||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
 | 
			
		||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
 | 
			
		||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 | 
			
		||||
github.com/tinylib/msgp v0.0.0-20180516164116-c8cf64dff200 h1:ZVvr38DYEyOPyelySqvF0I9I++85NnUMsWkroBDS4fs=
 | 
			
		||||
github.com/tinylib/msgp v0.0.0-20180516164116-c8cf64dff200/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
 | 
			
		||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
			
		||||
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
 | 
			
		||||
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
 | 
			
		||||
github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ=
 | 
			
		||||
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
 | 
			
		||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
			
		||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 | 
			
		||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 | 
			
		||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
 | 
			
		||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 | 
			
		||||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 h1:E8u341JM/N8LCnPXBV6ZFD1RKo/j+qHl1XOqSV+GstA=
 | 
			
		||||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 | 
			
		||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 | 
			
		||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
			
		||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 h1:HsIQ6yAjfjQ3IxPGrTusxp6Qxn92gNVq2x5CbvQvx3w=
 | 
			
		||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53/go.mod h1:f6elajwZV+xceiaqgRL090YzLEDGSbqr3poGL3ZgXYo=
 | 
			
		||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
 | 
			
		||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
 | 
			
		||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 | 
			
		||||
go.mongodb.org/mongo-driver v1.1.0 h1:aeOqSrhl9eDRAap/3T5pCfMBEBxZ0vuXBP+RMtp2KX8=
 | 
			
		||||
go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 | 
			
		||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
			
		||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
			
		||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
			
		||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190814143026-e8b3e6111d02/go.mod h1:z5wpDCy2wbnXyFdvEuY3LhY9gBUL86/IOILm+Hsjx+E=
 | 
			
		||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 | 
			
		||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
 | 
			
		||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
 | 
			
		||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759 h1:TMrx+Qdx7uJAeUbv15N72h5Hmyb5+VDjEiMufAEAM04=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
@@ -391,24 +650,74 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
 | 
			
		||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190618155005-516e3c20635f h1:dHNZYIYdq2QuU6w73vZ/DzesPbVlZVYZTtTZmrnsbQ8=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190618163018-fdf1049a943a h1:aQmaYPOmKItb96VioBrTlYay5tSNUdKAFEhPCWMeLSM=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190618163018-fdf1049a943a/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190820033707-85edb9ef3283/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190903163617-be0da057c5e3 h1:1cLrGl9PL64Mzl9NATDCqFE57dVYwWOkoPXvppEnjO4=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190903163617-be0da057c5e3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 | 
			
		||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 | 
			
		||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=
 | 
			
		||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/appengine v1.6.2 h1:j8RI1yW0SkI+paT6uGwMlrMI/6zwYA6/CFil8rxOzGI=
 | 
			
		||||
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
			
		||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
 | 
			
		||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
 | 
			
		||||
@@ -419,20 +728,27 @@ gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e/go.mod h1:xsQCaysVCudhrYTfz
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0 h1:CO465/foR4+bY1xNYjZEl6l8By1g/iMsImoruxfEt84=
 | 
			
		||||
gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0/go.mod h1:s2mQFI9McjArkyCwyEwU//+luQENTnD/Lfb/7Sj3/kQ=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0 h1:oxOEwvhxLMpWpN+0pb2r9TWrM0DCFBHxbuIlS27tmFg=
 | 
			
		||||
gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0/go.mod h1:s2mQFI9McjArkyCwyEwU//+luQENTnD/Lfb/7Sj3/kQ=
 | 
			
		||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 | 
			
		||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
 | 
			
		||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
 | 
			
		||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
			
		||||
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
 | 
			
		||||
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
			
		||||
gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
 | 
			
		||||
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
 | 
			
		||||
gopkg.in/macaron.v1 v1.3.2 h1:AvWIaPmwBUA87/OWzePkoxeaw6YJWDfBt1pDFPBnLf8=
 | 
			
		||||
gopkg.in/macaron.v1 v1.3.2/go.mod h1:PrsiawTWAGZs6wFbT5hlr7SQ2Ns9h7cUVtcUu4lQOVo=
 | 
			
		||||
gopkg.in/redis.v2 v2.3.2 h1:GPVIIB/JnL1wvfULefy3qXmPu1nfNu2d0yA09FHgwfs=
 | 
			
		||||
gopkg.in/redis.v2 v2.3.2/go.mod h1:4wl9PJ/CqzeHk3LVq1hNLHH8krm3+AXEgut4jVc++LU=
 | 
			
		||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
			
		||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
 | 
			
		||||
@@ -447,12 +763,19 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 | 
			
		||||
mvdan.cc/xurls/v2 v2.0.0 h1:r1zSOSNS/kqtpmATyMMMvaZ4/djsesbYz5kr0+qMRWc=
 | 
			
		||||
mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU=
 | 
			
		||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
			
		||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a h1:8q33ShxKXRwQ7JVd1ZnhIU3hZhwwn0Le+4fTeAackuM=
 | 
			
		||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
 | 
			
		||||
xorm.io/builder v0.3.5 h1:EilU39fvWDxjb1cDaELpYhsF+zziRBhew8xk4pngO+A=
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	gitRepo, err := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	err = gitRepo.CreateTag("v0.0.1", "master")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
@@ -112,6 +113,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	gitRepo, err := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	err = gitRepo.CreateTag("v0.0.1", "master")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -139,6 +139,7 @@ func TestAPICreateFile(t *testing.T) {
 | 
			
		||||
			assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
 | 
			
		||||
			assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
 | 
			
		||||
			assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Test creating a file in a new branch
 | 
			
		||||
 
 | 
			
		||||
@@ -143,6 +143,7 @@ func TestAPIUpdateFile(t *testing.T) {
 | 
			
		||||
			assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
 | 
			
		||||
			assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
 | 
			
		||||
			assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Test updating a file in a new branch
 | 
			
		||||
 
 | 
			
		||||
@@ -74,6 +74,8 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
 | 
			
		||||
	repo1.CreateNewBranch(user2, repo1.DefaultBranch, newBranch)
 | 
			
		||||
	// Get the commit ID of the default branch
 | 
			
		||||
	gitRepo, _ := git.OpenRepository(repo1.RepoPath())
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
 | 
			
		||||
	// Make a new tag in repo1
 | 
			
		||||
	newTag := "test_tag"
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,8 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
 | 
			
		||||
	repo1.CreateNewBranch(user2, repo1.DefaultBranch, newBranch)
 | 
			
		||||
	// Get the commit ID of the default branch
 | 
			
		||||
	gitRepo, _ := git.OpenRepository(repo1.RepoPath())
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
 | 
			
		||||
	// Make a new tag in repo1
 | 
			
		||||
	newTag := "test_tag"
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,8 @@ func TestAPIGitTags(t *testing.T) {
 | 
			
		||||
	git.NewCommand("config", "user.email", user.Email).RunInDir(repo.RepoPath())
 | 
			
		||||
 | 
			
		||||
	gitRepo, _ := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	commit, _ := gitRepo.GetBranchCommit("master")
 | 
			
		||||
	lTagName := "lightweightTag"
 | 
			
		||||
	gitRepo.CreateTag(lTagName, commit.ID.String())
 | 
			
		||||
 
 | 
			
		||||
@@ -104,8 +104,11 @@ func TestAPILFSLocksLogged(t *testing.T) {
 | 
			
		||||
		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks", test.repo.FullName()), map[string]string{"path": test.path})
 | 
			
		||||
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
 | 
			
		||||
		req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
 | 
			
		||||
		session.MakeRequest(t, req, test.httpResult)
 | 
			
		||||
		resp := session.MakeRequest(t, req, test.httpResult)
 | 
			
		||||
		if len(test.addTime) > 0 {
 | 
			
		||||
			var lfsLock api.LFSLockResponse
 | 
			
		||||
			DecodeJSON(t, resp, &lfsLock)
 | 
			
		||||
			assert.EqualValues(t, lfsLock.Lock.LockedAt.Format(time.RFC3339), lfsLock.Lock.LockedAt.Format(time.RFC3339Nano)) //locked at should be rounded to second
 | 
			
		||||
			for _, id := range test.addTime {
 | 
			
		||||
				resultsTests[id].locksTimes = append(resultsTests[id].locksTimes, time.Now())
 | 
			
		||||
			}
 | 
			
		||||
@@ -124,6 +127,7 @@ func TestAPILFSLocksLogged(t *testing.T) {
 | 
			
		||||
		for i, lock := range lfsLocks.Locks {
 | 
			
		||||
			assert.EqualValues(t, test.locksOwners[i].DisplayName(), lock.Owner.Name)
 | 
			
		||||
			assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 3*time.Second)
 | 
			
		||||
			assert.EqualValues(t, lock.LockedAt.Format(time.RFC3339), lock.LockedAt.Format(time.RFC3339Nano)) //locked at should be rounded to second
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/verify", test.repo.FullName()), map[string]string{})
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								integrations/api_team_user_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								integrations/api_team_user_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
// Copyright 2019 The Gitea 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 integrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
	"code.gitea.io/gitea/routers/api/v1/convert"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestAPITeamUser(t *testing.T) {
 | 
			
		||||
	prepareTestEnv(t)
 | 
			
		||||
 | 
			
		||||
	normalUsername := "user2"
 | 
			
		||||
	session := loginUser(t, normalUsername)
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
	req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1?token="+token)
 | 
			
		||||
	session.MakeRequest(t, req, http.StatusNotFound)
 | 
			
		||||
 | 
			
		||||
	req = NewRequest(t, "GET", "/api/v1/teams/1/members/user2?token="+token)
 | 
			
		||||
	resp := session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
	var user2 *api.User
 | 
			
		||||
	DecodeJSON(t, resp, &user2)
 | 
			
		||||
	user2.Created = user2.Created.In(time.Local)
 | 
			
		||||
	user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, convert.ToUser(user, true, false), user2)
 | 
			
		||||
}
 | 
			
		||||
@@ -24,7 +24,7 @@ func TestUserHeatmap(t *testing.T) {
 | 
			
		||||
	var heatmap []*models.UserHeatmapData
 | 
			
		||||
	DecodeJSON(t, resp, &heatmap)
 | 
			
		||||
	var dummyheatmap []*models.UserHeatmapData
 | 
			
		||||
	dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1540080000, Contributions: 1})
 | 
			
		||||
	dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1571616000, Contributions: 1})
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, dummyheatmap, heatmap)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,9 @@ import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -24,20 +26,29 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func withKeyFile(t *testing.T, keyname string, callback func(string)) {
 | 
			
		||||
	keyFile := filepath.Join(setting.AppDataPath, keyname)
 | 
			
		||||
	err := ssh.GenKeyPair(keyFile)
 | 
			
		||||
 | 
			
		||||
	tmpDir, err := ioutil.TempDir("", "key-file")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
 | 
			
		||||
	err = os.Chmod(tmpDir, 0700)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	keyFile := filepath.Join(tmpDir, keyname)
 | 
			
		||||
	err = ssh.GenKeyPair(keyFile)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	err = ioutil.WriteFile(path.Join(tmpDir, "ssh"), []byte("#!/bin/bash\n"+
 | 
			
		||||
		"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\" \"$@\""), 0700)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	//Setup ssh wrapper
 | 
			
		||||
	os.Setenv("GIT_SSH", path.Join(tmpDir, "ssh"))
 | 
			
		||||
	os.Setenv("GIT_SSH_COMMAND",
 | 
			
		||||
		"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "+
 | 
			
		||||
			filepath.Join(setting.AppWorkPath, keyFile))
 | 
			
		||||
		"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\"")
 | 
			
		||||
	os.Setenv("GIT_SSH_VARIANT", "ssh")
 | 
			
		||||
 | 
			
		||||
	callback(keyFile)
 | 
			
		||||
 | 
			
		||||
	defer os.RemoveAll(keyFile)
 | 
			
		||||
	defer os.RemoveAll(keyFile + ".pub")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createSSHUrl(gitPath string, u *url.URL) *url.URL {
 | 
			
		||||
@@ -49,6 +60,24 @@ func createSSHUrl(gitPath string, u *url.URL) *url.URL {
 | 
			
		||||
	return &u2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func allowLFSFilters() []string {
 | 
			
		||||
	// Now here we should explicitly allow lfs filters to run
 | 
			
		||||
	globalArgs := git.GlobalCommandArgs
 | 
			
		||||
	filteredLFSGlobalArgs := make([]string, len(git.GlobalCommandArgs))
 | 
			
		||||
	j := 0
 | 
			
		||||
	for _, arg := range git.GlobalCommandArgs {
 | 
			
		||||
		if strings.Contains(arg, "lfs") {
 | 
			
		||||
			j--
 | 
			
		||||
		} else {
 | 
			
		||||
			filteredLFSGlobalArgs[j] = arg
 | 
			
		||||
			j++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	filteredLFSGlobalArgs = filteredLFSGlobalArgs[:j]
 | 
			
		||||
	git.GlobalCommandArgs = filteredLFSGlobalArgs
 | 
			
		||||
	return globalArgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
 | 
			
		||||
	prepareTestEnv(t, 1)
 | 
			
		||||
	s := http.Server{
 | 
			
		||||
@@ -74,7 +103,9 @@ func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
 | 
			
		||||
 | 
			
		||||
func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
 | 
			
		||||
	return func(t *testing.T) {
 | 
			
		||||
		oldGlobals := allowLFSFilters()
 | 
			
		||||
		assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
 | 
			
		||||
		git.GlobalCommandArgs = oldGlobals
 | 
			
		||||
		assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -135,7 +166,9 @@ func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
 | 
			
		||||
 | 
			
		||||
func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
 | 
			
		||||
	return func(t *testing.T) {
 | 
			
		||||
		oldGlobals := allowLFSFilters()
 | 
			
		||||
		_, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath)
 | 
			
		||||
		git.GlobalCommandArgs = oldGlobals
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -149,7 +182,9 @@ func doGitMerge(dstPath string, args ...string) func(*testing.T) {
 | 
			
		||||
 | 
			
		||||
func doGitPull(dstPath string, args ...string) func(*testing.T) {
 | 
			
		||||
	return func(t *testing.T) {
 | 
			
		||||
		oldGlobals := allowLFSFilters()
 | 
			
		||||
		_, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath)
 | 
			
		||||
		git.GlobalCommandArgs = oldGlobals
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/git"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
@@ -135,6 +136,11 @@ func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string
 | 
			
		||||
func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
 | 
			
		||||
	t.Run("LFS", func(t *testing.T) {
 | 
			
		||||
		PrintCurrentTest(t)
 | 
			
		||||
		setting.CheckLFSVersion()
 | 
			
		||||
		if !setting.LFS.StartServer {
 | 
			
		||||
			t.Skip()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		prefix := "lfs-data-file-"
 | 
			
		||||
		_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
@@ -142,6 +148,21 @@ func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS strin
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		err = git.AddChanges(dstPath, false, ".gitattributes")
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		oldGlobals := allowLFSFilters()
 | 
			
		||||
		err = git.CommitChanges(dstPath, git.CommitChangesOptions{
 | 
			
		||||
			Committer: &git.Signature{
 | 
			
		||||
				Email: "user2@example.com",
 | 
			
		||||
				Name:  "User Two",
 | 
			
		||||
				When:  time.Now(),
 | 
			
		||||
			},
 | 
			
		||||
			Author: &git.Signature{
 | 
			
		||||
				Email: "user2@example.com",
 | 
			
		||||
				Name:  "User Two",
 | 
			
		||||
				When:  time.Now(),
 | 
			
		||||
			},
 | 
			
		||||
			Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
 | 
			
		||||
		})
 | 
			
		||||
		git.GlobalCommandArgs = oldGlobals
 | 
			
		||||
 | 
			
		||||
		littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix)
 | 
			
		||||
 | 
			
		||||
@@ -185,20 +206,25 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s
 | 
			
		||||
		resp := session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
		assert.Equal(t, littleSize, resp.Body.Len())
 | 
			
		||||
 | 
			
		||||
		req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
 | 
			
		||||
		resp = session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
		assert.NotEqual(t, littleSize, resp.Body.Len())
 | 
			
		||||
		assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
 | 
			
		||||
		setting.CheckLFSVersion()
 | 
			
		||||
		if setting.LFS.StartServer {
 | 
			
		||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
 | 
			
		||||
			resp = session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
			assert.NotEqual(t, littleSize, resp.Body.Len())
 | 
			
		||||
			assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !testing.Short() {
 | 
			
		||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
 | 
			
		||||
			resp = session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
			assert.Equal(t, bigSize, resp.Body.Len())
 | 
			
		||||
 | 
			
		||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
 | 
			
		||||
			resp = session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
			assert.NotEqual(t, bigSize, resp.Body.Len())
 | 
			
		||||
			assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
 | 
			
		||||
			if setting.LFS.StartServer {
 | 
			
		||||
				req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
 | 
			
		||||
				resp = session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
				assert.NotEqual(t, bigSize, resp.Body.Len())
 | 
			
		||||
				assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -217,18 +243,23 @@ func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS
 | 
			
		||||
		resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
			
		||||
		assert.Equal(t, littleSize, resp.Length)
 | 
			
		||||
 | 
			
		||||
		req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
 | 
			
		||||
		resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
			
		||||
		assert.Equal(t, littleSize, resp.Length)
 | 
			
		||||
		setting.CheckLFSVersion()
 | 
			
		||||
		if setting.LFS.StartServer {
 | 
			
		||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
 | 
			
		||||
			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
			
		||||
			assert.Equal(t, littleSize, resp.Length)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !testing.Short() {
 | 
			
		||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
 | 
			
		||||
			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
			
		||||
			assert.Equal(t, bigSize, resp.Length)
 | 
			
		||||
 | 
			
		||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
 | 
			
		||||
			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
			
		||||
			assert.Equal(t, bigSize, resp.Length)
 | 
			
		||||
			if setting.LFS.StartServer {
 | 
			
		||||
				req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
 | 
			
		||||
				resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
			
		||||
				assert.Equal(t, bigSize, resp.Length)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -274,6 +305,8 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Commit
 | 
			
		||||
	// Now here we should explicitly allow lfs filters to run
 | 
			
		||||
	oldGlobals := allowLFSFilters()
 | 
			
		||||
	err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
@@ -291,6 +324,7 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
 | 
			
		||||
		},
 | 
			
		||||
		Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
 | 
			
		||||
	})
 | 
			
		||||
	git.GlobalCommandArgs = oldGlobals
 | 
			
		||||
	return filepath.Base(tmpFile.Name()), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,11 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string
 | 
			
		||||
 | 
			
		||||
func doLfs(t *testing.T, content *[]byte, expectGzip bool) {
 | 
			
		||||
	prepareTestEnv(t)
 | 
			
		||||
	setting.CheckLFSVersion()
 | 
			
		||||
	if !setting.LFS.StartServer {
 | 
			
		||||
		t.Skip()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	oid := storeObjectInRepo(t, repo.ID, content)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								integrations/migration-test/gitea-v1.3.3.sqlite3.sql.gz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								integrations/migration-test/gitea-v1.3.3.sqlite3.sql.gz
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -54,6 +54,10 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
 | 
			
		||||
 | 
			
		||||
func TestPullMerge(t *testing.T) {
 | 
			
		||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
			
		||||
		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		hookTasksLenBefore := len(hookTasks)
 | 
			
		||||
 | 
			
		||||
		session := loginUser(t, "user1")
 | 
			
		||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
			
		||||
		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
			
		||||
@@ -63,11 +67,19 @@ func TestPullMerge(t *testing.T) {
 | 
			
		||||
		elem := strings.Split(test.RedirectURL(resp), "/")
 | 
			
		||||
		assert.EqualValues(t, "pulls", elem[3])
 | 
			
		||||
		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
 | 
			
		||||
 | 
			
		||||
		hookTasks, err = models.HookTasks(1, 1)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPullRebase(t *testing.T) {
 | 
			
		||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
			
		||||
		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		hookTasksLenBefore := len(hookTasks)
 | 
			
		||||
 | 
			
		||||
		session := loginUser(t, "user1")
 | 
			
		||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
			
		||||
		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
			
		||||
@@ -77,12 +89,21 @@ func TestPullRebase(t *testing.T) {
 | 
			
		||||
		elem := strings.Split(test.RedirectURL(resp), "/")
 | 
			
		||||
		assert.EqualValues(t, "pulls", elem[3])
 | 
			
		||||
		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase)
 | 
			
		||||
 | 
			
		||||
		hookTasks, err = models.HookTasks(1, 1)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPullRebaseMerge(t *testing.T) {
 | 
			
		||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
			
		||||
		prepareTestEnv(t)
 | 
			
		||||
 | 
			
		||||
		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		hookTasksLenBefore := len(hookTasks)
 | 
			
		||||
 | 
			
		||||
		session := loginUser(t, "user1")
 | 
			
		||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
			
		||||
		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
			
		||||
@@ -92,12 +113,21 @@ func TestPullRebaseMerge(t *testing.T) {
 | 
			
		||||
		elem := strings.Split(test.RedirectURL(resp), "/")
 | 
			
		||||
		assert.EqualValues(t, "pulls", elem[3])
 | 
			
		||||
		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge)
 | 
			
		||||
 | 
			
		||||
		hookTasks, err = models.HookTasks(1, 1)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPullSquash(t *testing.T) {
 | 
			
		||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
			
		||||
		prepareTestEnv(t)
 | 
			
		||||
 | 
			
		||||
		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		hookTasksLenBefore := len(hookTasks)
 | 
			
		||||
 | 
			
		||||
		session := loginUser(t, "user1")
 | 
			
		||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
			
		||||
		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
			
		||||
@@ -108,6 +138,10 @@ func TestPullSquash(t *testing.T) {
 | 
			
		||||
		elem := strings.Split(test.RedirectURL(resp), "/")
 | 
			
		||||
		assert.EqualValues(t, "pulls", elem[3])
 | 
			
		||||
		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash)
 | 
			
		||||
 | 
			
		||||
		hookTasks, err = models.HookTasks(1, 1)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
@@ -93,3 +94,28 @@ func TestPullCreate_CommitStatus(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPullCreate_EmptyChangesWithCommits(t *testing.T) {
 | 
			
		||||
	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 | 
			
		||||
		session := loginUser(t, "user1")
 | 
			
		||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
			
		||||
		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
 | 
			
		||||
		testEditFileToNewBranch(t, session, "user1", "repo1", "status1", "status1", "README.md", "# repo1\n\nDescription for repo1")
 | 
			
		||||
 | 
			
		||||
		url := path.Join("user1", "repo1", "compare", "master...status1")
 | 
			
		||||
		req := NewRequestWithValues(t, "POST", url,
 | 
			
		||||
			map[string]string{
 | 
			
		||||
				"_csrf": GetCSRF(t, session, url),
 | 
			
		||||
				"title": "pull request from status1",
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		session.MakeRequest(t, req, http.StatusFound)
 | 
			
		||||
 | 
			
		||||
		req = NewRequest(t, "GET", "/user1/repo1/pulls/1")
 | 
			
		||||
		resp := session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
		doc := NewHTMLParser(t, resp.Body)
 | 
			
		||||
 | 
			
		||||
		text := strings.TrimSpace(doc.doc.Find(".item.text.green").Text())
 | 
			
		||||
		assert.EqualValues(t, "This pull request can be merged automatically.", text)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,13 @@
 | 
			
		||||
package integrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"path"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
@@ -67,6 +70,29 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
 | 
			
		||||
	for _, class := range classes {
 | 
			
		||||
		assert.True(t, sel.HasClass(class))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//By SHA
 | 
			
		||||
	req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/"+path.Base(commitURL)+"/statuses")
 | 
			
		||||
	testRepoCommitsWithStatus(t, session.MakeRequest(t, req, http.StatusOK), state)
 | 
			
		||||
	//By Ref
 | 
			
		||||
	req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/master/statuses")
 | 
			
		||||
	testRepoCommitsWithStatus(t, session.MakeRequest(t, req, http.StatusOK), state)
 | 
			
		||||
	req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/v1.1/statuses")
 | 
			
		||||
	testRepoCommitsWithStatus(t, session.MakeRequest(t, req, http.StatusOK), state)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testRepoCommitsWithStatus(t *testing.T, resp *httptest.ResponseRecorder, state string) {
 | 
			
		||||
	decoder := json.NewDecoder(resp.Body)
 | 
			
		||||
	statuses := []*api.Status{}
 | 
			
		||||
	assert.NoError(t, decoder.Decode(&statuses))
 | 
			
		||||
	assert.Len(t, statuses, 1)
 | 
			
		||||
	for _, s := range statuses {
 | 
			
		||||
		assert.Equal(t, api.StatusState(state), s.State)
 | 
			
		||||
		assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/statuses/65f1bf27bc3bf70f64657658635e66094edbcb4d", s.URL)
 | 
			
		||||
		assert.Equal(t, "http://test.ci/", s.TargetURL)
 | 
			
		||||
		assert.Equal(t, "", s.Description)
 | 
			
		||||
		assert.Equal(t, "testci", s.Context)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRepoCommitsWithStatusPending(t *testing.T) {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,6 +73,7 @@ func testDeleteRepoFile(t *testing.T, u *url.URL) {
 | 
			
		||||
	test.LoadRepoCommit(t, ctx)
 | 
			
		||||
	test.LoadUser(t, ctx, 2)
 | 
			
		||||
	test.LoadGitRepo(t, ctx)
 | 
			
		||||
	defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	doer := ctx.User
 | 
			
		||||
	opts := getDeleteRepoFileOptions(repo)
 | 
			
		||||
@@ -111,6 +112,8 @@ func testDeleteRepoFileWithoutBranchNames(t *testing.T, u *url.URL) {
 | 
			
		||||
	test.LoadRepoCommit(t, ctx)
 | 
			
		||||
	test.LoadUser(t, ctx, 2)
 | 
			
		||||
	test.LoadGitRepo(t, ctx)
 | 
			
		||||
	defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	doer := ctx.User
 | 
			
		||||
	opts := getDeleteRepoFileOptions(repo)
 | 
			
		||||
@@ -139,6 +142,8 @@ func TestDeleteRepoFileErrors(t *testing.T) {
 | 
			
		||||
	test.LoadRepoCommit(t, ctx)
 | 
			
		||||
	test.LoadUser(t, ctx, 2)
 | 
			
		||||
	test.LoadGitRepo(t, ctx)
 | 
			
		||||
	defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	doer := ctx.User
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -191,6 +191,8 @@ func TestCreateOrUpdateRepoFileForCreate(t *testing.T) {
 | 
			
		||||
		test.LoadRepoCommit(t, ctx)
 | 
			
		||||
		test.LoadUser(t, ctx, 2)
 | 
			
		||||
		test.LoadGitRepo(t, ctx)
 | 
			
		||||
		defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		repo := ctx.Repo.Repository
 | 
			
		||||
		doer := ctx.User
 | 
			
		||||
		opts := getCreateRepoFileOptions(repo)
 | 
			
		||||
@@ -201,6 +203,8 @@ func TestCreateOrUpdateRepoFileForCreate(t *testing.T) {
 | 
			
		||||
		// asserts
 | 
			
		||||
		assert.Nil(t, err)
 | 
			
		||||
		gitRepo, _ := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
		defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
 | 
			
		||||
		expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID)
 | 
			
		||||
		assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
 | 
			
		||||
@@ -220,6 +224,8 @@ func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) {
 | 
			
		||||
		test.LoadRepoCommit(t, ctx)
 | 
			
		||||
		test.LoadUser(t, ctx, 2)
 | 
			
		||||
		test.LoadGitRepo(t, ctx)
 | 
			
		||||
		defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		repo := ctx.Repo.Repository
 | 
			
		||||
		doer := ctx.User
 | 
			
		||||
		opts := getUpdateRepoFileOptions(repo)
 | 
			
		||||
@@ -230,6 +236,8 @@ func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) {
 | 
			
		||||
		// asserts
 | 
			
		||||
		assert.Nil(t, err)
 | 
			
		||||
		gitRepo, _ := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
		defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
 | 
			
		||||
		expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath)
 | 
			
		||||
		assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
 | 
			
		||||
@@ -249,6 +257,8 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
 | 
			
		||||
		test.LoadRepoCommit(t, ctx)
 | 
			
		||||
		test.LoadUser(t, ctx, 2)
 | 
			
		||||
		test.LoadGitRepo(t, ctx)
 | 
			
		||||
		defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		repo := ctx.Repo.Repository
 | 
			
		||||
		doer := ctx.User
 | 
			
		||||
		opts := getUpdateRepoFileOptions(repo)
 | 
			
		||||
@@ -261,6 +271,8 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
 | 
			
		||||
		// asserts
 | 
			
		||||
		assert.Nil(t, err)
 | 
			
		||||
		gitRepo, _ := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
		defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
 | 
			
		||||
		expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath)
 | 
			
		||||
		// assert that the old file no longer exists in the last commit of the branch
 | 
			
		||||
@@ -288,6 +300,8 @@ func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) {
 | 
			
		||||
		test.LoadRepoCommit(t, ctx)
 | 
			
		||||
		test.LoadUser(t, ctx, 2)
 | 
			
		||||
		test.LoadGitRepo(t, ctx)
 | 
			
		||||
		defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		repo := ctx.Repo.Repository
 | 
			
		||||
		doer := ctx.User
 | 
			
		||||
		opts := getUpdateRepoFileOptions(repo)
 | 
			
		||||
@@ -300,6 +314,8 @@ func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) {
 | 
			
		||||
		// asserts
 | 
			
		||||
		assert.Nil(t, err)
 | 
			
		||||
		gitRepo, _ := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
		defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch)
 | 
			
		||||
		expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath)
 | 
			
		||||
		assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
 | 
			
		||||
@@ -315,6 +331,8 @@ func TestCreateOrUpdateRepoFileErrors(t *testing.T) {
 | 
			
		||||
		test.LoadRepoCommit(t, ctx)
 | 
			
		||||
		test.LoadUser(t, ctx, 2)
 | 
			
		||||
		test.LoadGitRepo(t, ctx)
 | 
			
		||||
		defer ctx.Repo.GitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		repo := ctx.Repo.Repository
 | 
			
		||||
		doer := ctx.User
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
@@ -25,11 +26,21 @@ type TestLogger struct {
 | 
			
		||||
var writerCloser = &testLoggerWriterCloser{}
 | 
			
		||||
 | 
			
		||||
type testLoggerWriterCloser struct {
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	t testing.TB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *testLoggerWriterCloser) setT(t *testing.TB) {
 | 
			
		||||
	w.Lock()
 | 
			
		||||
	w.t = *t
 | 
			
		||||
	w.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *testLoggerWriterCloser) Write(p []byte) (int, error) {
 | 
			
		||||
	if w.t != nil {
 | 
			
		||||
	w.RLock()
 | 
			
		||||
	t := w.t
 | 
			
		||||
	w.RUnlock()
 | 
			
		||||
	if t != nil {
 | 
			
		||||
		if len(p) > 0 && p[len(p)-1] == '\n' {
 | 
			
		||||
			p = p[:len(p)-1]
 | 
			
		||||
		}
 | 
			
		||||
@@ -54,7 +65,7 @@ func (w *testLoggerWriterCloser) Write(p []byte) (int, error) {
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		w.t.Log(string(p))
 | 
			
		||||
		t.Log(string(p))
 | 
			
		||||
		return len(p), nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(p), nil
 | 
			
		||||
@@ -77,7 +88,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) {
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", t.Name(), strings.TrimPrefix(filename, prefix), line)
 | 
			
		||||
	}
 | 
			
		||||
	writerCloser.t = t
 | 
			
		||||
	writerCloser.setT(&t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Printf takes a format and args and prints the string to os.Stdout
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,7 @@ var (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const issueRefRegexpStr = `(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)+`
 | 
			
		||||
const issueRefRegexpStrNoKeyword = `(?:\s|^|\(|\[)(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`
 | 
			
		||||
 | 
			
		||||
func assembleKeywordsPattern(words []string) string {
 | 
			
		||||
	return fmt.Sprintf(`(?i)(?:%s)(?::?) %s`, strings.Join(words, "|"), issueRefRegexpStr)
 | 
			
		||||
@@ -73,7 +74,7 @@ func assembleKeywordsPattern(words []string) string {
 | 
			
		||||
func init() {
 | 
			
		||||
	issueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueCloseKeywords))
 | 
			
		||||
	issueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueReopenKeywords))
 | 
			
		||||
	issueReferenceKeywordsPat = regexp.MustCompile(issueRefRegexpStr)
 | 
			
		||||
	issueReferenceKeywordsPat = regexp.MustCompile(issueRefRegexpStrNoKeyword)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Action represents user operation type and other information to
 | 
			
		||||
@@ -761,6 +762,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("GetBranchCommitID[%s]: %v", opts.RefFullName, err)
 | 
			
		||||
			}
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
			if err = PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
 | 
			
		||||
				Ref:     refName,
 | 
			
		||||
				Sha:     shaSum,
 | 
			
		||||
@@ -796,6 +798,8 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error("GetTagCommitID[%s]: %v", opts.RefFullName, err)
 | 
			
		||||
		}
 | 
			
		||||
		gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		if err = PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
 | 
			
		||||
			Ref:     refName,
 | 
			
		||||
			Sha:     shaSum,
 | 
			
		||||
 
 | 
			
		||||
@@ -155,6 +155,26 @@ func TestPushCommits_AvatarLink(t *testing.T) {
 | 
			
		||||
		pushCommits.AvatarLink("nonexistent@example.com"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRegExp_issueReferenceKeywordsPat(t *testing.T) {
 | 
			
		||||
	trueTestCases := []string{
 | 
			
		||||
		"#2",
 | 
			
		||||
		"[#2]",
 | 
			
		||||
		"please see go-gitea/gitea#5",
 | 
			
		||||
		"#2:",
 | 
			
		||||
	}
 | 
			
		||||
	falseTestCases := []string{
 | 
			
		||||
		"kb#2",
 | 
			
		||||
		"#2xy",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, testCase := range trueTestCases {
 | 
			
		||||
		assert.True(t, issueReferenceKeywordsPat.MatchString(testCase))
 | 
			
		||||
	}
 | 
			
		||||
	for _, testCase := range falseTestCases {
 | 
			
		||||
		assert.False(t, issueReferenceKeywordsPat.MatchString(testCase))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_getIssueFromRef(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, PrepareTestDatabase())
 | 
			
		||||
	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ func (protectBranch *ProtectedBranch) HasEnoughApprovals(pr *PullRequest) bool {
 | 
			
		||||
 | 
			
		||||
// GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist.
 | 
			
		||||
func (protectBranch *ProtectedBranch) GetGrantedApprovalsCount(pr *PullRequest) int64 {
 | 
			
		||||
	reviews, err := GetReviewersByPullID(pr.Issue.ID)
 | 
			
		||||
	reviews, err := GetReviewersByPullID(pr.IssueID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("GetReviewersByPullID: %v", err)
 | 
			
		||||
		return 0
 | 
			
		||||
@@ -192,7 +192,7 @@ func UpdateProtectBranch(repo *Repository, protectBranch *ProtectedBranch, opts
 | 
			
		||||
	}
 | 
			
		||||
	protectBranch.MergeWhitelistUserIDs = whitelist
 | 
			
		||||
 | 
			
		||||
	whitelist, err = updateUserWhitelist(repo, protectBranch.ApprovalsWhitelistUserIDs, opts.ApprovalsUserIDs)
 | 
			
		||||
	whitelist, err = updateApprovalWhitelist(repo, protectBranch.ApprovalsWhitelistUserIDs, opts.ApprovalsUserIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -298,6 +298,27 @@ func (repo *Repository) IsProtectedBranchForMerging(pr *PullRequest, branchName
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
 | 
			
		||||
// the users from newWhitelist which have explicit read or write access to the repo.
 | 
			
		||||
func updateApprovalWhitelist(repo *Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
 | 
			
		||||
	hasUsersChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist)
 | 
			
		||||
	if !hasUsersChanged {
 | 
			
		||||
		return currentWhitelist, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	whitelist = make([]int64, 0, len(newWhitelist))
 | 
			
		||||
	for _, userID := range newWhitelist {
 | 
			
		||||
		if reader, err := repo.IsReader(userID); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		} else if !reader {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		whitelist = append(whitelist, userID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
 | 
			
		||||
// the users from newWhitelist which have write access to the repo.
 | 
			
		||||
func updateUserWhitelist(repo *Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
 | 
			
		||||
 
 | 
			
		||||
@@ -87,7 +87,7 @@ func (status *CommitStatus) loadRepo(e Engine) (err error) {
 | 
			
		||||
// APIURL returns the absolute APIURL to this commit-status.
 | 
			
		||||
func (status *CommitStatus) APIURL() string {
 | 
			
		||||
	_ = status.loadRepo(x)
 | 
			
		||||
	return fmt.Sprintf("%sapi/v1/%s/statuses/%s",
 | 
			
		||||
	return fmt.Sprintf("%sapi/v1/repos/%s/statuses/%s",
 | 
			
		||||
		setting.AppURL, status.Repo.FullName(), status.SHA)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,20 +19,25 @@ func TestGetCommitStatuses(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	statuses, err := GetCommitStatuses(repo1, sha1, 0)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	if assert.Len(t, statuses, 5) {
 | 
			
		||||
		assert.Equal(t, statuses[0].Context, "ci/awesomeness")
 | 
			
		||||
		assert.Equal(t, statuses[0].State, CommitStatusPending)
 | 
			
		||||
	assert.Len(t, statuses, 5)
 | 
			
		||||
 | 
			
		||||
		assert.Equal(t, statuses[1].Context, "cov/awesomeness")
 | 
			
		||||
		assert.Equal(t, statuses[1].State, CommitStatusWarning)
 | 
			
		||||
	assert.Equal(t, "ci/awesomeness", statuses[0].Context)
 | 
			
		||||
	assert.Equal(t, CommitStatusPending, statuses[0].State)
 | 
			
		||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[0].APIURL())
 | 
			
		||||
 | 
			
		||||
		assert.Equal(t, statuses[2].Context, "cov/awesomeness")
 | 
			
		||||
		assert.Equal(t, statuses[2].State, CommitStatusSuccess)
 | 
			
		||||
	assert.Equal(t, "cov/awesomeness", statuses[1].Context)
 | 
			
		||||
	assert.Equal(t, CommitStatusWarning, statuses[1].State)
 | 
			
		||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[1].APIURL())
 | 
			
		||||
 | 
			
		||||
		assert.Equal(t, statuses[3].Context, "ci/awesomeness")
 | 
			
		||||
		assert.Equal(t, statuses[3].State, CommitStatusFailure)
 | 
			
		||||
	assert.Equal(t, "cov/awesomeness", statuses[2].Context)
 | 
			
		||||
	assert.Equal(t, CommitStatusSuccess, statuses[2].State)
 | 
			
		||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[2].APIURL())
 | 
			
		||||
 | 
			
		||||
		assert.Equal(t, statuses[4].Context, "deploy/awesomeness")
 | 
			
		||||
		assert.Equal(t, statuses[4].State, CommitStatusError)
 | 
			
		||||
	}
 | 
			
		||||
	assert.Equal(t, "ci/awesomeness", statuses[3].Context)
 | 
			
		||||
	assert.Equal(t, CommitStatusFailure, statuses[3].State)
 | 
			
		||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[3].APIURL())
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
 | 
			
		||||
	assert.Equal(t, CommitStatusError, statuses[4].State)
 | 
			
		||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
  act_user_id: 2
 | 
			
		||||
  repo_id: 2
 | 
			
		||||
  is_private: true
 | 
			
		||||
  created_unix: 1540139562
 | 
			
		||||
  created_unix: 1571686356
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 2
 | 
			
		||||
 
 | 
			
		||||
@@ -675,6 +675,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	commit, err := gitRepo.GetCommit(afterCommitID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -747,6 +748,7 @@ func GetRawDiffForFile(repoPath, startCommit, endCommit string, diffType RawDiff
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer repo.Close()
 | 
			
		||||
 | 
			
		||||
	commit, err := repo.GetCommit(endCommit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ func BenchmarkGetCommitGraph(b *testing.B) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		b.Error("Could not open repository")
 | 
			
		||||
	}
 | 
			
		||||
	defer currentRepo.Close()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		graph, err := GetCommitGraph(currentRepo)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,13 +12,13 @@ import (
 | 
			
		||||
 | 
			
		||||
// PushingEnvironment returns an os environment to allow hooks to work on push
 | 
			
		||||
func PushingEnvironment(doer *User, repo *Repository) []string {
 | 
			
		||||
	return FullPushingEnvironment(doer, doer, repo, 0)
 | 
			
		||||
	return FullPushingEnvironment(doer, doer, repo, repo.Name, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FullPushingEnvironment returns an os environment to allow hooks to work on push
 | 
			
		||||
func FullPushingEnvironment(author, committer *User, repo *Repository, prID int64) []string {
 | 
			
		||||
func FullPushingEnvironment(author, committer *User, repo *Repository, repoName string, prID int64) []string {
 | 
			
		||||
	isWiki := "false"
 | 
			
		||||
	if strings.HasSuffix(repo.Name, ".wiki") {
 | 
			
		||||
	if strings.HasSuffix(repoName, ".wiki") {
 | 
			
		||||
		isWiki = "true"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +32,7 @@ func FullPushingEnvironment(author, committer *User, repo *Repository, prID int6
 | 
			
		||||
		"GIT_AUTHOR_EMAIL="+authorSig.Email,
 | 
			
		||||
		"GIT_COMMITTER_NAME="+committerSig.Name,
 | 
			
		||||
		"GIT_COMMITTER_EMAIL="+committerSig.Email,
 | 
			
		||||
		EnvRepoName+"="+repo.Name,
 | 
			
		||||
		EnvRepoName+"="+repoName,
 | 
			
		||||
		EnvRepoUsername+"="+repo.MustOwnerName(),
 | 
			
		||||
		EnvRepoIsWiki+"="+isWiki,
 | 
			
		||||
		EnvPusherName+"="+committer.Name,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										184
									
								
								models/issue.go
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								models/issue.go
									
									
									
									
									
								
							@@ -755,7 +755,7 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (er
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update issue count of milestone
 | 
			
		||||
	if err = changeMilestoneIssueStats(e, issue); err != nil {
 | 
			
		||||
	if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1099,7 +1099,11 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Issue.MilestoneID > 0 {
 | 
			
		||||
		if err = changeMilestoneAssign(e, doer, opts.Issue, -1); err != nil {
 | 
			
		||||
		if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err = createMilestoneComment(e, doer, opts.Repo, opts.Issue, 0, opts.Issue.MilestoneID); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -1446,7 +1450,7 @@ func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) {
 | 
			
		||||
	userIDs := make([]int64, 0, 5)
 | 
			
		||||
	if err := e.Table("comment").Cols("poster_id").
 | 
			
		||||
		Where("`comment`.issue_id = ?", issueID).
 | 
			
		||||
		And("`comment`.type = ?", CommentTypeComment).
 | 
			
		||||
		And("`comment`.type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview).
 | 
			
		||||
		And("`user`.is_active = ?", true).
 | 
			
		||||
		And("`user`.prohibit_login = ?", false).
 | 
			
		||||
		Join("INNER", "`user`", "`user`.id = `comment`.poster_id").
 | 
			
		||||
@@ -1462,46 +1466,18 @@ func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) {
 | 
			
		||||
	return users, e.In("id", userIDs).Find(&users)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateIssueMentions extracts mentioned people from content and
 | 
			
		||||
// updates issue-user relations for them.
 | 
			
		||||
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
 | 
			
		||||
// UpdateIssueMentions updates issue-user relations for mentioned users.
 | 
			
		||||
func UpdateIssueMentions(e Engine, issueID int64, mentions []*User) error {
 | 
			
		||||
	if len(mentions) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range mentions {
 | 
			
		||||
		mentions[i] = strings.ToLower(mentions[i])
 | 
			
		||||
	ids := make([]int64, len(mentions))
 | 
			
		||||
	for i, u := range mentions {
 | 
			
		||||
		ids[i] = u.ID
 | 
			
		||||
	}
 | 
			
		||||
	users := make([]*User, 0, len(mentions))
 | 
			
		||||
 | 
			
		||||
	if err := e.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
 | 
			
		||||
		return fmt.Errorf("find mentioned users: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ids := make([]int64, 0, len(mentions))
 | 
			
		||||
	for _, user := range users {
 | 
			
		||||
		ids = append(ids, user.ID)
 | 
			
		||||
		if !user.IsOrganization() || user.NumMembers == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memberIDs := make([]int64, 0, user.NumMembers)
 | 
			
		||||
		orgUsers, err := getOrgUsersByOrgID(e, user.ID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("GetOrgUsersByOrgID [%d]: %v", user.ID, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, orgUser := range orgUsers {
 | 
			
		||||
			memberIDs = append(memberIDs, orgUser.ID)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ids = append(ids, memberIDs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := UpdateIssueUsersByMentions(e, issueID, ids); err != nil {
 | 
			
		||||
		return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1835,3 +1811,139 @@ func (issue *Issue) BlockedByDependencies() ([]*Issue, error) {
 | 
			
		||||
func (issue *Issue) BlockingDependencies() ([]*Issue, error) {
 | 
			
		||||
	return issue.getBlockingDependencies(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (issue *Issue) updateClosedNum(e Engine) (err error) {
 | 
			
		||||
	if issue.IsPull {
 | 
			
		||||
		_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=(SELECT count(*) FROM issue WHERE repo_id=? AND is_pull=? AND is_closed=?) WHERE id=?",
 | 
			
		||||
			issue.RepoID,
 | 
			
		||||
			true,
 | 
			
		||||
			true,
 | 
			
		||||
			issue.RepoID,
 | 
			
		||||
		)
 | 
			
		||||
	} else {
 | 
			
		||||
		_, err = e.Exec("UPDATE `repository` SET num_closed_issues=(SELECT count(*) FROM issue WHERE repo_id=? AND is_pull=? AND is_closed=?) WHERE id=?",
 | 
			
		||||
			issue.RepoID,
 | 
			
		||||
			false,
 | 
			
		||||
			true,
 | 
			
		||||
			issue.RepoID,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that
 | 
			
		||||
// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
 | 
			
		||||
func (issue *Issue) ResolveMentionsByVisibility(e Engine, doer *User, mentions []string) (users []*User, err error) {
 | 
			
		||||
	if len(mentions) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err = issue.loadRepo(e); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	resolved := make(map[string]bool, 20)
 | 
			
		||||
	names := make([]string, 0, 20)
 | 
			
		||||
	resolved[doer.LowerName] = true
 | 
			
		||||
	for _, name := range mentions {
 | 
			
		||||
		name := strings.ToLower(name)
 | 
			
		||||
		if _, ok := resolved[name]; ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		resolved[name] = false
 | 
			
		||||
		names = append(names, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := issue.Repo.getOwner(e); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.Repo.Owner.IsOrganization() {
 | 
			
		||||
		// Since there can be users with names that match the name of a team,
 | 
			
		||||
		// if the team exists and can read the issue, the team takes precedence.
 | 
			
		||||
		teams := make([]*Team, 0, len(names))
 | 
			
		||||
		if err := e.
 | 
			
		||||
			Join("INNER", "team_repo", "team_repo.team_id = team.id").
 | 
			
		||||
			Where("team_repo.repo_id=?", issue.Repo.ID).
 | 
			
		||||
			In("team.lower_name", names).
 | 
			
		||||
			Find(&teams); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("find mentioned teams: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if len(teams) != 0 {
 | 
			
		||||
			checked := make([]int64, 0, len(teams))
 | 
			
		||||
			unittype := UnitTypeIssues
 | 
			
		||||
			if issue.IsPull {
 | 
			
		||||
				unittype = UnitTypePullRequests
 | 
			
		||||
			}
 | 
			
		||||
			for _, team := range teams {
 | 
			
		||||
				if team.Authorize >= AccessModeOwner {
 | 
			
		||||
					checked = append(checked, team.ID)
 | 
			
		||||
					resolved[team.LowerName] = true
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				has, err := e.Get(&TeamUnit{OrgID: issue.Repo.Owner.ID, TeamID: team.ID, Type: unittype})
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, fmt.Errorf("get team units (%d): %v", team.ID, err)
 | 
			
		||||
				}
 | 
			
		||||
				if has {
 | 
			
		||||
					checked = append(checked, team.ID)
 | 
			
		||||
					resolved[team.LowerName] = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(checked) != 0 {
 | 
			
		||||
				teamusers := make([]*User, 0, 20)
 | 
			
		||||
				if err := e.
 | 
			
		||||
					Join("INNER", "team_user", "team_user.uid = `user`.id").
 | 
			
		||||
					In("`team_user`.team_id", checked).
 | 
			
		||||
					And("`user`.is_active = ?", true).
 | 
			
		||||
					And("`user`.prohibit_login = ?", false).
 | 
			
		||||
					Find(&teamusers); err != nil {
 | 
			
		||||
					return nil, fmt.Errorf("get teams users: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				if len(teamusers) > 0 {
 | 
			
		||||
					users = make([]*User, 0, len(teamusers))
 | 
			
		||||
					for _, user := range teamusers {
 | 
			
		||||
						if already, ok := resolved[user.LowerName]; !ok || !already {
 | 
			
		||||
							users = append(users, user)
 | 
			
		||||
							resolved[user.LowerName] = true
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Remove names already in the list to avoid querying the database if pending names remain
 | 
			
		||||
		names = make([]string, 0, len(resolved))
 | 
			
		||||
		for name, already := range resolved {
 | 
			
		||||
			if !already {
 | 
			
		||||
				names = append(names, name)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(names) == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unchecked := make([]*User, 0, len(names))
 | 
			
		||||
	if err := e.
 | 
			
		||||
		Where("`user`.is_active = ?", true).
 | 
			
		||||
		And("`user`.prohibit_login = ?", false).
 | 
			
		||||
		In("`user`.lower_name", names).
 | 
			
		||||
		Find(&unchecked); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("find mentioned users: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, user := range unchecked {
 | 
			
		||||
		if already := resolved[user.LowerName]; already || user.IsOrganization() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// Normal users must have read access to the referencing issue
 | 
			
		||||
		perm, err := getUserRepoPermission(e, issue.Repo, user)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("getUserRepoPermission [%d]: %v", user.ID, err)
 | 
			
		||||
		}
 | 
			
		||||
		if !perm.CanReadIssuesOrPulls(issue.IsPull) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		users = append(users, user)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -142,11 +142,15 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
	if err := sess.Commit(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go HookQueue.Add(issue.RepoID)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64, isCreate bool) (err error) {
 | 
			
		||||
 | 
			
		||||
	// Update the assignee
 | 
			
		||||
	removed, err := updateIssueAssignee(sess, issue, assigneeID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -209,7 +213,6 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	go HookQueue.Add(issue.RepoID)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -387,11 +387,18 @@ func (c *Comment) MailParticipants(opType ActionType, issue *Issue) (err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
 | 
			
		||||
	mentions := markup.FindAllMentions(c.Content)
 | 
			
		||||
	if err = UpdateIssueMentions(e, c.IssueID, mentions); err != nil {
 | 
			
		||||
	rawMentions := markup.FindAllMentions(c.Content)
 | 
			
		||||
	userMentions, err := issue.ResolveMentionsByVisibility(e, c.Poster, rawMentions)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("ResolveMentionsByVisibility [%d]: %v", c.IssueID, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err = UpdateIssueMentions(e, c.IssueID, userMentions); err != nil {
 | 
			
		||||
		return fmt.Errorf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mentions := make([]string, len(userMentions))
 | 
			
		||||
	for i, u := range userMentions {
 | 
			
		||||
		mentions[i] = u.LowerName
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.Content) > 0 {
 | 
			
		||||
		if err = mailIssueCommentToParticipants(e, issue, c.Poster, c.Content, c, mentions); err != nil {
 | 
			
		||||
			log.Error("mailIssueCommentToParticipants: %v", err)
 | 
			
		||||
@@ -632,12 +639,7 @@ func sendCreateCommentAction(e *xorm.Session, opts *CreateCommentOptions, commen
 | 
			
		||||
			act.OpType = ActionReopenPullRequest
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if opts.Issue.IsPull {
 | 
			
		||||
			_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID)
 | 
			
		||||
		} else {
 | 
			
		||||
			_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		if err = opts.Issue.updateClosedNum(e); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -647,12 +649,7 @@ func sendCreateCommentAction(e *xorm.Session, opts *CreateCommentOptions, commen
 | 
			
		||||
			act.OpType = ActionClosePullRequest
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if opts.Issue.IsPull {
 | 
			
		||||
			_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID)
 | 
			
		||||
		} else {
 | 
			
		||||
			_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		if err = opts.Issue.updateClosedNum(e); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -896,6 +893,7 @@ func CreateCodeComment(doer *User, repo *Repository, issue *Issue, content, tree
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	// FIXME validate treePath
 | 
			
		||||
	// Get latest commit referencing the commented line
 | 
			
		||||
 
 | 
			
		||||
@@ -123,11 +123,18 @@ func (issue *Issue) MailParticipants(doer *User, opType ActionType) (err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (issue *Issue) mailParticipants(e Engine, doer *User, opType ActionType) (err error) {
 | 
			
		||||
	mentions := markup.FindAllMentions(issue.Content)
 | 
			
		||||
 | 
			
		||||
	if err = UpdateIssueMentions(e, issue.ID, mentions); err != nil {
 | 
			
		||||
	rawMentions := markup.FindAllMentions(issue.Content)
 | 
			
		||||
	userMentions, err := issue.ResolveMentionsByVisibility(e, doer, rawMentions)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("ResolveMentionsByVisibility [%d]: %v", issue.ID, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err = UpdateIssueMentions(e, issue.ID, userMentions); err != nil {
 | 
			
		||||
		return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
 | 
			
		||||
	}
 | 
			
		||||
	mentions := make([]string, len(userMentions))
 | 
			
		||||
	for i, u := range userMentions {
 | 
			
		||||
		mentions[i] = u.LowerName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(issue.Content) > 0 {
 | 
			
		||||
		if err = mailIssueCommentToParticipants(e, issue, doer, issue.Content, nil, mentions); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
@@ -290,6 +291,10 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m.IsClosed = isClosed
 | 
			
		||||
	if isClosed {
 | 
			
		||||
		m.ClosedDateUnix = util.TimeStamp(time.Now().Unix())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = updateMilestone(sess, m); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -311,71 +316,74 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
 | 
			
		||||
	if issue.MilestoneID == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
func updateMilestoneTotalNum(e Engine, milestoneID int64) (err error) {
 | 
			
		||||
	if _, err = e.Exec("UPDATE `milestone` SET num_issues=(SELECT count(*) FROM issue WHERE milestone_id=?) WHERE id=?",
 | 
			
		||||
		milestoneID,
 | 
			
		||||
		milestoneID,
 | 
			
		||||
	); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	_, err = e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
 | 
			
		||||
		milestoneID,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateMilestoneClosedNum(e Engine, milestoneID int64) (err error) {
 | 
			
		||||
	if _, err = e.Exec("UPDATE `milestone` SET num_closed_issues=(SELECT count(*) FROM issue WHERE milestone_id=? AND is_closed=?) WHERE id=?",
 | 
			
		||||
		milestoneID,
 | 
			
		||||
		true,
 | 
			
		||||
		milestoneID,
 | 
			
		||||
	); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.IsClosed {
 | 
			
		||||
		m.NumOpenIssues--
 | 
			
		||||
		m.NumClosedIssues++
 | 
			
		||||
	} else {
 | 
			
		||||
		m.NumOpenIssues++
 | 
			
		||||
		m.NumClosedIssues--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return updateMilestone(e, m)
 | 
			
		||||
	_, err = e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
 | 
			
		||||
		milestoneID,
 | 
			
		||||
	)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilestoneID int64) error {
 | 
			
		||||
	if err := updateIssueCols(e, issue, "milestone_id"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if oldMilestoneID > 0 {
 | 
			
		||||
		m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		if err := updateMilestoneTotalNum(e, oldMilestoneID); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m.NumIssues--
 | 
			
		||||
		if issue.IsClosed {
 | 
			
		||||
			m.NumClosedIssues--
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err = updateMilestone(e, m); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			if err := updateMilestoneClosedNum(e, oldMilestoneID); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.MilestoneID > 0 {
 | 
			
		||||
		m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		if err := updateMilestoneTotalNum(e, issue.MilestoneID); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m.NumIssues++
 | 
			
		||||
		if issue.IsClosed {
 | 
			
		||||
			m.NumClosedIssues++
 | 
			
		||||
			if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err = updateMilestone(e, m); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := issue.loadRepo(e); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if oldMilestoneID > 0 || issue.MilestoneID > 0 {
 | 
			
		||||
		if err := issue.loadRepo(e); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err := createMilestoneComment(e, doer, issue.Repo, issue, oldMilestoneID, issue.MilestoneID); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return updateIssueCols(e, issue, "milestone_id")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChangeMilestoneAssign changes assignment of milestone for issue.
 | 
			
		||||
 
 | 
			
		||||
@@ -231,7 +231,7 @@ func TestChangeMilestoneStatus(t *testing.T) {
 | 
			
		||||
	CheckConsistencyFor(t, &Repository{ID: milestone.RepoID}, &Milestone{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestChangeMilestoneIssueStats(t *testing.T) {
 | 
			
		||||
func TestUpdateMilestoneClosedNum(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, PrepareTestDatabase())
 | 
			
		||||
	issue := AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
 | 
			
		||||
		"is_closed=0").(*Issue)
 | 
			
		||||
@@ -240,14 +240,14 @@ func TestChangeMilestoneIssueStats(t *testing.T) {
 | 
			
		||||
	issue.ClosedUnix = util.TimeStampNow()
 | 
			
		||||
	_, err := x.Cols("is_closed", "closed_unix").Update(issue)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
 | 
			
		||||
	assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
 | 
			
		||||
	CheckConsistencyFor(t, &Milestone{})
 | 
			
		||||
 | 
			
		||||
	issue.IsClosed = false
 | 
			
		||||
	issue.ClosedUnix = 0
 | 
			
		||||
	_, err = x.Cols("is_closed", "closed_unix").Update(issue)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
 | 
			
		||||
	assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
 | 
			
		||||
	CheckConsistencyFor(t, &Milestone{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -320,3 +320,35 @@ func TestIssue_SearchIssueIDsByKeyword(t *testing.T) {
 | 
			
		||||
	assert.EqualValues(t, 1, total)
 | 
			
		||||
	assert.EqualValues(t, []int64{1}, ids)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIssue_ResolveMentions(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, PrepareTestDatabase())
 | 
			
		||||
 | 
			
		||||
	testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
 | 
			
		||||
		o := AssertExistsAndLoadBean(t, &User{LowerName: owner}).(*User)
 | 
			
		||||
		r := AssertExistsAndLoadBean(t, &Repository{OwnerID: o.ID, LowerName: repo}).(*Repository)
 | 
			
		||||
		issue := &Issue{RepoID: r.ID}
 | 
			
		||||
		d := AssertExistsAndLoadBean(t, &User{LowerName: doer}).(*User)
 | 
			
		||||
		resolved, err := issue.ResolveMentionsByVisibility(x, d, mentions)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		ids := make([]int64, len(resolved))
 | 
			
		||||
		for i, user := range resolved {
 | 
			
		||||
			ids[i] = user.ID
 | 
			
		||||
		}
 | 
			
		||||
		sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
 | 
			
		||||
		assert.EqualValues(t, expected, ids)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Public repo, existing user
 | 
			
		||||
	testSuccess("user2", "repo1", "user1", []string{"user5"}, []int64{5})
 | 
			
		||||
	// Public repo, non-existing user
 | 
			
		||||
	testSuccess("user2", "repo1", "user1", []string{"nonexisting"}, []int64{})
 | 
			
		||||
	// Public repo, doer
 | 
			
		||||
	testSuccess("user2", "repo1", "user1", []string{"user1"}, []int64{})
 | 
			
		||||
	// Private repo, team member
 | 
			
		||||
	testSuccess("user17", "big_test_private_4", "user20", []string{"user2"}, []int64{2})
 | 
			
		||||
	// Private repo, not a team member
 | 
			
		||||
	testSuccess("user17", "big_test_private_4", "user20", []string{"user5"}, []int64{})
 | 
			
		||||
	// Private repo, whole team
 | 
			
		||||
	testSuccess("user17", "big_test_private_4", "user15", []string{"owners"}, []int64{18})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ func (l *LFSLock) APIFormat() *api.LFSLock {
 | 
			
		||||
	return &api.LFSLock{
 | 
			
		||||
		ID:       strconv.FormatInt(l.ID, 10),
 | 
			
		||||
		Path:     l.Path,
 | 
			
		||||
		LockedAt: l.Created,
 | 
			
		||||
		LockedAt: l.Created.Round(time.Second),
 | 
			
		||||
		Owner: &api.LFSLockOwner{
 | 
			
		||||
			Name: l.Owner.DisplayName(),
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -62,38 +62,50 @@ func insertIssue(sess *xorm.Session, issue *Issue) error {
 | 
			
		||||
	if _, err := sess.Insert(issueLabels); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cols := make([]string, 0)
 | 
			
		||||
	if !issue.IsPull {
 | 
			
		||||
		sess.ID(issue.RepoID).Incr("num_issues")
 | 
			
		||||
		cols = append(cols, "num_issues")
 | 
			
		||||
		if issue.IsClosed {
 | 
			
		||||
			sess.Incr("num_closed_issues")
 | 
			
		||||
			cols = append(cols, "num_closed_issues")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		sess.ID(issue.RepoID).Incr("num_pulls")
 | 
			
		||||
		cols = append(cols, "num_pulls")
 | 
			
		||||
		if issue.IsClosed {
 | 
			
		||||
			sess.Incr("num_closed_pulls")
 | 
			
		||||
			cols = append(cols, "num_closed_pulls")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := sess.NoAutoTime().Update(issue.Repo); err != nil {
 | 
			
		||||
	if _, err := sess.NoAutoTime().Cols(cols...).Update(issue.Repo); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cols = []string{"num_issues"}
 | 
			
		||||
	sess.Incr("num_issues")
 | 
			
		||||
	if issue.IsClosed {
 | 
			
		||||
		sess.Incr("num_closed_issues")
 | 
			
		||||
		cols = append(cols, "num_closed_issues")
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := sess.In("id", labelIDs).NoAutoTime().Update(new(Label)); err != nil {
 | 
			
		||||
	if _, err := sess.In("id", labelIDs).NoAutoTime().Cols(cols...).Update(new(Label)); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.MilestoneID > 0 {
 | 
			
		||||
		cols = []string{"num_issues"}
 | 
			
		||||
		sess.Incr("num_issues")
 | 
			
		||||
		cl := "num_closed_issues"
 | 
			
		||||
		if issue.IsClosed {
 | 
			
		||||
			sess.Incr("num_closed_issues")
 | 
			
		||||
			cols = append(cols, "num_closed_issues")
 | 
			
		||||
			cl = "(num_closed_issues + 1)"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err := sess.ID(issue.MilestoneID).
 | 
			
		||||
			SetExpr("completeness", "num_closed_issues * 100 / num_issues").
 | 
			
		||||
			NoAutoTime().
 | 
			
		||||
			SetExpr("completeness", cl+" * 100 / (num_issues + 1)").
 | 
			
		||||
			NoAutoTime().Cols(cols...).
 | 
			
		||||
			Update(new(Milestone)); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -321,11 +321,25 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		tableSQL := string(res[0]["sql"])
 | 
			
		||||
 | 
			
		||||
		// Separate out the column definitions
 | 
			
		||||
		tableSQL = tableSQL[strings.Index(tableSQL, "("):]
 | 
			
		||||
 | 
			
		||||
		// Remove the required columnNames
 | 
			
		||||
		for _, name := range columnNames {
 | 
			
		||||
			tableSQL = regexp.MustCompile(regexp.QuoteMeta("`"+name+"`")+"[^`,)]*[,)]").ReplaceAllString(tableSQL, "")
 | 
			
		||||
			tableSQL = regexp.MustCompile(regexp.QuoteMeta("`"+name+"`")+"[^`,)]*?[,)]").ReplaceAllString(tableSQL, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Ensure the query is ended properly
 | 
			
		||||
		tableSQL = strings.TrimSpace(tableSQL)
 | 
			
		||||
		if tableSQL[len(tableSQL)-1] != ')' {
 | 
			
		||||
			if tableSQL[len(tableSQL)-1] == ',' {
 | 
			
		||||
				tableSQL = tableSQL[:len(tableSQL)-1]
 | 
			
		||||
			}
 | 
			
		||||
			tableSQL += ")"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Find all the columns in the table
 | 
			
		||||
		columns := regexp.MustCompile("`([^`]*)`").FindAllString(tableSQL, -1)
 | 
			
		||||
 | 
			
		||||
		tableSQL = fmt.Sprintf("CREATE TABLE `new_%s_new` ", tableName) + tableSQL
 | 
			
		||||
@@ -370,9 +384,11 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
 | 
			
		||||
		}
 | 
			
		||||
		for _, index := range res {
 | 
			
		||||
			indexName := index["column_name"]
 | 
			
		||||
			_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			if len(indexName) > 0 {
 | 
			
		||||
				_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ func releaseAddColumnIsTagAndSyncTags(x *xorm.Engine) error {
 | 
			
		||||
			if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
 | 
			
		||||
				log.Warn("SyncReleasesWithTags: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
		}
 | 
			
		||||
		if len(repos) < pageSize {
 | 
			
		||||
			break
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,7 @@
 | 
			
		||||
package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-xorm/xorm"
 | 
			
		||||
	"xorm.io/core"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func renameRepoIsBareToIsEmpty(x *xorm.Engine) error {
 | 
			
		||||
@@ -21,73 +15,28 @@ func renameRepoIsBareToIsEmpty(x *xorm.Engine) error {
 | 
			
		||||
		IsEmpty bool `xorm:"INDEX"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// First remove the index
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	if models.DbCfg.Type == core.POSTGRES || models.DbCfg.Type == core.SQLITE {
 | 
			
		||||
		_, err = sess.Exec("DROP INDEX IF EXISTS IDX_repository_is_bare")
 | 
			
		||||
	} else if models.DbCfg.Type == core.MSSQL {
 | 
			
		||||
		_, err = sess.Exec(`DECLARE @ConstraintName VARCHAR(256)
 | 
			
		||||
		DECLARE @SQL NVARCHAR(256)
 | 
			
		||||
		SELECT @ConstraintName = obj.name FROM sys.columns col LEFT OUTER JOIN sys.objects obj ON obj.object_id = col.default_object_id AND obj.type = 'D' WHERE col.object_id = OBJECT_ID('repository') AND obj.name IS NOT NULL AND col.name = 'is_bare'
 | 
			
		||||
		SET @SQL = N'ALTER TABLE [repository] DROP CONSTRAINT [' + @ConstraintName + N']'
 | 
			
		||||
		EXEC sp_executesql @SQL`)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	} else if models.DbCfg.Type == core.MYSQL {
 | 
			
		||||
		indexes, err := sess.QueryString(`SHOW INDEX FROM repository WHERE KEY_NAME = 'IDX_repository_is_bare'`)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(indexes) >= 1 {
 | 
			
		||||
			_, err = sess.Exec("DROP INDEX IDX_repository_is_bare ON repository")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("Drop index failed: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		_, err = sess.Exec("DROP INDEX IDX_repository_is_bare ON repository")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Drop index failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = sess.Commit(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := sess.Sync2(new(Repository)); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := sess.Exec("UPDATE repository SET is_empty = is_bare;"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if models.DbCfg.Type != core.SQLITE {
 | 
			
		||||
		_, err = sess.Exec("ALTER TABLE repository DROP COLUMN is_bare")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Drop column failed: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = sess.Commit(); err != nil {
 | 
			
		||||
	if err := sess.Commit(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if models.DbCfg.Type == core.SQLITE {
 | 
			
		||||
		log.Warn("TABLE repository's COLUMN is_bare should be DROP but sqlite is not supported, you could manually do that.")
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	if err := dropTableColumns(sess, "repository", "is_bare"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -91,6 +91,7 @@ func fixReleaseSha1OnReleaseTable(x *xorm.Engine) error {
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				defer gitRepo.Close()
 | 
			
		||||
				gitRepoCache[release.RepoID] = gitRepo
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-xorm/xorm"
 | 
			
		||||
	"xorm.io/core"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/generate"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/util"
 | 
			
		||||
@@ -37,41 +35,6 @@ func hashAppToken(x *xorm.Engine) error {
 | 
			
		||||
	// First remove the index
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	if models.DbCfg.Type == core.POSTGRES || models.DbCfg.Type == core.SQLITE {
 | 
			
		||||
		_, err = sess.Exec("DROP INDEX IF EXISTS UQE_access_token_sha1")
 | 
			
		||||
	} else if models.DbCfg.Type == core.MSSQL {
 | 
			
		||||
		_, err = sess.Exec(`DECLARE @ConstraintName VARCHAR(256)
 | 
			
		||||
		DECLARE @SQL NVARCHAR(256)
 | 
			
		||||
		SELECT @ConstraintName = obj.name FROM sys.columns col LEFT OUTER JOIN sys.objects obj ON obj.object_id = col.default_object_id AND obj.type = 'D' WHERE col.object_id = OBJECT_ID('access_token') AND obj.name IS NOT NULL AND col.name = 'sha1'
 | 
			
		||||
		SET @SQL = N'ALTER TABLE [access_token] DROP CONSTRAINT [' + @ConstraintName + N']'
 | 
			
		||||
		EXEC sp_executesql @SQL`)
 | 
			
		||||
	} else if models.DbCfg.Type == core.MYSQL {
 | 
			
		||||
		indexes, err := sess.QueryString(`SHOW INDEX FROM access_token WHERE KEY_NAME = 'UQE_access_token_sha1'`)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(indexes) >= 1 {
 | 
			
		||||
			_, err = sess.Exec("DROP INDEX UQE_access_token_sha1 ON access_token")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		_, err = sess.Exec("DROP INDEX UQE_access_token_sha1 ON access_token")
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Drop index failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = sess.Commit(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -81,7 +44,7 @@ func hashAppToken(x *xorm.Engine) error {
 | 
			
		||||
		return fmt.Errorf("Sync2: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = sess.Commit(); err != nil {
 | 
			
		||||
	if err := sess.Commit(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -252,7 +252,7 @@ func (t *Team) UnitEnabled(tp UnitType) bool {
 | 
			
		||||
 | 
			
		||||
func (t *Team) unitEnabled(e Engine, tp UnitType) bool {
 | 
			
		||||
	if err := t.getUnits(e); err != nil {
 | 
			
		||||
		log.Warn("Error loading repository (ID: %d) units: %s", t.ID, err.Error())
 | 
			
		||||
		log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, unit := range t.Units {
 | 
			
		||||
@@ -755,11 +755,14 @@ func IsUserInTeams(userID int64, teamIDs []int64) (bool, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UsersInTeamsCount counts the number of users which are in userIDs and teamIDs
 | 
			
		||||
func UsersInTeamsCount(userIDs []int64, teamIDs []int64) (count int64, err error) {
 | 
			
		||||
	if count, err = x.In("uid", userIDs).In("team_id", teamIDs).Count(new(TeamUser)); err != nil {
 | 
			
		||||
func UsersInTeamsCount(userIDs []int64, teamIDs []int64) (int64, error) {
 | 
			
		||||
	var ids []int64
 | 
			
		||||
	if err := x.In("uid", userIDs).In("team_id", teamIDs).
 | 
			
		||||
		Table("team_user").
 | 
			
		||||
		Cols("uid").GroupBy("uid").Find(&ids); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
	return int64(len(ids)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ___________                  __________
 | 
			
		||||
 
 | 
			
		||||
@@ -370,7 +370,7 @@ func TestUsersInTeamsCount(t *testing.T) {
 | 
			
		||||
		assert.Equal(t, expected, count)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	test([]int64{2}, []int64{1, 2, 3, 4}, 2)
 | 
			
		||||
	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)
 | 
			
		||||
	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3)
 | 
			
		||||
	test([]int64{2}, []int64{1, 2, 3, 4}, 1)          // only userid 2
 | 
			
		||||
	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4
 | 
			
		||||
	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								models/pull.go
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								models/pull.go
									
									
									
									
									
								
							@@ -189,36 +189,6 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if baseBranch, err = pr.BaseRepo.GetBranch(pr.BaseBranch); err != nil {
 | 
			
		||||
		log.Error("pr.BaseRepo.GetBranch[%d]: %v", pr.BaseBranch, err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if baseCommit, err = baseBranch.GetCommit(); err != nil {
 | 
			
		||||
		log.Error("baseBranch.GetCommit[%d]: %v", pr.ID, err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch); err != nil {
 | 
			
		||||
		log.Error("pr.HeadRepo.GetBranch[%d]: %v", pr.HeadBranch, err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if headCommit, err = headBranch.GetCommit(); err != nil {
 | 
			
		||||
		log.Error("headBranch.GetCommit[%d]: %v", pr.ID, err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	apiBaseBranchInfo := &api.PRBranchInfo{
 | 
			
		||||
		Name:       pr.BaseBranch,
 | 
			
		||||
		Ref:        pr.BaseBranch,
 | 
			
		||||
		Sha:        baseCommit.ID.String(),
 | 
			
		||||
		RepoID:     pr.BaseRepoID,
 | 
			
		||||
		Repository: pr.BaseRepo.innerAPIFormat(e, AccessModeNone, false),
 | 
			
		||||
	}
 | 
			
		||||
	apiHeadBranchInfo := &api.PRBranchInfo{
 | 
			
		||||
		Name:       pr.HeadBranch,
 | 
			
		||||
		Ref:        pr.HeadBranch,
 | 
			
		||||
		Sha:        headCommit.ID.String(),
 | 
			
		||||
		RepoID:     pr.HeadRepoID,
 | 
			
		||||
		Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = pr.Issue.loadRepo(e); err != nil {
 | 
			
		||||
		log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
 | 
			
		||||
@@ -227,6 +197,7 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
 | 
			
		||||
 | 
			
		||||
	apiPullRequest := &api.PullRequest{
 | 
			
		||||
		ID:        pr.ID,
 | 
			
		||||
		URL:       pr.Issue.HTMLURL(),
 | 
			
		||||
		Index:     pr.Index,
 | 
			
		||||
		Poster:    apiIssue.Poster,
 | 
			
		||||
		Title:     apiIssue.Title,
 | 
			
		||||
@@ -241,13 +212,68 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
 | 
			
		||||
		DiffURL:   pr.Issue.DiffURL(),
 | 
			
		||||
		PatchURL:  pr.Issue.PatchURL(),
 | 
			
		||||
		HasMerged: pr.HasMerged,
 | 
			
		||||
		Base:      apiBaseBranchInfo,
 | 
			
		||||
		Head:      apiHeadBranchInfo,
 | 
			
		||||
		MergeBase: pr.MergeBase,
 | 
			
		||||
		Deadline:  apiIssue.Deadline,
 | 
			
		||||
		Created:   pr.Issue.CreatedUnix.AsTimePtr(),
 | 
			
		||||
		Updated:   pr.Issue.UpdatedUnix.AsTimePtr(),
 | 
			
		||||
	}
 | 
			
		||||
	baseBranch, err = pr.BaseRepo.GetBranch(pr.BaseBranch)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if git.IsErrBranchNotExist(err) {
 | 
			
		||||
			apiPullRequest.Base = nil
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Error("GetBranch[%s]: %v", pr.BaseBranch, err)
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		apiBaseBranchInfo := &api.PRBranchInfo{
 | 
			
		||||
			Name:       pr.BaseBranch,
 | 
			
		||||
			Ref:        pr.BaseBranch,
 | 
			
		||||
			RepoID:     pr.BaseRepoID,
 | 
			
		||||
			Repository: pr.BaseRepo.innerAPIFormat(e, AccessModeNone, false),
 | 
			
		||||
		}
 | 
			
		||||
		baseCommit, err = baseBranch.GetCommit()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if git.IsErrNotExist(err) {
 | 
			
		||||
				apiBaseBranchInfo.Sha = ""
 | 
			
		||||
			} else {
 | 
			
		||||
				log.Error("GetCommit[%s]: %v", baseBranch.Name, err)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			apiBaseBranchInfo.Sha = baseCommit.ID.String()
 | 
			
		||||
		}
 | 
			
		||||
		apiPullRequest.Base = apiBaseBranchInfo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if git.IsErrBranchNotExist(err) {
 | 
			
		||||
			apiPullRequest.Head = nil
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		apiHeadBranchInfo := &api.PRBranchInfo{
 | 
			
		||||
			Name:       pr.HeadBranch,
 | 
			
		||||
			Ref:        pr.HeadBranch,
 | 
			
		||||
			RepoID:     pr.HeadRepoID,
 | 
			
		||||
			Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
 | 
			
		||||
		}
 | 
			
		||||
		headCommit, err = headBranch.GetCommit()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if git.IsErrNotExist(err) {
 | 
			
		||||
				apiHeadBranchInfo.Sha = ""
 | 
			
		||||
			} else {
 | 
			
		||||
				log.Error("GetCommit[%s]: %v", headBranch.Name, err)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			apiHeadBranchInfo.Sha = headCommit.ID.String()
 | 
			
		||||
		}
 | 
			
		||||
		apiPullRequest.Head = apiHeadBranchInfo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pr.Status != PullRequestStatusChecking {
 | 
			
		||||
		mergeable := pr.Status != PullRequestStatusConflict && !pr.IsWorkInProgress()
 | 
			
		||||
@@ -312,15 +338,19 @@ func (pr *PullRequest) GetLastCommitStatus() (status *CommitStatus, err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer headGitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	repo := pr.HeadRepo
 | 
			
		||||
	lastCommitID, err := headGitRepo.GetBranchCommitID(pr.HeadBranch)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var statusList []*CommitStatus
 | 
			
		||||
	statusList, err = GetLatestCommitStatus(repo, lastCommitID, 0)
 | 
			
		||||
	err = pr.GetBaseRepo()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	statusList, err := GetLatestCommitStatus(pr.BaseRepo, lastCommitID, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -498,6 +528,7 @@ func (pr *PullRequest) getMergeCommit() (*git.Commit, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	commit, err := gitRepo.GetCommit(mergeCommit[:40])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -572,7 +603,7 @@ func (pr *PullRequest) testPatch(e Engine) (err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		for i := range patchConflicts {
 | 
			
		||||
			if strings.Contains(stderr, patchConflicts[i]) {
 | 
			
		||||
				log.Trace("PullRequest[%d].testPatch (apply): has conflict", pr.ID)
 | 
			
		||||
				log.Trace("PullRequest[%d].testPatch (apply): has conflict: %s", pr.ID, stderr)
 | 
			
		||||
				const prefix = "error: patch failed:"
 | 
			
		||||
				pr.Status = PullRequestStatusConflict
 | 
			
		||||
				pr.ConflictedFiles = make([]string, 0, 5)
 | 
			
		||||
@@ -635,13 +666,16 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr.Index = pull.Index
 | 
			
		||||
	if err = repo.savePatch(sess, pr.Index, patch); err != nil {
 | 
			
		||||
		return fmt.Errorf("SavePatch: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr.BaseRepo = repo
 | 
			
		||||
	if err = pr.testPatch(sess); err != nil {
 | 
			
		||||
		return fmt.Errorf("testPatch: %v", err)
 | 
			
		||||
	pr.Status = PullRequestStatusChecking
 | 
			
		||||
	if len(patch) > 0 {
 | 
			
		||||
		if err = repo.savePatch(sess, pr.Index, patch); err != nil {
 | 
			
		||||
			return fmt.Errorf("SavePatch: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err = pr.testPatch(sess); err != nil {
 | 
			
		||||
			return fmt.Errorf("testPatch: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// No conflict appears after test means mergeable.
 | 
			
		||||
	if pr.Status == PullRequestStatusChecking {
 | 
			
		||||
@@ -888,6 +922,7 @@ func (pr *PullRequest) UpdatePatch() (err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer headGitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	// Add a temporary remote.
 | 
			
		||||
	tmpRemote := com.ToStr(time.Now().UnixNano())
 | 
			
		||||
@@ -929,6 +964,7 @@ func (pr *PullRequest) PushToBaseRepo() (err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer headGitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
 | 
			
		||||
	if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
 | 
			
		||||
@@ -1118,6 +1154,7 @@ func checkForInvalidation(requests PullRequestList, repoID int64, doer *User, br
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error("PullRequestList.InvalidateCodeComments: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		gitRepo.Close()
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,15 @@ func TestPullRequest_LoadIssue(t *testing.T) {
 | 
			
		||||
	assert.Equal(t, int64(2), pr.Issue.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO TestPullRequest_APIFormat
 | 
			
		||||
func TestPullRequest_APIFormat(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, PrepareTestDatabase())
 | 
			
		||||
	pr := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
 | 
			
		||||
	assert.NoError(t, pr.LoadAttributes())
 | 
			
		||||
	assert.NoError(t, pr.LoadIssue())
 | 
			
		||||
	apiPullRequest := pr.APIFormat()
 | 
			
		||||
	assert.NotNil(t, apiPullRequest)
 | 
			
		||||
	assert.Nil(t, apiPullRequest.Head)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPullRequest_GetBaseRepo(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, PrepareTestDatabase())
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ func (r *Release) LoadAttributes() error {
 | 
			
		||||
 | 
			
		||||
// APIURL the api url for a release. release must have attributes loaded
 | 
			
		||||
func (r *Release) APIURL() string {
 | 
			
		||||
	return fmt.Sprintf("%sapi/v1/%s/releases/%d",
 | 
			
		||||
	return fmt.Sprintf("%sapi/v1/repos/%s/releases/%d",
 | 
			
		||||
		setting.AppURL, r.Repo.FullName(), r.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -397,19 +397,21 @@ func UpdateRelease(doer *User, gitRepo *git.Repository, rel *Release, attachment
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = addReleaseAttachments(rel.ID, attachmentUUIDs); err != nil {
 | 
			
		||||
		log.Error("addReleaseAttachments: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = rel.loadAttributes(x)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = addReleaseAttachments(rel.ID, attachmentUUIDs)
 | 
			
		||||
 | 
			
		||||
	mode, _ := AccessLevel(doer, rel.Repo)
 | 
			
		||||
	if err1 := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{
 | 
			
		||||
		Action:     api.HookReleaseUpdated,
 | 
			
		||||
		Release:    rel.APIFormat(),
 | 
			
		||||
		Repository: rel.Repo.APIFormat(mode),
 | 
			
		||||
		Sender:     rel.Publisher.APIFormat(),
 | 
			
		||||
		Sender:     doer.APIFormat(),
 | 
			
		||||
	}); err1 != nil {
 | 
			
		||||
		log.Error("PrepareWebhooks: %v", err)
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -420,7 +422,7 @@ func UpdateRelease(doer *User, gitRepo *git.Repository, rel *Release, attachment
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
 | 
			
		||||
func DeleteReleaseByID(id int64, u *User, delTag bool) error {
 | 
			
		||||
func DeleteReleaseByID(id int64, doer *User, delTag bool) error {
 | 
			
		||||
	rel, err := GetReleaseByID(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("GetReleaseByID: %v", err)
 | 
			
		||||
@@ -459,12 +461,12 @@ func DeleteReleaseByID(id int64, u *User, delTag bool) error {
 | 
			
		||||
		return fmt.Errorf("LoadAttributes: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mode, _ := AccessLevel(u, rel.Repo)
 | 
			
		||||
	mode, _ := AccessLevel(doer, rel.Repo)
 | 
			
		||||
	if err := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{
 | 
			
		||||
		Action:     api.HookReleaseDeleted,
 | 
			
		||||
		Release:    rel.APIFormat(),
 | 
			
		||||
		Repository: rel.Repo.APIFormat(mode),
 | 
			
		||||
		Sender:     rel.Publisher.APIFormat(),
 | 
			
		||||
		Sender:     doer.APIFormat(),
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		log.Error("PrepareWebhooks: %v", err)
 | 
			
		||||
	} else {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ func TestRelease_Create(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	gitRepo, err := git.OpenRepository(repoPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, CreateRelease(gitRepo, &Release{
 | 
			
		||||
		RepoID:       repo.ID,
 | 
			
		||||
@@ -115,6 +116,7 @@ func TestRelease_MirrorDelete(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	gitRepo, err := git.OpenRepository(repoPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	findOptions := FindReleasesOptions{IncludeDrafts: true, IncludeTags: true}
 | 
			
		||||
	initCount, err := GetReleaseCountByRepoID(mirror.ID, findOptions)
 | 
			
		||||
 
 | 
			
		||||
@@ -134,8 +134,8 @@ type Repository struct {
 | 
			
		||||
	Owner         *User  `xorm:"-"`
 | 
			
		||||
	LowerName     string `xorm:"UNIQUE(s) INDEX NOT NULL"`
 | 
			
		||||
	Name          string `xorm:"INDEX NOT NULL"`
 | 
			
		||||
	Description   string
 | 
			
		||||
	Website       string
 | 
			
		||||
	Description   string `xorm:"TEXT"`
 | 
			
		||||
	Website       string `xorm:"VARCHAR(2048)"`
 | 
			
		||||
	DefaultBranch string
 | 
			
		||||
 | 
			
		||||
	NumWatches          int
 | 
			
		||||
@@ -707,11 +707,24 @@ func (repo *Repository) CanEnableEditor() bool {
 | 
			
		||||
	return !repo.IsMirror
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetReaders returns all users that have explicit read access or higher to the repository.
 | 
			
		||||
func (repo *Repository) GetReaders() (_ []*User, err error) {
 | 
			
		||||
	return repo.getUsersWithAccessMode(x, AccessModeRead)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetWriters returns all users that have write access to the repository.
 | 
			
		||||
func (repo *Repository) GetWriters() (_ []*User, err error) {
 | 
			
		||||
	return repo.getUsersWithAccessMode(x, AccessModeWrite)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsReader returns true if user has explicit read access or higher to the repository.
 | 
			
		||||
func (repo *Repository) IsReader(userID int64) (bool, error) {
 | 
			
		||||
	if repo.OwnerID == userID {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return x.Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, AccessModeRead).Get(&Access{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
 | 
			
		||||
func (repo *Repository) getUsersWithAccessMode(e Engine, mode AccessMode) (_ []*User, err error) {
 | 
			
		||||
	if err = repo.getOwner(e); err != nil {
 | 
			
		||||
@@ -937,6 +950,7 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return repo, fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	repo.IsEmpty, err = gitRepo.IsEmpty()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -1050,7 +1064,7 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
 | 
			
		||||
	_, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
 | 
			
		||||
	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
 | 
			
		||||
		return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -1324,7 +1338,6 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
 | 
			
		||||
		}); err != nil {
 | 
			
		||||
			return fmt.Errorf("prepareWebhooks: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		go HookQueue.Add(repo.ID)
 | 
			
		||||
	} else if err = repo.recalculateAccesses(e); err != nil {
 | 
			
		||||
		// Organization automatically called this in addRepository method.
 | 
			
		||||
		return fmt.Errorf("recalculateAccesses: %v", err)
 | 
			
		||||
@@ -1393,7 +1406,16 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return repo, sess.Commit()
 | 
			
		||||
	if err = sess.Commit(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add to hook queue for created repo after session commit.
 | 
			
		||||
	if u.IsOrganization() {
 | 
			
		||||
		go HookQueue.Add(repo.ID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return repo, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func countRepositories(userID int64, private bool) int64 {
 | 
			
		||||
@@ -1465,6 +1487,13 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
 | 
			
		||||
		return fmt.Errorf("update owner: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update pull request headusername
 | 
			
		||||
	if _, err := sess.Where("head_repo_id = ?", repo.ID).Update(&PullRequest{
 | 
			
		||||
		HeadUserName: newOwner.LowerName,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return fmt.Errorf("update pull request: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove redundant collaborators.
 | 
			
		||||
	collaborators, err := repo.getCollaborators(sess)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -1783,6 +1812,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
			
		||||
		&HookTask{RepoID: repoID},
 | 
			
		||||
		&Notification{RepoID: repoID},
 | 
			
		||||
		&CommitStatus{RepoID: repoID},
 | 
			
		||||
		&RepoIndexerStatus{RepoID: repoID},
 | 
			
		||||
	); err != nil {
 | 
			
		||||
		return fmt.Errorf("deleteBeans: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -1873,12 +1903,11 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if count > 1 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		oidPath := filepath.Join(v.Oid[0:2], v.Oid[2:4], v.Oid[4:len(v.Oid)])
 | 
			
		||||
		oidPath := filepath.Join(setting.LFS.ContentPath, v.Oid[0:2], v.Oid[2:4], v.Oid[4:len(v.Oid)])
 | 
			
		||||
		removeAllWithNotice(sess, "Delete orphaned LFS file", oidPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2327,6 +2356,23 @@ func CheckRepoStats() {
 | 
			
		||||
	}
 | 
			
		||||
	// ***** END: Repository.NumClosedIssues *****
 | 
			
		||||
 | 
			
		||||
	// ***** START: Repository.NumClosedPulls *****
 | 
			
		||||
	desc = "repository count 'num_closed_pulls'"
 | 
			
		||||
	results, err = x.Query("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("Select %s: %v", desc, err)
 | 
			
		||||
	} else {
 | 
			
		||||
		for _, result := range results {
 | 
			
		||||
			id := com.StrTo(result["id"]).MustInt64()
 | 
			
		||||
			log.Trace("Updating %s: %d", desc, id)
 | 
			
		||||
			_, err = x.Exec("UPDATE `repository` SET num_closed_pulls=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, true, id)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("Update %s[%d]: %v", desc, id, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// ***** END: Repository.NumClosedPulls *****
 | 
			
		||||
 | 
			
		||||
	// FIXME: use checker when stop supporting old fork repo format.
 | 
			
		||||
	// ***** START: Repository.NumForks *****
 | 
			
		||||
	results, err = x.Query("SELECT repo.id FROM `repository` repo WHERE repo.num_forks!=(SELECT COUNT(*) FROM `repository` WHERE fork_id=repo.id)")
 | 
			
		||||
@@ -2461,6 +2507,11 @@ func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *R
 | 
			
		||||
		go HookQueue.Add(oldRepo.ID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add to hook queue for created repo after session commit.
 | 
			
		||||
	if u.IsOrganization() {
 | 
			
		||||
		go HookQueue.Add(repo.ID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = repo.UpdateSize(); err != nil {
 | 
			
		||||
		log.Error("Failed to update size for repository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,8 @@ func GetActivityStats(repo *Repository, timeFrom time.Time, releases, issues, pr
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		code, err := gitRepo.GetCodeActivityStats(timeFrom, repo.DefaultBranch)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("FillFromGit: %v", err)
 | 
			
		||||
@@ -79,6 +81,8 @@ func GetActivityStatsTopAuthors(repo *Repository, timeFrom time.Time, count int)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	code, err := gitRepo.GetCodeActivityStats(timeFrom, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("FillFromGit: %v", err)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ func (repo *Repository) GetBranch(branch string) (*git.Branch, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	return gitRepo.GetBranch(branch)
 | 
			
		||||
}
 | 
			
		||||
@@ -38,6 +39,7 @@ func (repo *Repository) CheckBranchName(name string) error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	branches, err := repo.GetBranches()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -94,6 +96,7 @@ func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName st
 | 
			
		||||
		log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
 | 
			
		||||
		return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	if err = gitRepo.CreateBranch(branchName, oldBranchName); err != nil {
 | 
			
		||||
		log.Error("Unable to create branch: %s from %s. (%v)", branchName, oldBranchName, err)
 | 
			
		||||
@@ -140,6 +143,7 @@ func (repo *Repository) CreateNewBranchFromCommit(doer *User, commit, branchName
 | 
			
		||||
		log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
 | 
			
		||||
		return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	if err = gitRepo.CreateBranch(branchName, commit); err != nil {
 | 
			
		||||
		log.Error("Unable to create branch: %s from %s. (%v)", branchName, commit, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -199,7 +199,7 @@ func addUpdate(update fileUpdate, repo *Repository, batch rupture.FlushingBatch)
 | 
			
		||||
	if size, err := strconv.Atoi(strings.TrimSpace(stdout)); err != nil {
 | 
			
		||||
		return fmt.Errorf("Misformatted git cat-file output: %v", err)
 | 
			
		||||
	} else if int64(size) > setting.Indexer.MaxIndexerFileSize {
 | 
			
		||||
		return nil
 | 
			
		||||
		return addDelete(update.Filename, repo, batch)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fileContents, err := git.NewCommand("cat-file", "blob", update.BlobSha).
 | 
			
		||||
@@ -231,20 +231,28 @@ func addDelete(filename string, repo *Repository, batch rupture.FlushingBatch) e
 | 
			
		||||
	return indexerUpdate.AddToFlushingBatch(batch)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isIndexable(entry *git.TreeEntry) bool {
 | 
			
		||||
	return entry.IsRegular() || entry.IsExecutable()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command
 | 
			
		||||
func parseGitLsTreeOutput(stdout []byte) ([]fileUpdate, error) {
 | 
			
		||||
	entries, err := git.ParseTreeEntries(stdout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var idxCount = 0
 | 
			
		||||
	updates := make([]fileUpdate, len(entries))
 | 
			
		||||
	for i, entry := range entries {
 | 
			
		||||
		updates[i] = fileUpdate{
 | 
			
		||||
			Filename: entry.Name(),
 | 
			
		||||
			BlobSha:  entry.ID.String(),
 | 
			
		||||
	for _, entry := range entries {
 | 
			
		||||
		if isIndexable(entry) {
 | 
			
		||||
			updates[idxCount] = fileUpdate{
 | 
			
		||||
				Filename: entry.Name(),
 | 
			
		||||
				BlobSha:  entry.ID.String(),
 | 
			
		||||
			}
 | 
			
		||||
			idxCount++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return updates, nil
 | 
			
		||||
	return updates[:idxCount], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// genesisChanges get changes to add repo to the indexer for the first time
 | 
			
		||||
 
 | 
			
		||||
@@ -134,7 +134,7 @@ func (m *Mirror) FullAddress() string {
 | 
			
		||||
func (m *Mirror) SaveAddress(addr string) error {
 | 
			
		||||
	repoPath := m.Repo.RepoPath()
 | 
			
		||||
	// Remove old origin
 | 
			
		||||
	_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
 | 
			
		||||
	_, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
 | 
			
		||||
	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -242,6 +242,7 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) {
 | 
			
		||||
	if err = SyncReleasesWithTags(m.Repo, gitRepo); err != nil {
 | 
			
		||||
		log.Error("Failed to synchronize tags to releases for repository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	if err := m.Repo.UpdateSize(); err != nil {
 | 
			
		||||
		log.Error("Failed to update size for mirror repository: %v", err)
 | 
			
		||||
@@ -426,6 +427,8 @@ func SyncMirrors() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
		// Get latest commit date and update to current repository updated time
 | 
			
		||||
		commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
// Copyright 2019 The Gitea 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 models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"code.gitea.io/gitea/modules/git"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetTagsByPath returns repo tags by its path
 | 
			
		||||
func GetTagsByPath(path string) ([]*git.Tag, error) {
 | 
			
		||||
	gitRepo, err := git.OpenRepository(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gitRepo.GetTagInfos()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTags return repo's tags
 | 
			
		||||
func (repo *Repository) GetTags() ([]*git.Tag, error) {
 | 
			
		||||
	return GetTagsByPath(repo.RepoPath())
 | 
			
		||||
}
 | 
			
		||||
@@ -32,7 +32,7 @@ import (
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	tplCommentPrefix = `# gitea public key`
 | 
			
		||||
	tplPublicKey     = tplCommentPrefix + "\n" + `command="%s serv key-%d --config='%s'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
 | 
			
		||||
	tplPublicKey     = tplCommentPrefix + "\n" + `command="%s --config='%s' serv key-%d",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var sshOpLocker sync.Mutex
 | 
			
		||||
@@ -77,7 +77,7 @@ func (key *PublicKey) OmitEmail() string {
 | 
			
		||||
 | 
			
		||||
// AuthorizedString returns formatted public key string for authorized_keys file.
 | 
			
		||||
func (key *PublicKey) AuthorizedString() string {
 | 
			
		||||
	return fmt.Sprintf(tplPublicKey, setting.AppPath, key.ID, setting.CustomConf, key.Content)
 | 
			
		||||
	return fmt.Sprintf(tplPublicKey, setting.AppPath, setting.CustomConf, key.ID, key.Content)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractTypeFromBase64Key(key string) (string, error) {
 | 
			
		||||
@@ -315,6 +315,18 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
 | 
			
		||||
	sshOpLocker.Lock()
 | 
			
		||||
	defer sshOpLocker.Unlock()
 | 
			
		||||
 | 
			
		||||
	if setting.SSH.RootPath != "" {
 | 
			
		||||
		// First of ensure that the RootPath is present, and if not make it with 0700 permissions
 | 
			
		||||
		// This of course doesn't guarantee that this is the right directory for authorized_keys
 | 
			
		||||
		// but at least if it's supposed to be this directory and it doesn't exist and we're the
 | 
			
		||||
		// right user it will at least be created properly.
 | 
			
		||||
		err := os.MkdirAll(setting.SSH.RootPath, 0700)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
 | 
			
		||||
	f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -602,6 +614,18 @@ func rewriteAllPublicKeys(e Engine) error {
 | 
			
		||||
	sshOpLocker.Lock()
 | 
			
		||||
	defer sshOpLocker.Unlock()
 | 
			
		||||
 | 
			
		||||
	if setting.SSH.RootPath != "" {
 | 
			
		||||
		// First of ensure that the RootPath is present, and if not make it with 0700 permissions
 | 
			
		||||
		// This of course doesn't guarantee that this is the right directory for authorized_keys
 | 
			
		||||
		// but at least if it's supposed to be this directory and it doesn't exist and we're the
 | 
			
		||||
		// right user it will at least be created properly.
 | 
			
		||||
		err := os.MkdirAll(setting.SSH.RootPath, 0700)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
 | 
			
		||||
	tmpPath := fPath + ".tmp"
 | 
			
		||||
	t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
 | 
			
		||||
@@ -642,12 +666,14 @@ func rewriteAllPublicKeys(e Engine) error {
 | 
			
		||||
			}
 | 
			
		||||
			_, err = t.WriteString(line + "\n")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				f.Close()
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		defer f.Close()
 | 
			
		||||
		f.Close()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.Close()
 | 
			
		||||
	return os.Rename(tmpPath, fPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -211,6 +211,7 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	if err = repo.UpdateSize(); err != nil {
 | 
			
		||||
		log.Error("Failed to update size for repository: %v", err)
 | 
			
		||||
 
 | 
			
		||||
@@ -195,9 +195,9 @@ func (u *User) UpdateTheme(themeName string) error {
 | 
			
		||||
	return UpdateUserCols(u, "theme")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getEmail returns an noreply email, if the user has set to keep his
 | 
			
		||||
// GetEmail returns an noreply email, if the user has set to keep his
 | 
			
		||||
// email address private, otherwise the primary email address.
 | 
			
		||||
func (u *User) getEmail() string {
 | 
			
		||||
func (u *User) GetEmail() string {
 | 
			
		||||
	if u.KeepEmailPrivate {
 | 
			
		||||
		return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)
 | 
			
		||||
	}
 | 
			
		||||
@@ -210,7 +210,7 @@ func (u *User) APIFormat() *api.User {
 | 
			
		||||
		ID:        u.ID,
 | 
			
		||||
		UserName:  u.Name,
 | 
			
		||||
		FullName:  u.FullName,
 | 
			
		||||
		Email:     u.getEmail(),
 | 
			
		||||
		Email:     u.GetEmail(),
 | 
			
		||||
		AvatarURL: u.AvatarLink(),
 | 
			
		||||
		Language:  u.Language,
 | 
			
		||||
		IsAdmin:   u.IsAdmin,
 | 
			
		||||
@@ -425,7 +425,7 @@ func (u *User) GetFollowing(page int) ([]*User, error) {
 | 
			
		||||
func (u *User) NewGitSig() *git.Signature {
 | 
			
		||||
	return &git.Signature{
 | 
			
		||||
		Name:  u.GitName(),
 | 
			
		||||
		Email: u.getEmail(),
 | 
			
		||||
		Email: u.GetEmail(),
 | 
			
		||||
		When:  time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -751,6 +751,7 @@ var (
 | 
			
		||||
		"robots.txt",
 | 
			
		||||
		".",
 | 
			
		||||
		"..",
 | 
			
		||||
		".well-known",
 | 
			
		||||
	}
 | 
			
		||||
	reservedUserPatterns = []string{"*.keys", "*.gpg"}
 | 
			
		||||
)
 | 
			
		||||
@@ -1375,9 +1376,7 @@ type SearchUserOptions struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (opts *SearchUserOptions) toConds() builder.Cond {
 | 
			
		||||
 | 
			
		||||
	var cond = builder.NewCond()
 | 
			
		||||
	cond = cond.And(builder.Eq{"type": opts.Type})
 | 
			
		||||
	var cond builder.Cond = builder.Eq{"type": opts.Type}
 | 
			
		||||
 | 
			
		||||
	if len(opts.Keyword) > 0 {
 | 
			
		||||
		lowerKeyword := strings.ToLower(opts.Keyword)
 | 
			
		||||
@@ -1646,7 +1645,12 @@ func SyncExternalUsers() {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			sr := s.LDAP().SearchEntries()
 | 
			
		||||
			sr, err := s.LDAP().SearchEntries()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("SyncExternalUsers LDAP source failure [%s], skipped", s.Name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, su := range sr {
 | 
			
		||||
				if len(su.Username) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
 | 
			
		||||
		CountResult int
 | 
			
		||||
		JSONResult  string
 | 
			
		||||
	}{
 | 
			
		||||
		{2, 1, `[{"timestamp":1540080000,"contributions":1}]`},
 | 
			
		||||
		{2, 1, `[{"timestamp":1571616000,"contributions":1}]`},
 | 
			
		||||
		{3, 0, `[]`},
 | 
			
		||||
	}
 | 
			
		||||
	// Prepare
 | 
			
		||||
@@ -41,7 +41,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
 | 
			
		||||
		// Get the heatmap and compare
 | 
			
		||||
		heatmap, err := GetUserHeatmapDataByUser(user)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Equal(t, len(actions), len(heatmap))
 | 
			
		||||
		assert.Equal(t, len(actions), len(heatmap), "invalid action count: did the test data became too old?")
 | 
			
		||||
		assert.Equal(t, tc.CountResult, len(heatmap))
 | 
			
		||||
 | 
			
		||||
		//Test JSON rendering
 | 
			
		||||
 
 | 
			
		||||
@@ -890,7 +890,6 @@ func DeliverHooks() {
 | 
			
		||||
	for _, t := range tasks {
 | 
			
		||||
		if err = t.deliver(); err != nil {
 | 
			
		||||
			log.Error("deliver: %v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -236,6 +236,7 @@ func getMSTeamsPushPayload(p *api.PushPayload) (*MSTeamsPayload, error) {
 | 
			
		||||
				ActivityTitle:    p.Sender.FullName,
 | 
			
		||||
				ActivitySubtitle: p.Sender.UserName,
 | 
			
		||||
				ActivityImage:    p.Sender.AvatarURL,
 | 
			
		||||
				Text:             text,
 | 
			
		||||
				Facts: []MSTeamsFact{
 | 
			
		||||
					{
 | 
			
		||||
						Name:  "Repository:",
 | 
			
		||||
 
 | 
			
		||||
@@ -120,8 +120,8 @@ func getSlackDeletePayload(p *api.DeletePayload, slack *SlackMeta) (*SlackPayloa
 | 
			
		||||
 | 
			
		||||
// getSlackForkPayload composes Slack payload for forked by a repository.
 | 
			
		||||
func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, error) {
 | 
			
		||||
	baseLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
 | 
			
		||||
	forkLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
 | 
			
		||||
	baseLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
 | 
			
		||||
	forkLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
 | 
			
		||||
	text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
 | 
			
		||||
	return &SlackPayload{
 | 
			
		||||
		Channel:  slack.Channel,
 | 
			
		||||
 
 | 
			
		||||
@@ -140,6 +140,7 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con
 | 
			
		||||
		log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
 | 
			
		||||
		return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	if hasMasterBranch {
 | 
			
		||||
		if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil {
 | 
			
		||||
@@ -217,7 +218,13 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con
 | 
			
		||||
	if err := git.Push(basePath, git.PushOptions{
 | 
			
		||||
		Remote: "origin",
 | 
			
		||||
		Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
 | 
			
		||||
		Env:    PushingEnvironment(doer, repo),
 | 
			
		||||
		Env: FullPushingEnvironment(
 | 
			
		||||
			doer,
 | 
			
		||||
			doer,
 | 
			
		||||
			repo,
 | 
			
		||||
			repo.Name+".wiki",
 | 
			
		||||
			0,
 | 
			
		||||
		),
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		log.Error("%v", err)
 | 
			
		||||
		return fmt.Errorf("Push: %v", err)
 | 
			
		||||
@@ -270,6 +277,7 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error)
 | 
			
		||||
		log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
 | 
			
		||||
		return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil {
 | 
			
		||||
		log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -161,6 +161,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
 | 
			
		||||
			// Now need to show that the page has been added:
 | 
			
		||||
			gitRepo, err := git.OpenRepository(repo.WikiPath())
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
			defer gitRepo.Close()
 | 
			
		||||
			masterTree, err := gitRepo.GetTree("master")
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
			wikiPath := WikiNameToFilename(wikiName)
 | 
			
		||||
@@ -214,6 +215,7 @@ func TestRepository_EditWikiPage(t *testing.T) {
 | 
			
		||||
			_, err := masterTree.GetTreeEntryByPath("Home.md")
 | 
			
		||||
			assert.Error(t, err)
 | 
			
		||||
		}
 | 
			
		||||
		gitRepo.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -226,6 +228,7 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
 | 
			
		||||
	// Now need to show that the page has been added:
 | 
			
		||||
	gitRepo, err := git.OpenRepository(repo.WikiPath())
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
	masterTree, err := gitRepo.GetTree("master")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	wikiPath := WikiNameToFilename("Home")
 | 
			
		||||
 
 | 
			
		||||
@@ -308,12 +308,12 @@ func (ls *Source) UsePagedSearch() bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SearchEntries : search an LDAP source for all users matching userFilter
 | 
			
		||||
func (ls *Source) SearchEntries() []*SearchResult {
 | 
			
		||||
func (ls *Source) SearchEntries() ([]*SearchResult, error) {
 | 
			
		||||
	l, err := dial(ls)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("LDAP Connect error, %s:%v", ls.Host, err)
 | 
			
		||||
		ls.Enabled = false
 | 
			
		||||
		return nil
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer l.Close()
 | 
			
		||||
 | 
			
		||||
@@ -321,7 +321,7 @@ func (ls *Source) SearchEntries() []*SearchResult {
 | 
			
		||||
		err := l.Bind(ls.BindDN, ls.BindPassword)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err)
 | 
			
		||||
			return nil
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		log.Trace("Bound as BindDN %s", ls.BindDN)
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -350,7 +350,7 @@ func (ls *Source) SearchEntries() []*SearchResult {
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("LDAP Search failed unexpectedly! (%v)", err)
 | 
			
		||||
		return nil
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := make([]*SearchResult, len(sr.Entries))
 | 
			
		||||
@@ -368,5 +368,5 @@ func (ls *Source) SearchEntries() []*SearchResult {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -187,7 +187,16 @@ func ReferencesGitRepo(allowEmpty bool) macaron.Handler {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Repo.GitRepo = gitRepo
 | 
			
		||||
			// We opened it, we should close it
 | 
			
		||||
			defer func() {
 | 
			
		||||
				// If it's been set to nil then assume someone else has closed it.
 | 
			
		||||
				if ctx.Repo.GitRepo != nil {
 | 
			
		||||
					ctx.Repo.GitRepo.Close()
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx.Next()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -249,6 +249,19 @@ func Contexter() macaron.Handler {
 | 
			
		||||
		if ctx.Query("go-get") == "1" {
 | 
			
		||||
			ownerName := c.Params(":username")
 | 
			
		||||
			repoName := c.Params(":reponame")
 | 
			
		||||
			trimmedRepoName := strings.TrimSuffix(repoName, ".git")
 | 
			
		||||
 | 
			
		||||
			if ownerName == "" || trimmedRepoName == "" {
 | 
			
		||||
				_, _ = c.Write([]byte(`<!doctype html>
 | 
			
		||||
<html>
 | 
			
		||||
	<body>
 | 
			
		||||
		invalid import path
 | 
			
		||||
	</body>
 | 
			
		||||
</html>
 | 
			
		||||
`))
 | 
			
		||||
				c.WriteHeader(400)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			branchName := "master"
 | 
			
		||||
 | 
			
		||||
			repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
 | 
			
		||||
@@ -276,7 +289,7 @@ func Contexter() macaron.Handler {
 | 
			
		||||
	</body>
 | 
			
		||||
</html>
 | 
			
		||||
`, map[string]string{
 | 
			
		||||
				"GoGetImport":    ComposeGoGetImport(ownerName, strings.TrimSuffix(repoName, ".git")),
 | 
			
		||||
				"GoGetImport":    ComposeGoGetImport(ownerName, trimmedRepoName),
 | 
			
		||||
				"CloneLink":      models.ComposeHTTPSCloneURL(ownerName, repoName),
 | 
			
		||||
				"GoDocDirectory": prefix + "{/dir}",
 | 
			
		||||
				"GoDocFile":      prefix + "{/dir}/{file}#L{line}",
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 | 
			
		||||
 | 
			
		||||
	// Force redirection when username is actually a user.
 | 
			
		||||
	if !org.IsOrganization() {
 | 
			
		||||
		ctx.Redirect("/" + org.Name)
 | 
			
		||||
		ctx.Redirect(setting.AppSubURL + "/" + org.Name)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -201,10 +201,14 @@ func ComposeGoGetImport(owner, repo string) string {
 | 
			
		||||
// .netrc file.
 | 
			
		||||
func EarlyResponseForGoGetMeta(ctx *Context) {
 | 
			
		||||
	username := ctx.Params(":username")
 | 
			
		||||
	reponame := ctx.Params(":reponame")
 | 
			
		||||
	reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
 | 
			
		||||
	if username == "" || reponame == "" {
 | 
			
		||||
		ctx.PlainText(400, []byte("invalid repository path"))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.PlainText(200, []byte(com.Expand(`<meta name="go-import" content="{GoGetImport} git {CloneLink}">`,
 | 
			
		||||
		map[string]string{
 | 
			
		||||
			"GoGetImport": ComposeGoGetImport(username, strings.TrimSuffix(reponame, ".git")),
 | 
			
		||||
			"GoGetImport": ComposeGoGetImport(username, reponame),
 | 
			
		||||
			"CloneLink":   models.ComposeHTTPSCloneURL(username, reponame),
 | 
			
		||||
		})))
 | 
			
		||||
}
 | 
			
		||||
@@ -229,7 +233,7 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
 | 
			
		||||
	if ctx.Req.URL.RawQuery != "" {
 | 
			
		||||
		redirectPath += "?" + ctx.Req.URL.RawQuery
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Redirect(redirectPath)
 | 
			
		||||
	ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func repoAssignment(ctx *Context, repo *models.Repository) {
 | 
			
		||||
@@ -360,6 +364,13 @@ func RepoAssignment() macaron.Handler {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Repo.GitRepo = gitRepo
 | 
			
		||||
		// We opened it, we should close it
 | 
			
		||||
		defer func() {
 | 
			
		||||
			// If it's been set to nil then assume someone else has closed it.
 | 
			
		||||
			if ctx.Repo.GitRepo != nil {
 | 
			
		||||
				ctx.Repo.GitRepo.Close()
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
		ctx.Repo.RepoLink = repo.Link()
 | 
			
		||||
		ctx.Data["RepoLink"] = ctx.Repo.RepoLink
 | 
			
		||||
		ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
 | 
			
		||||
@@ -422,6 +433,7 @@ func RepoAssignment() macaron.Handler {
 | 
			
		||||
		// repo is empty and display enable
 | 
			
		||||
		if ctx.Repo.Repository.IsEmpty {
 | 
			
		||||
			ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
 | 
			
		||||
			ctx.Next()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -472,6 +484,7 @@ func RepoAssignment() macaron.Handler {
 | 
			
		||||
			ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
 | 
			
		||||
			ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Next()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -577,6 +590,13 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
 | 
			
		||||
				ctx.ServerError("RepoRef Invalid repo "+repoPath, err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			// We opened it, we should close it
 | 
			
		||||
			defer func() {
 | 
			
		||||
				// If it's been set to nil then assume someone else has closed it.
 | 
			
		||||
				if ctx.Repo.GitRepo != nil {
 | 
			
		||||
					ctx.Repo.GitRepo.Close()
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get default branch.
 | 
			
		||||
@@ -665,6 +685,8 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 | 
			
		||||
 | 
			
		||||
		ctx.Next()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -87,10 +87,11 @@ func (r *BlameReader) Close() error {
 | 
			
		||||
 | 
			
		||||
// CreateBlameReader creates reader for given repository, commit and file
 | 
			
		||||
func CreateBlameReader(repoPath, commitID, file string) (*BlameReader, error) {
 | 
			
		||||
	_, err := OpenRepository(repoPath)
 | 
			
		||||
	gitRepo, err := OpenRepository(repoPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	return createBlameReader(repoPath, GitExecutable, "blame", commitID, "--porcelain", "--", file)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,8 @@ THE SOFTWARE.
 | 
			
		||||
`
 | 
			
		||||
	repo, err := OpenRepository("../../.git")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer repo.Close()
 | 
			
		||||
 | 
			
		||||
	testBlob, err := repo.GetBlob("a8d4b49dd073a4a38a7e58385eeff7cc52568697")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
@@ -55,6 +57,8 @@ func Benchmark_Blob_Data(b *testing.B) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		b.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer repo.Close()
 | 
			
		||||
 | 
			
		||||
	testBlob, err := repo.GetBlob("a8d4b49dd073a4a38a7e58385eeff7cc52568697")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		b.Fatal(err)
 | 
			
		||||
 
 | 
			
		||||
@@ -169,6 +169,7 @@ func AddChanges(repoPath string, all bool, files ...string) error {
 | 
			
		||||
	if all {
 | 
			
		||||
		cmd.AddArguments("--all")
 | 
			
		||||
	}
 | 
			
		||||
	cmd.AddArguments("--")
 | 
			
		||||
	_, err := cmd.AddArguments(files...).RunInDir(repoPath)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
@@ -304,6 +305,7 @@ func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileChangedSinceCommit Returns true if the file given has changed since the the past commit
 | 
			
		||||
// YOU MUST ENSURE THAT pastCommit is a valid commit ID.
 | 
			
		||||
func (c *Commit) FileChangedSinceCommit(filename, pastCommit string) (bool, error) {
 | 
			
		||||
	return c.repo.FileChangedBetweenCommits(filename, pastCommit, c.ID.String())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,9 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache LastCom
 | 
			
		||||
	// get it for free during the tree traversal and it's used for listing
 | 
			
		||||
	// pages to display information about newest commit for a given path.
 | 
			
		||||
	var treeCommit *Commit
 | 
			
		||||
	if rev, ok := revs[""]; ok {
 | 
			
		||||
	if treePath == "" {
 | 
			
		||||
		treeCommit = commit
 | 
			
		||||
	} else if rev, ok := revs[""]; ok {
 | 
			
		||||
		treeCommit = convertCommit(rev)
 | 
			
		||||
	}
 | 
			
		||||
	return commitsInfo, treeCommit, nil
 | 
			
		||||
 
 | 
			
		||||
@@ -28,21 +28,27 @@ func cloneRepo(url, dir, name string) (string, error) {
 | 
			
		||||
func testGetCommitsInfo(t *testing.T, repo1 *Repository) {
 | 
			
		||||
	// these test case are specific to the repo1 test repo
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		CommitID    string
 | 
			
		||||
		Path        string
 | 
			
		||||
		ExpectedIDs map[string]string
 | 
			
		||||
		CommitID           string
 | 
			
		||||
		Path               string
 | 
			
		||||
		ExpectedIDs        map[string]string
 | 
			
		||||
		ExpectedTreeCommit string
 | 
			
		||||
	}{
 | 
			
		||||
		{"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", "", map[string]string{
 | 
			
		||||
			"file1.txt": "95bb4d39648ee7e325106df01a621c530863a653",
 | 
			
		||||
			"file2.txt": "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
 | 
			
		||||
		}},
 | 
			
		||||
		}, "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2"},
 | 
			
		||||
		{"2839944139e0de9737a044f78b0e4b40d989a9e3", "", map[string]string{
 | 
			
		||||
			"file1.txt":   "2839944139e0de9737a044f78b0e4b40d989a9e3",
 | 
			
		||||
			"branch1.txt": "9c9aef8dd84e02bc7ec12641deb4c930a7c30185",
 | 
			
		||||
		}},
 | 
			
		||||
		}, "2839944139e0de9737a044f78b0e4b40d989a9e3"},
 | 
			
		||||
		{"5c80b0245c1c6f8343fa418ec374b13b5d4ee658", "branch2", map[string]string{
 | 
			
		||||
			"branch2.txt": "5c80b0245c1c6f8343fa418ec374b13b5d4ee658",
 | 
			
		||||
		}},
 | 
			
		||||
		}, "5c80b0245c1c6f8343fa418ec374b13b5d4ee658"},
 | 
			
		||||
		{"feaf4ba6bc635fec442f46ddd4512416ec43c2c2", "", map[string]string{
 | 
			
		||||
			"file1.txt": "95bb4d39648ee7e325106df01a621c530863a653",
 | 
			
		||||
			"file2.txt": "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
 | 
			
		||||
			"foo":       "37991dec2c8e592043f47155ce4808d4580f9123",
 | 
			
		||||
		}, "feaf4ba6bc635fec442f46ddd4512416ec43c2c2"},
 | 
			
		||||
	}
 | 
			
		||||
	for _, testCase := range testCases {
 | 
			
		||||
		commit, err := repo1.GetCommit(testCase.CommitID)
 | 
			
		||||
@@ -51,7 +57,8 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) {
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		entries, err := tree.ListEntries()
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		commitsInfo, _, err := entries.GetCommitsInfo(commit, testCase.Path, nil)
 | 
			
		||||
		commitsInfo, treeCommit, err := entries.GetCommitsInfo(commit, testCase.Path, nil)
 | 
			
		||||
		assert.Equal(t, testCase.ExpectedTreeCommit, treeCommit.ID.String())
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Len(t, commitsInfo, len(testCase.ExpectedIDs))
 | 
			
		||||
		for _, commitInfo := range commitsInfo {
 | 
			
		||||
@@ -70,6 +77,8 @@ func TestEntries_GetCommitsInfo(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	testGetCommitsInfo(t, bareRepo1)
 | 
			
		||||
 | 
			
		||||
	clonedPath, err := cloneRepo(bareRepo1Path, testReposDir, "repo1_TestEntries_GetCommitsInfo")
 | 
			
		||||
@@ -77,6 +86,8 @@ func TestEntries_GetCommitsInfo(t *testing.T) {
 | 
			
		||||
	defer os.RemoveAll(clonedPath)
 | 
			
		||||
	clonedRepo1, err := OpenRepository(clonedPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer clonedRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	testGetCommitsInfo(t, clonedRepo1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -94,13 +105,16 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
 | 
			
		||||
	for _, benchmark := range benchmarks {
 | 
			
		||||
		var commit *Commit
 | 
			
		||||
		var entries Entries
 | 
			
		||||
		var repo *Repository
 | 
			
		||||
		if repoPath, err := cloneRepo(benchmark.url, benchmarkReposDir, benchmark.name); err != nil {
 | 
			
		||||
			b.Fatal(err)
 | 
			
		||||
		} else if repo, err := OpenRepository(repoPath); err != nil {
 | 
			
		||||
		} else if repo, err = OpenRepository(repoPath); err != nil {
 | 
			
		||||
			b.Fatal(err)
 | 
			
		||||
		} else if commit, err = repo.GetBranchCommit("master"); err != nil {
 | 
			
		||||
			repo.Close()
 | 
			
		||||
			b.Fatal(err)
 | 
			
		||||
		} else if entries, err = commit.Tree.ListEntries(); err != nil {
 | 
			
		||||
			repo.Close()
 | 
			
		||||
			b.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		entries.Sort()
 | 
			
		||||
@@ -113,5 +127,6 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		repo.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,11 @@ func (h *Hook) Update() error {
 | 
			
		||||
		h.IsActive = false
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	d := filepath.Dir(h.path)
 | 
			
		||||
	if err := os.MkdirAll(d, os.ModePerm); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := ioutil.WriteFile(h.path, []byte(strings.Replace(h.Content, "\r", "", -1)), os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ package git
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
			
		||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NotesRef is the git ref where Gitea will look for git-notes data.
 | 
			
		||||
@@ -27,13 +27,28 @@ func GetNote(repo *Repository, commitID string, note *Note) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entry, err := notes.GetTreeEntryByPath(commitID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	remainingCommitID := commitID
 | 
			
		||||
	path := ""
 | 
			
		||||
	currentTree := notes.Tree.gogitTree
 | 
			
		||||
	var file *object.File
 | 
			
		||||
	for len(remainingCommitID) > 2 {
 | 
			
		||||
		file, err = currentTree.File(remainingCommitID)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			path += remainingCommitID
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if err == object.ErrFileNotFound {
 | 
			
		||||
			currentTree, err = currentTree.Tree(remainingCommitID[0:2])
 | 
			
		||||
			path += remainingCommitID[0:2] + "/"
 | 
			
		||||
			remainingCommitID = remainingCommitID[2:]
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	blob := entry.Blob()
 | 
			
		||||
	dataRc, err := blob.DataAsync()
 | 
			
		||||
	blob := file.Blob
 | 
			
		||||
	dataRc, err := blob.Reader()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -45,26 +60,21 @@ func GetNote(repo *Repository, commitID string, note *Note) error {
 | 
			
		||||
	}
 | 
			
		||||
	note.Message = d
 | 
			
		||||
 | 
			
		||||
	commit, err := repo.gogitRepo.CommitObject(plumbing.Hash(notes.ID))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	commitNodeIndex, commitGraphFile := repo.CommitNodeIndex()
 | 
			
		||||
	if commitGraphFile != nil {
 | 
			
		||||
		defer commitGraphFile.Close()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	commitNode, err := commitNodeIndex.Get(commit.Hash)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lastCommits, err := getLastCommitForPaths(commitNode, "", []string{commitID})
 | 
			
		||||
	commitNode, err := commitNodeIndex.Get(notes.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	note.Commit = convertCommit(lastCommits[commitID])
 | 
			
		||||
 | 
			
		||||
	lastCommits, err := getLastCommitForPaths(commitNode, "", []string{path})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	note.Commit = convertCommit(lastCommits[path])
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ func TestGetNotes(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	note := Note{}
 | 
			
		||||
	err = GetNote(bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e)
 | 
			
		||||
@@ -22,3 +23,18 @@ func TestGetNotes(t *testing.T) {
 | 
			
		||||
	assert.Equal(t, []byte("Note contents\n"), note.Message)
 | 
			
		||||
	assert.Equal(t, "Vladimir Panteleev", note.Commit.Author.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetNestedNotes(t *testing.T) {
 | 
			
		||||
	repoPath := filepath.Join(testReposDir, "repo3_notes")
 | 
			
		||||
	repo, err := OpenRepository(repoPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer repo.Close()
 | 
			
		||||
 | 
			
		||||
	note := Note{}
 | 
			
		||||
	err = GetNote(repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", ¬e)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []byte("Note 2"), note.Message)
 | 
			
		||||
	err = GetNote(repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", ¬e)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []byte("Note 1"), note.Message)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	gitealog "code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"github.com/Unknwon/com"
 | 
			
		||||
	"gopkg.in/src-d/go-billy.v4/osfs"
 | 
			
		||||
	gogit "gopkg.in/src-d/go-git.v4"
 | 
			
		||||
@@ -107,6 +108,21 @@ func OpenRepository(repoPath string) (*Repository, error) {
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close this repository, in particular close the underlying gogitStorage if this is not nil
 | 
			
		||||
func (repo *Repository) Close() {
 | 
			
		||||
	if repo == nil || repo.gogitStorage == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err := repo.gogitStorage.Close(); err != nil {
 | 
			
		||||
		gitealog.Error("Error closing storage: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GoGitRepo gets the go-git repo representation
 | 
			
		||||
func (repo *Repository) GoGitRepo() *gogit.Repository {
 | 
			
		||||
	return repo.gogitRepo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty Check if repository is empty.
 | 
			
		||||
func (repo *Repository) IsEmpty() (bool, error) {
 | 
			
		||||
	var errbuf strings.Builder
 | 
			
		||||
@@ -187,8 +203,7 @@ func Pull(repoPath string, opts PullRemoteOptions) error {
 | 
			
		||||
	if opts.All {
 | 
			
		||||
		cmd.AddArguments("--all")
 | 
			
		||||
	} else {
 | 
			
		||||
		cmd.AddArguments(opts.Remote)
 | 
			
		||||
		cmd.AddArguments(opts.Branch)
 | 
			
		||||
		cmd.AddArguments("--", opts.Remote, opts.Branch)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Timeout <= 0 {
 | 
			
		||||
@@ -213,7 +228,7 @@ func Push(repoPath string, opts PushOptions) error {
 | 
			
		||||
	if opts.Force {
 | 
			
		||||
		cmd.AddArguments("-f")
 | 
			
		||||
	}
 | 
			
		||||
	cmd.AddArguments(opts.Remote, opts.Branch)
 | 
			
		||||
	cmd.AddArguments("--", opts.Remote, opts.Branch)
 | 
			
		||||
	_, err := cmd.RunInDirWithEnv(repoPath, opts.Env)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ func TestRepository_GetBlob_Found(t *testing.T) {
 | 
			
		||||
	repoPath := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	r, err := OpenRepository(repoPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer r.Close()
 | 
			
		||||
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		OID  string
 | 
			
		||||
@@ -44,6 +45,7 @@ func TestRepository_GetBlob_NotExist(t *testing.T) {
 | 
			
		||||
	repoPath := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	r, err := OpenRepository(repoPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer r.Close()
 | 
			
		||||
 | 
			
		||||
	testCase := "0000000000000000000000000000000000000000"
 | 
			
		||||
	testError := ErrNotExist{testCase, ""}
 | 
			
		||||
@@ -57,6 +59,7 @@ func TestRepository_GetBlob_NoId(t *testing.T) {
 | 
			
		||||
	repoPath := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	r, err := OpenRepository(repoPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer r.Close()
 | 
			
		||||
 | 
			
		||||
	testCase := ""
 | 
			
		||||
	testError := fmt.Errorf("Length must be 40: %s", testCase)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,14 @@ func IsBranchExist(repoPath, name string) bool {
 | 
			
		||||
 | 
			
		||||
// IsBranchExist returns true if given branch exists in current repository.
 | 
			
		||||
func (repo *Repository) IsBranchExist(name string) bool {
 | 
			
		||||
	_, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true)
 | 
			
		||||
	return err == nil
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	reference, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return reference.Type() != plumbing.InvalidReference
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Branch represents a Git branch.
 | 
			
		||||
@@ -102,6 +108,7 @@ func GetBranchesByPath(path string) ([]*Branch, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	brs, err := gitRepo.GetBranches()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -135,7 +142,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
 | 
			
		||||
		cmd.AddArguments("-d")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.AddArguments(name)
 | 
			
		||||
	cmd.AddArguments("--", name)
 | 
			
		||||
	_, err := cmd.RunInDir(repo.Path)
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
@@ -165,7 +172,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
 | 
			
		||||
 | 
			
		||||
// RemoveRemote removes a remote from repository.
 | 
			
		||||
func (repo *Repository) RemoveRemote(name string) error {
 | 
			
		||||
	_, err := NewCommand("remote", "remove", name).RunInDir(repo.Path)
 | 
			
		||||
	_, err := NewCommand("remote", "rm", name).RunInDir(repo.Path)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ func TestRepository_GetBranches(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	branches, err := bareRepo1.GetBranches()
 | 
			
		||||
 | 
			
		||||
@@ -29,6 +30,7 @@ func BenchmarkRepository_GetBranches(b *testing.B) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		b.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		_, err := bareRepo1.GetBranches()
 | 
			
		||||
 
 | 
			
		||||
@@ -117,20 +117,26 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
 | 
			
		||||
	return commit, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCommit returns commit object of by ID string.
 | 
			
		||||
func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
 | 
			
		||||
// ConvertToSHA1 returns a Hash object from a potential ID string
 | 
			
		||||
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
 | 
			
		||||
	if len(commitID) != 40 {
 | 
			
		||||
		var err error
 | 
			
		||||
		actualCommitID, err := NewCommand("rev-parse", commitID).RunInDir(repo.Path)
 | 
			
		||||
		actualCommitID, err := NewCommand("rev-parse", "--verify", commitID).RunInDir(repo.Path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if strings.Contains(err.Error(), "unknown revision or path") {
 | 
			
		||||
				return nil, ErrNotExist{commitID, ""}
 | 
			
		||||
			if strings.Contains(err.Error(), "unknown revision or path") ||
 | 
			
		||||
				strings.Contains(err.Error(), "fatal: Needed a single revision") {
 | 
			
		||||
				return SHA1{}, ErrNotExist{commitID, ""}
 | 
			
		||||
			}
 | 
			
		||||
			return nil, err
 | 
			
		||||
			return SHA1{}, err
 | 
			
		||||
		}
 | 
			
		||||
		commitID = actualCommitID
 | 
			
		||||
	}
 | 
			
		||||
	id, err := NewIDFromString(commitID)
 | 
			
		||||
	return NewIDFromString(commitID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCommit returns commit object of by ID string.
 | 
			
		||||
func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
 | 
			
		||||
	id, err := repo.ConvertToSHA1(commitID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -243,6 +249,7 @@ func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2
 | 
			
		||||
// You must ensure that id1 and id2 are valid commit ids.
 | 
			
		||||
func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) {
 | 
			
		||||
	stdout, err := NewCommand("diff", "--name-only", "-z", id1, id2, "--", filename).RunInDirBytes(repo.Path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ func TestRepository_GetCommitBranches(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	// these test case are specific to the repo1_bare test repo
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
@@ -40,6 +41,9 @@ func TestRepository_GetCommitBranches(t *testing.T) {
 | 
			
		||||
func TestGetTagCommitWithSignature(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	commit, err := bareRepo1.GetCommit("3ad28a9149a2864384548f3d17ed7f38014c9e8a")
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
@@ -52,8 +56,11 @@ func TestGetTagCommitWithSignature(t *testing.T) {
 | 
			
		||||
func TestGetCommitWithBadCommitID(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	commit, err := bareRepo1.GetCommit("bad_branch")
 | 
			
		||||
	assert.Nil(t, commit)
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
	assert.EqualError(t, err, "object does not exist [id: bad_branch, rel_path: ]")
 | 
			
		||||
	assert.True(t, IsErrNotExist(err))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (strin
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stdout, err := NewCommand("merge-base", base, head).RunInDir(repo.Path)
 | 
			
		||||
	stdout, err := NewCommand("merge-base", "--", base, head).RunInDir(repo.Path)
 | 
			
		||||
	return strings.TrimSpace(stdout), base, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -54,7 +54,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string)
 | 
			
		||||
	if repo.Path != basePath {
 | 
			
		||||
		// Add a temporary remote
 | 
			
		||||
		tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10)
 | 
			
		||||
		if err = repo.AddRemote(tmpRemote, basePath, true); err != nil {
 | 
			
		||||
		if err = repo.AddRemote(tmpRemote, basePath, false); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("AddRemote: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ func TestGetFormatPatch(t *testing.T) {
 | 
			
		||||
	defer os.RemoveAll(clonedPath)
 | 
			
		||||
	repo, err := OpenRepository(clonedPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer repo.Close()
 | 
			
		||||
	rd, err := repo.GetFormatPatch("8d92fc95^", "8d92fc95")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	patchb, err := ioutil.ReadAll(rd)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
// ReadTreeToIndex reads a treeish to the index
 | 
			
		||||
func (repo *Repository) ReadTreeToIndex(treeish string) error {
 | 
			
		||||
	if len(treeish) != 40 {
 | 
			
		||||
		res, err := NewCommand("rev-parse", treeish).RunInDir(repo.Path)
 | 
			
		||||
		res, err := NewCommand("rev-parse", "--verify", treeish).RunInDir(repo.Path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ func TestRepository_GetRefs(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	refs, err := bareRepo1.GetRefs()
 | 
			
		||||
 | 
			
		||||
@@ -38,6 +39,7 @@ func TestRepository_GetRefsFiltered(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	refs, err := bareRepo1.GetRefsFiltered(TagPrefix)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ func TestRepository_GetCodeActivityStats(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	timeFrom, err := time.Parse(time.RFC3339, "2016-01-01T00:00:00+00:00")
 | 
			
		||||
 | 
			
		||||
@@ -23,12 +24,12 @@ func TestRepository_GetCodeActivityStats(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.NotNil(t, code)
 | 
			
		||||
 | 
			
		||||
	assert.EqualValues(t, 8, code.CommitCount)
 | 
			
		||||
	assert.EqualValues(t, 2, code.AuthorCount)
 | 
			
		||||
	assert.EqualValues(t, 8, code.CommitCountInAllBranches)
 | 
			
		||||
	assert.EqualValues(t, 9, code.CommitCount)
 | 
			
		||||
	assert.EqualValues(t, 3, code.AuthorCount)
 | 
			
		||||
	assert.EqualValues(t, 9, code.CommitCountInAllBranches)
 | 
			
		||||
	assert.EqualValues(t, 10, code.Additions)
 | 
			
		||||
	assert.EqualValues(t, 1, code.Deletions)
 | 
			
		||||
	assert.Len(t, code.Authors, 2)
 | 
			
		||||
	assert.Len(t, code.Authors, 3)
 | 
			
		||||
	assert.Contains(t, code.Authors, "tris.git@shoddynet.org")
 | 
			
		||||
	assert.EqualValues(t, 3, code.Authors["tris.git@shoddynet.org"])
 | 
			
		||||
	assert.EqualValues(t, 5, code.Authors[""])
 | 
			
		||||
 
 | 
			
		||||
@@ -29,13 +29,13 @@ func (repo *Repository) IsTagExist(name string) bool {
 | 
			
		||||
 | 
			
		||||
// CreateTag create one tag in the repository
 | 
			
		||||
func (repo *Repository) CreateTag(name, revision string) error {
 | 
			
		||||
	_, err := NewCommand("tag", name, revision).RunInDir(repo.Path)
 | 
			
		||||
	_, err := NewCommand("tag", "--", name, revision).RunInDir(repo.Path)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateAnnotatedTag create one annotated tag in the repository
 | 
			
		||||
func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error {
 | 
			
		||||
	_, err := NewCommand("tag", "-a", "-m", message, name, revision).RunInDir(repo.Path)
 | 
			
		||||
	_, err := NewCommand("tag", "-a", "-m", message, "--", name, revision).RunInDir(repo.Path)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -153,15 +153,18 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
 | 
			
		||||
 | 
			
		||||
// GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA)
 | 
			
		||||
func (repo *Repository) GetTagID(name string) (string, error) {
 | 
			
		||||
	stdout, err := NewCommand("show-ref", name).RunInDir(repo.Path)
 | 
			
		||||
	stdout, err := NewCommand("show-ref", "--tags", "--", name).RunInDir(repo.Path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	fields := strings.Fields(stdout)
 | 
			
		||||
	if len(fields) != 2 {
 | 
			
		||||
		return "", ErrNotExist{ID: name}
 | 
			
		||||
	// Make sure exact match is used: "v1" != "release/v1"
 | 
			
		||||
	for _, line := range strings.Split(stdout, "\n") {
 | 
			
		||||
		fields := strings.Fields(line)
 | 
			
		||||
		if len(fields) == 2 && fields[1] == "refs/tags/"+name {
 | 
			
		||||
			return fields[0], nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fields[0], nil
 | 
			
		||||
	return "", ErrNotExist{ID: name}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTag returns a Git tag by given name.
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ func TestRepository_GetTags(t *testing.T) {
 | 
			
		||||
	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
			
		||||
	bareRepo1, err := OpenRepository(bareRepo1Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	tags, err := bareRepo1.GetTagInfos()
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
@@ -34,6 +35,7 @@ func TestRepository_GetTag(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	bareRepo1, err := OpenRepository(clonedPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	lTagCommitID := "6fbd69e9823458e6c4a2fc5c0f6bc022b2f2acd1"
 | 
			
		||||
	lTagName := "lightweightTag"
 | 
			
		||||
@@ -62,6 +64,16 @@ func TestRepository_GetTag(t *testing.T) {
 | 
			
		||||
	assert.NotEqual(t, aTagID, aTag.Object.String())
 | 
			
		||||
	assert.EqualValues(t, aTagCommitID, aTag.Object.String())
 | 
			
		||||
	assert.EqualValues(t, "tag", aTag.Type)
 | 
			
		||||
 | 
			
		||||
	rTagCommitID := "8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"
 | 
			
		||||
	rTagName := "release/" + lTagName
 | 
			
		||||
	bareRepo1.CreateTag(rTagName, rTagCommitID)
 | 
			
		||||
	rTagID, err := bareRepo1.GetTagID(rTagName)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.EqualValues(t, rTagCommitID, rTagID)
 | 
			
		||||
	oTagID, err := bareRepo1.GetTagID(lTagName)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.EqualValues(t, lTagCommitID, oTagID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRepository_GetAnnotatedTag(t *testing.T) {
 | 
			
		||||
@@ -73,6 +85,7 @@ func TestRepository_GetAnnotatedTag(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	bareRepo1, err := OpenRepository(clonedPath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer bareRepo1.Close()
 | 
			
		||||
 | 
			
		||||
	lTagCommitID := "6fbd69e9823458e6c4a2fc5c0f6bc022b2f2acd1"
 | 
			
		||||
	lTagName := "lightweightTag"
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ func TestRepoIsEmpty(t *testing.T) {
 | 
			
		||||
	emptyRepo2Path := filepath.Join(testReposDir, "repo2_empty")
 | 
			
		||||
	repo, err := OpenRepository(emptyRepo2Path)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer repo.Close()
 | 
			
		||||
	isEmpty, err := repo.IsEmpty()
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.True(t, isEmpty)
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user