2019-06-07 16:29:29 -04:00
// 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 repo
import (
2019-11-14 21:52:59 -05:00
"bufio"
2022-01-19 18:26:57 -05:00
gocontext "context"
2021-03-29 16:44:28 -04:00
"encoding/csv"
"errors"
2019-10-04 15:58:54 -04:00
"fmt"
2019-11-14 21:52:59 -05:00
"html"
2021-10-24 18:42:32 -04:00
"io"
2021-04-05 11:30:52 -04:00
"net/http"
2021-11-16 13:18:25 -05:00
"net/url"
2021-03-29 16:44:28 -04:00
"path/filepath"
2019-06-07 16:29:29 -04:00
"strings"
2022-06-12 11:51:54 -04:00
git_model "code.gitea.io/gitea/models/git"
2022-06-13 05:37:59 -04:00
issues_model "code.gitea.io/gitea/models/issues"
2022-05-11 06:09:36 -04:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-09 20:27:50 -05:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 14:57:58 -05:00
"code.gitea.io/gitea/models/unit"
2021-11-24 04:49:20 -05:00
user_model "code.gitea.io/gitea/models/user"
2019-06-07 16:29:29 -04:00
"code.gitea.io/gitea/modules/base"
2021-03-29 16:44:28 -04:00
"code.gitea.io/gitea/modules/charset"
2019-06-07 16:29:29 -04:00
"code.gitea.io/gitea/modules/context"
2021-03-29 16:44:28 -04:00
csv_module "code.gitea.io/gitea/modules/csv"
2019-06-07 16:29:29 -04:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
2021-10-30 11:50:40 -04:00
"code.gitea.io/gitea/modules/markup"
2019-06-07 16:29:29 -04:00
"code.gitea.io/gitea/modules/setting"
2020-10-05 01:49:33 -04:00
"code.gitea.io/gitea/modules/upload"
2021-07-24 22:59:27 -04:00
"code.gitea.io/gitea/modules/util"
2019-09-05 22:20:09 -04:00
"code.gitea.io/gitea/services/gitdiff"
2019-06-07 16:29:29 -04:00
)
const (
2019-11-14 21:52:59 -05:00
tplCompare base . TplName = "repo/diff/compare"
tplBlobExcerpt base . TplName = "repo/diff/blob_excerpt"
2021-10-15 12:05:33 -04:00
tplDiffBox base . TplName = "repo/diff/box"
2019-06-07 16:29:29 -04:00
)
2021-03-29 16:44:28 -04:00
// setCompareContext sets context data.
2021-12-19 23:41:31 -05:00
func setCompareContext ( ctx * context . Context , base , head * git . Commit , headOwner , headName string ) {
2021-03-29 16:44:28 -04:00
ctx . Data [ "BaseCommit" ] = base
ctx . Data [ "HeadCommit" ] = head
2021-06-05 08:32:19 -04:00
ctx . Data [ "GetBlobByPathForCommit" ] = func ( commit * git . Commit , path string ) * git . Blob {
if commit == nil {
return nil
}
blob , err := commit . GetBlobByPath ( path )
if err != nil {
return nil
}
return blob
}
2021-11-16 13:18:25 -05:00
setPathsCompareContext ( ctx , base , head , headOwner , headName )
2021-06-05 08:32:19 -04:00
setImageCompareContext ( ctx )
2021-03-29 16:44:28 -04:00
setCsvCompareContext ( ctx )
}
2021-11-16 13:18:25 -05:00
// SourceCommitURL creates a relative URL for a commit in the given repository
func SourceCommitURL ( owner , name string , commit * git . Commit ) string {
return setting . AppSubURL + "/" + url . PathEscape ( owner ) + "/" + url . PathEscape ( name ) + "/src/commit/" + url . PathEscape ( commit . ID . String ( ) )
}
2019-10-04 15:58:54 -04:00
2021-11-16 13:18:25 -05:00
// RawCommitURL creates a relative URL for the raw commit in the given repository
func RawCommitURL ( owner , name string , commit * git . Commit ) string {
return setting . AppSubURL + "/" + url . PathEscape ( owner ) + "/" + url . PathEscape ( name ) + "/raw/commit/" + url . PathEscape ( commit . ID . String ( ) )
}
// setPathsCompareContext sets context data for source and raw paths
2021-12-19 23:41:31 -05:00
func setPathsCompareContext ( ctx * context . Context , base , head * git . Commit , headOwner , headName string ) {
2021-11-16 13:18:25 -05:00
ctx . Data [ "SourcePath" ] = SourceCommitURL ( headOwner , headName , head )
ctx . Data [ "RawPath" ] = RawCommitURL ( headOwner , headName , head )
2019-10-04 15:58:54 -04:00
if base != nil {
2022-02-25 01:46:15 -05:00
ctx . Data [ "BeforeSourcePath" ] = SourceCommitURL ( headOwner , headName , base )
ctx . Data [ "BeforeRawPath" ] = RawCommitURL ( headOwner , headName , base )
2019-10-04 15:58:54 -04:00
}
}
// setImageCompareContext sets context data that is required by image compare template
2021-06-05 08:32:19 -04:00
func setImageCompareContext ( ctx * context . Context ) {
ctx . Data [ "IsBlobAnImage" ] = func ( blob * git . Blob ) bool {
if blob == nil {
return false
2019-10-04 15:58:54 -04:00
}
2021-06-05 08:32:19 -04:00
st , err := blob . GuessContentType ( )
2019-10-04 15:58:54 -04:00
if err != nil {
2021-06-05 08:32:19 -04:00
log . Error ( "GuessContentType failed: %v" , err )
return false
2019-10-04 15:58:54 -04:00
}
2021-06-05 08:32:19 -04:00
return st . IsImage ( ) && ( setting . UI . SVG . Enabled || ! st . IsSvgImage ( ) )
2019-10-04 15:58:54 -04:00
}
}
2021-03-29 16:44:28 -04:00
// setCsvCompareContext sets context data that is required by the CSV compare template
func setCsvCompareContext ( ctx * context . Context ) {
ctx . Data [ "IsCsvFile" ] = func ( diffFile * gitdiff . DiffFile ) bool {
extension := strings . ToLower ( filepath . Ext ( diffFile . Name ) )
return extension == ".csv" || extension == ".tsv"
}
type CsvDiffResult struct {
Sections [ ] * gitdiff . TableDiffSection
Error string
}
2022-01-20 12:46:10 -05:00
ctx . Data [ "CreateCsvDiff" ] = func ( diffFile * gitdiff . DiffFile , baseCommit , headCommit * git . Commit ) CsvDiffResult {
2021-03-29 16:44:28 -04:00
if diffFile == nil || baseCommit == nil || headCommit == nil {
return CsvDiffResult { nil , "" }
}
errTooLarge := errors . New ( ctx . Locale . Tr ( "repo.error.csv.too_large" ) )
2021-10-30 11:50:40 -04:00
csvReaderFromCommit := func ( ctx * markup . RenderContext , c * git . Commit ) ( * csv . Reader , io . Closer , error ) {
2021-03-29 16:44:28 -04:00
blob , err := c . GetBlobByPath ( diffFile . Name )
if err != nil {
2021-10-24 18:42:32 -04:00
return nil , nil , err
2021-03-29 16:44:28 -04:00
}
if setting . UI . CSV . MaxFileSize != 0 && setting . UI . CSV . MaxFileSize < blob . Size ( ) {
2021-10-24 18:42:32 -04:00
return nil , nil , errTooLarge
2021-03-29 16:44:28 -04:00
}
reader , err := blob . DataAsync ( )
if err != nil {
2021-10-24 18:42:32 -04:00
return nil , nil , err
2021-03-29 16:44:28 -04:00
}
2021-10-30 11:50:40 -04:00
csvReader , err := csv_module . CreateReaderAndDetermineDelimiter ( ctx , charset . ToUTF8WithFallbackReader ( reader ) )
2021-10-24 18:42:32 -04:00
return csvReader , reader , err
2021-03-29 16:44:28 -04:00
}
2022-06-15 23:33:23 -04:00
baseReader , baseBlobCloser , err := csvReaderFromCommit ( & markup . RenderContext { Ctx : ctx , RelativePath : diffFile . OldName } , baseCommit )
2021-10-24 18:42:32 -04:00
if baseBlobCloser != nil {
defer baseBlobCloser . Close ( )
}
2021-03-29 16:44:28 -04:00
if err == errTooLarge {
return CsvDiffResult { nil , err . Error ( ) }
}
2022-05-02 12:46:50 -04:00
if err != nil {
log . Error ( "CreateCsvDiff error whilst creating baseReader from file %s in commit %s in %s: %v" , diffFile . Name , baseCommit . ID . String ( ) , ctx . Repo . Repository . Name , err )
return CsvDiffResult { nil , "unable to load file from base commit" }
}
2022-06-15 23:33:23 -04:00
headReader , headBlobCloser , err := csvReaderFromCommit ( & markup . RenderContext { Ctx : ctx , RelativePath : diffFile . Name } , headCommit )
2021-10-24 18:42:32 -04:00
if headBlobCloser != nil {
defer headBlobCloser . Close ( )
}
2021-03-29 16:44:28 -04:00
if err == errTooLarge {
return CsvDiffResult { nil , err . Error ( ) }
}
2022-05-02 12:46:50 -04:00
if err != nil {
log . Error ( "CreateCsvDiff error whilst creating headReader from file %s in commit %s in %s: %v" , diffFile . Name , headCommit . ID . String ( ) , ctx . Repo . Repository . Name , err )
return CsvDiffResult { nil , "unable to load file from head commit" }
}
2021-03-29 16:44:28 -04:00
sections , err := gitdiff . CreateCsvDiff ( diffFile , baseReader , headReader )
if err != nil {
errMessage , err := csv_module . FormatError ( err , ctx . Locale )
if err != nil {
2022-05-02 12:46:50 -04:00
log . Error ( "CreateCsvDiff FormatError failed: %v" , err )
return CsvDiffResult { nil , "unknown csv diff error" }
2021-03-29 16:44:28 -04:00
}
return CsvDiffResult { nil , errMessage }
}
return CsvDiffResult { sections , "" }
}
}
2021-09-27 08:19:34 -04:00
// CompareInfo represents the collected results from ParseCompareInfo
type CompareInfo struct {
2021-11-24 04:49:20 -05:00
HeadUser * user_model . User
2021-12-09 20:27:50 -05:00
HeadRepo * repo_model . Repository
2021-09-27 08:19:34 -04:00
HeadGitRepo * git . Repository
CompareInfo * git . CompareInfo
BaseBranch string
HeadBranch string
DirectComparison bool
}
2019-06-07 16:29:29 -04:00
// ParseCompareInfo parse compare info between two commit for preparing comparing references
2021-09-27 08:19:34 -04:00
func ParseCompareInfo ( ctx * context . Context ) * CompareInfo {
2019-06-07 16:29:29 -04:00
baseRepo := ctx . Repo . Repository
2021-09-27 08:19:34 -04:00
ci := & CompareInfo { }
2019-06-07 16:29:29 -04:00
2021-10-15 12:05:33 -04:00
fileOnly := ctx . FormBool ( "file-only" )
2019-06-07 16:29:29 -04:00
// Get compared branches information
2020-05-12 01:52:46 -04:00
// A full compare url is of the form:
//
// 1. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headBranch}
// 2. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}:{:headBranch}
// 3. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}/{:headRepoName}:{:headBranch}
2021-12-06 12:04:07 -05:00
// 4. /{:baseOwner}/{:baseRepoName}/compare/{:headBranch}
// 5. /{:baseOwner}/{:baseRepoName}/compare/{:headOwner}:{:headBranch}
// 6. /{:baseOwner}/{:baseRepoName}/compare/{:headOwner}/{:headRepoName}:{:headBranch}
2020-05-12 01:52:46 -04:00
//
// Here we obtain the infoPath "{:baseBranch}...[{:headOwner}/{:headRepoName}:]{:headBranch}" as ctx.Params("*")
// with the :baseRepo in ctx.Repo.
//
// Note: Generally :headRepoName is not provided here - we are only passed :headOwner.
//
// How do we determine the :headRepo?
//
// 1. If :headOwner is not set then the :headRepo = :baseRepo
// 2. If :headOwner is set - then look for the fork of :baseRepo owned by :headOwner
// 3. But... :baseRepo could be a fork of :headOwner's repo - so check that
// 4. Now, :baseRepo and :headRepos could be forks of the same repo - so check that
//
2019-06-07 16:29:29 -04:00
// format: <base branch>...[<head repo>:]<head branch>
// base<-head: master...head:feature
// same repo: master...feature
var (
isSameRepo bool
infoPath string
err error
)
2021-09-27 08:19:34 -04:00
2019-06-07 16:29:29 -04:00
infoPath = ctx . Params ( "*" )
2021-12-17 17:20:27 -05:00
var infos [ ] string
if infoPath == "" {
infos = [ ] string { baseRepo . DefaultBranch , baseRepo . DefaultBranch }
} else {
infos = strings . SplitN ( infoPath , "..." , 2 )
if len ( infos ) != 2 {
if infos = strings . SplitN ( infoPath , ".." , 2 ) ; len ( infos ) == 2 {
ci . DirectComparison = true
ctx . Data [ "PageIsComparePull" ] = false
} else {
infos = [ ] string { baseRepo . DefaultBranch , infoPath }
}
2021-12-06 12:04:07 -05:00
}
2021-09-27 08:19:34 -04:00
}
2020-05-12 01:52:46 -04:00
ctx . Data [ "BaseName" ] = baseRepo . OwnerName
2021-09-27 08:19:34 -04:00
ci . BaseBranch = infos [ 0 ]
ctx . Data [ "BaseBranch" ] = ci . BaseBranch
2019-06-07 16:29:29 -04:00
// If there is no head repository, it means compare between same repository.
headInfos := strings . Split ( infos [ 1 ] , ":" )
if len ( headInfos ) == 1 {
isSameRepo = true
2021-09-27 08:19:34 -04:00
ci . HeadUser = ctx . Repo . Owner
ci . HeadBranch = headInfos [ 0 ]
2019-06-07 16:29:29 -04:00
} else if len ( headInfos ) == 2 {
2020-05-12 01:52:46 -04:00
headInfosSplit := strings . Split ( headInfos [ 0 ] , "/" )
if len ( headInfosSplit ) == 1 {
2022-05-20 10:08:52 -04:00
ci . HeadUser , err = user_model . GetUserByName ( ctx , headInfos [ 0 ] )
2020-05-12 01:52:46 -04:00
if err != nil {
2021-11-24 04:49:20 -05:00
if user_model . IsErrUserNotExist ( err ) {
2020-05-12 01:52:46 -04:00
ctx . NotFound ( "GetUserByName" , nil )
} else {
ctx . ServerError ( "GetUserByName" , err )
}
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2021-09-27 08:19:34 -04:00
ci . HeadBranch = headInfos [ 1 ]
isSameRepo = ci . HeadUser . ID == ctx . Repo . Owner . ID
2020-05-12 01:52:46 -04:00
if isSameRepo {
2021-09-27 08:19:34 -04:00
ci . HeadRepo = baseRepo
2020-05-12 01:52:46 -04:00
}
} else {
2021-12-09 20:27:50 -05:00
ci . HeadRepo , err = repo_model . GetRepositoryByOwnerAndName ( headInfosSplit [ 0 ] , headInfosSplit [ 1 ] )
2020-05-12 01:52:46 -04:00
if err != nil {
2021-12-09 20:27:50 -05:00
if repo_model . IsErrRepoNotExist ( err ) {
2020-05-12 01:52:46 -04:00
ctx . NotFound ( "GetRepositoryByOwnerAndName" , nil )
} else {
ctx . ServerError ( "GetRepositoryByOwnerAndName" , err )
}
2021-09-27 08:19:34 -04:00
return nil
2020-05-12 01:52:46 -04:00
}
2022-03-22 11:22:54 -04:00
if err := ci . HeadRepo . GetOwner ( ctx ) ; err != nil {
2021-11-24 04:49:20 -05:00
if user_model . IsErrUserNotExist ( err ) {
2020-05-12 01:52:46 -04:00
ctx . NotFound ( "GetUserByName" , nil )
} else {
ctx . ServerError ( "GetUserByName" , err )
}
2021-09-27 08:19:34 -04:00
return nil
2020-05-12 01:52:46 -04:00
}
2021-09-27 08:19:34 -04:00
ci . HeadBranch = headInfos [ 1 ]
ci . HeadUser = ci . HeadRepo . Owner
isSameRepo = ci . HeadRepo . ID == ctx . Repo . Repository . ID
2019-06-07 16:29:29 -04:00
}
} else {
ctx . NotFound ( "CompareAndPullRequest" , nil )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2021-09-27 08:19:34 -04:00
ctx . Data [ "HeadUser" ] = ci . HeadUser
ctx . Data [ "HeadBranch" ] = ci . HeadBranch
2019-06-07 16:29:29 -04:00
ctx . Repo . PullRequest . SameRepo = isSameRepo
// Check if base branch is valid.
2021-09-27 08:19:34 -04:00
baseIsCommit := ctx . Repo . GitRepo . IsCommitExist ( ci . BaseBranch )
baseIsBranch := ctx . Repo . GitRepo . IsBranchExist ( ci . BaseBranch )
baseIsTag := ctx . Repo . GitRepo . IsTagExist ( ci . BaseBranch )
2019-06-07 16:29:29 -04:00
if ! baseIsCommit && ! baseIsBranch && ! baseIsTag {
// Check if baseBranch is short sha commit hash
2021-09-27 08:19:34 -04:00
if baseCommit , _ := ctx . Repo . GitRepo . GetCommit ( ci . BaseBranch ) ; baseCommit != nil {
ci . BaseBranch = baseCommit . ID . String ( )
ctx . Data [ "BaseBranch" ] = ci . BaseBranch
2019-06-07 16:29:29 -04:00
baseIsCommit = true
2022-03-23 09:40:12 -04:00
} else if ci . BaseBranch == git . EmptySHA {
if isSameRepo {
ctx . Redirect ( ctx . Repo . RepoLink + "/compare/" + util . PathEscapeSegments ( ci . HeadBranch ) )
} else {
ctx . Redirect ( ctx . Repo . RepoLink + "/compare/" + util . PathEscapeSegments ( ci . HeadRepo . FullName ( ) ) + ":" + util . PathEscapeSegments ( ci . HeadBranch ) )
}
return nil
2019-06-07 16:29:29 -04:00
} else {
ctx . NotFound ( "IsRefExist" , nil )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
}
ctx . Data [ "BaseIsCommit" ] = baseIsCommit
ctx . Data [ "BaseIsBranch" ] = baseIsBranch
ctx . Data [ "BaseIsTag" ] = baseIsTag
2020-11-17 00:02:41 -05:00
ctx . Data [ "IsPull" ] = true
2019-06-07 16:29:29 -04:00
2020-05-12 01:52:46 -04:00
// Now we have the repository that represents the base
// The current base and head repositories and branches may not
// actually be the intended branches that the user wants to
// create a pull-request from - but also determining the head
// repo is difficult.
// We will want therefore to offer a few repositories to set as
// our base and head
// 1. First if the baseRepo is a fork get the "RootRepo" it was
// forked from
2021-12-09 20:27:50 -05:00
var rootRepo * repo_model . Repository
2020-05-12 01:52:46 -04:00
if baseRepo . IsFork {
err = baseRepo . GetBaseRepo ( )
if err != nil {
2021-12-09 20:27:50 -05:00
if ! repo_model . IsErrRepoNotExist ( err ) {
2020-05-12 01:52:46 -04:00
ctx . ServerError ( "Unable to find root repo" , err )
2021-09-27 08:19:34 -04:00
return nil
2020-05-12 01:52:46 -04:00
}
} else {
rootRepo = baseRepo . BaseRepo
}
}
// 2. Now if the current user is not the owner of the baseRepo,
// check if they have a fork of the base repo and offer that as
// "OwnForkRepo"
2021-12-09 20:27:50 -05:00
var ownForkRepo * repo_model . Repository
2022-03-22 03:03:22 -04:00
if ctx . Doer != nil && baseRepo . OwnerID != ctx . Doer . ID {
repo := repo_model . GetForkedRepo ( ctx . Doer . ID , baseRepo . ID )
2021-11-22 10:21:55 -05:00
if repo != nil {
2020-05-12 01:52:46 -04:00
ownForkRepo = repo
ctx . Data [ "OwnForkRepo" ] = ownForkRepo
}
}
2021-09-27 08:19:34 -04:00
has := ci . HeadRepo != nil
2020-05-12 01:52:46 -04:00
// 3. If the base is a forked from "RootRepo" and the owner of
// the "RootRepo" is the :headUser - set headRepo to that
2021-09-27 08:19:34 -04:00
if ! has && rootRepo != nil && rootRepo . OwnerID == ci . HeadUser . ID {
ci . HeadRepo = rootRepo
2020-05-12 01:52:46 -04:00
has = true
}
2022-03-22 03:03:22 -04:00
// 4. If the ctx.Doer has their own fork of the baseRepo and the headUser is the ctx.Doer
2020-05-12 01:52:46 -04:00
// set the headRepo to the ownFork
2021-09-27 08:19:34 -04:00
if ! has && ownForkRepo != nil && ownForkRepo . OwnerID == ci . HeadUser . ID {
ci . HeadRepo = ownForkRepo
2020-05-12 01:52:46 -04:00
has = true
}
// 5. If the headOwner has a fork of the baseRepo - use that
if ! has {
2021-12-12 10:48:20 -05:00
ci . HeadRepo = repo_model . GetForkedRepo ( ci . HeadUser . ID , baseRepo . ID )
2021-11-22 10:21:55 -05:00
has = ci . HeadRepo != nil
2020-05-12 01:52:46 -04:00
}
// 6. If the baseRepo is a fork and the headUser has a fork of that use that
if ! has && baseRepo . IsFork {
2021-12-12 10:48:20 -05:00
ci . HeadRepo = repo_model . GetForkedRepo ( ci . HeadUser . ID , baseRepo . ForkID )
2021-11-22 10:21:55 -05:00
has = ci . HeadRepo != nil
2020-05-12 01:52:46 -04:00
}
// 7. Otherwise if we're not the same repo and haven't found a repo give up
if ! isSameRepo && ! has {
2019-06-07 16:29:29 -04:00
ctx . Data [ "PageIsComparePull" ] = false
}
2020-05-12 01:52:46 -04:00
// 8. Finally open the git repo
2019-06-07 16:29:29 -04:00
if isSameRepo {
2021-09-27 08:19:34 -04:00
ci . HeadRepo = ctx . Repo . Repository
ci . HeadGitRepo = ctx . Repo . GitRepo
2020-05-12 01:52:46 -04:00
} else if has {
2022-03-29 15:13:41 -04:00
ci . HeadGitRepo , err = git . OpenRepository ( ctx , ci . HeadRepo . RepoPath ( ) )
2019-06-07 16:29:29 -04:00
if err != nil {
ctx . ServerError ( "OpenRepository" , err )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2021-09-27 08:19:34 -04:00
defer ci . HeadGitRepo . Close ( )
2019-06-07 16:29:29 -04:00
}
2021-09-27 08:19:34 -04:00
ctx . Data [ "HeadRepo" ] = ci . HeadRepo
2022-04-28 11:45:33 -04:00
ctx . Data [ "BaseCompareRepo" ] = ctx . Repo . Repository
2020-05-12 01:52:46 -04:00
2022-03-22 03:03:22 -04:00
// Now we need to assert that the ctx.Doer has permission to read
2020-05-12 01:52:46 -04:00
// the baseRepo's code and pulls
// (NOT headRepo's)
2022-05-11 06:09:36 -04:00
permBase , err := access_model . GetUserRepoPermission ( ctx , baseRepo , ctx . Doer )
2019-06-07 16:29:29 -04:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2021-11-09 14:57:58 -05:00
if ! permBase . CanRead ( unit . TypeCode ) {
2019-06-07 16:29:29 -04:00
if log . IsTrace ( ) {
log . Trace ( "Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in baseRepo has Permissions: %-+v" ,
2022-03-22 03:03:22 -04:00
ctx . Doer ,
2019-06-07 16:29:29 -04:00
baseRepo ,
permBase )
}
ctx . NotFound ( "ParseCompareInfo" , nil )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2020-05-12 01:52:46 -04:00
// If we're not merging from the same repo:
2020-01-16 14:59:07 -05:00
if ! isSameRepo {
2022-03-22 03:03:22 -04:00
// Assert ctx.Doer has permission to read headRepo's codes
2022-05-11 06:09:36 -04:00
permHead , err := access_model . GetUserRepoPermission ( ctx , ci . HeadRepo , ctx . Doer )
2020-01-16 14:59:07 -05:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
2021-09-27 08:19:34 -04:00
return nil
2020-01-16 14:59:07 -05:00
}
2021-11-09 14:57:58 -05:00
if ! permHead . CanRead ( unit . TypeCode ) {
2020-01-16 14:59:07 -05:00
if log . IsTrace ( ) {
log . Trace ( "Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v" ,
2022-03-22 03:03:22 -04:00
ctx . Doer ,
2021-09-27 08:19:34 -04:00
ci . HeadRepo ,
2020-01-16 14:59:07 -05:00
permHead )
}
ctx . NotFound ( "ParseCompareInfo" , nil )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2022-04-28 11:45:33 -04:00
ctx . Data [ "CanWriteToHeadRepo" ] = permHead . CanWrite ( unit . TypeCode )
2019-06-07 16:29:29 -04:00
}
2020-05-12 01:52:46 -04:00
// If we have a rootRepo and it's different from:
// 1. the computed base
// 2. the computed head
// then get the branches of it
if rootRepo != nil &&
2021-09-27 08:19:34 -04:00
rootRepo . ID != ci . HeadRepo . ID &&
2020-05-12 01:52:46 -04:00
rootRepo . ID != baseRepo . ID {
2022-08-24 22:31:57 -04:00
canRead := access_model . CheckRepoUnitUser ( ctx , rootRepo , ctx . Doer , unit . TypeCode )
2021-10-15 12:05:33 -04:00
if canRead {
2020-05-12 01:52:46 -04:00
ctx . Data [ "RootRepo" ] = rootRepo
2021-10-15 12:05:33 -04:00
if ! fileOnly {
2022-01-19 18:26:57 -05:00
branches , tags , err := getBranchesAndTagsForRepo ( ctx , rootRepo )
2021-10-15 12:05:33 -04:00
if err != nil {
ctx . ServerError ( "GetBranchesForRepo" , err )
return nil
}
ctx . Data [ "RootRepoBranches" ] = branches
ctx . Data [ "RootRepoTags" ] = tags
}
2020-05-12 01:52:46 -04:00
}
}
// If we have a ownForkRepo and it's different from:
// 1. The computed base
2021-05-07 17:10:05 -04:00
// 2. The computed head
2020-05-12 01:52:46 -04:00
// 3. The rootRepo (if we have one)
// then get the branches from it.
if ownForkRepo != nil &&
2021-09-27 08:19:34 -04:00
ownForkRepo . ID != ci . HeadRepo . ID &&
2020-05-12 01:52:46 -04:00
ownForkRepo . ID != baseRepo . ID &&
( rootRepo == nil || ownForkRepo . ID != rootRepo . ID ) {
2022-08-24 22:31:57 -04:00
canRead := access_model . CheckRepoUnitUser ( ctx , ownForkRepo , ctx . Doer , unit . TypeCode )
2021-10-15 12:05:33 -04:00
if canRead {
2020-05-12 01:52:46 -04:00
ctx . Data [ "OwnForkRepo" ] = ownForkRepo
2021-10-15 12:05:33 -04:00
if ! fileOnly {
2022-01-19 18:26:57 -05:00
branches , tags , err := getBranchesAndTagsForRepo ( ctx , ownForkRepo )
2021-10-15 12:05:33 -04:00
if err != nil {
ctx . ServerError ( "GetBranchesForRepo" , err )
return nil
}
ctx . Data [ "OwnForkRepoBranches" ] = branches
ctx . Data [ "OwnForkRepoTags" ] = tags
}
2020-05-12 01:52:46 -04:00
}
}
2019-06-07 16:29:29 -04:00
// Check if head branch is valid.
2021-09-27 08:19:34 -04:00
headIsCommit := ci . HeadGitRepo . IsCommitExist ( ci . HeadBranch )
headIsBranch := ci . HeadGitRepo . IsBranchExist ( ci . HeadBranch )
headIsTag := ci . HeadGitRepo . IsTagExist ( ci . HeadBranch )
2019-06-07 16:29:29 -04:00
if ! headIsCommit && ! headIsBranch && ! headIsTag {
// Check if headBranch is short sha commit hash
2021-09-27 08:19:34 -04:00
if headCommit , _ := ci . HeadGitRepo . GetCommit ( ci . HeadBranch ) ; headCommit != nil {
ci . HeadBranch = headCommit . ID . String ( )
ctx . Data [ "HeadBranch" ] = ci . HeadBranch
2019-06-07 16:29:29 -04:00
headIsCommit = true
} else {
ctx . NotFound ( "IsRefExist" , nil )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
}
ctx . Data [ "HeadIsCommit" ] = headIsCommit
ctx . Data [ "HeadIsBranch" ] = headIsBranch
ctx . Data [ "HeadIsTag" ] = headIsTag
// Treat as pull request if both references are branches
if ctx . Data [ "PageIsComparePull" ] == nil {
ctx . Data [ "PageIsComparePull" ] = headIsBranch && baseIsBranch
}
if ctx . Data [ "PageIsComparePull" ] == true && ! permBase . CanReadIssuesOrPulls ( true ) {
if log . IsTrace ( ) {
log . Trace ( "Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v" ,
2022-03-22 03:03:22 -04:00
ctx . Doer ,
2019-06-07 16:29:29 -04:00
baseRepo ,
permBase )
}
ctx . NotFound ( "ParseCompareInfo" , nil )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2021-09-27 08:19:34 -04:00
baseBranchRef := ci . BaseBranch
2020-06-11 19:49:47 -04:00
if baseIsBranch {
2021-09-27 08:19:34 -04:00
baseBranchRef = git . BranchPrefix + ci . BaseBranch
2020-06-11 19:49:47 -04:00
} else if baseIsTag {
2021-09-27 08:19:34 -04:00
baseBranchRef = git . TagPrefix + ci . BaseBranch
2020-06-11 19:49:47 -04:00
}
2021-09-27 08:19:34 -04:00
headBranchRef := ci . HeadBranch
2020-06-11 19:49:47 -04:00
if headIsBranch {
2021-09-27 08:19:34 -04:00
headBranchRef = git . BranchPrefix + ci . HeadBranch
2020-06-11 19:49:47 -04:00
} else if headIsTag {
2021-09-27 08:19:34 -04:00
headBranchRef = git . TagPrefix + ci . HeadBranch
2020-06-11 19:49:47 -04:00
}
2021-10-15 12:05:33 -04:00
ci . CompareInfo , err = ci . HeadGitRepo . GetCompareInfo ( baseRepo . RepoPath ( ) , baseBranchRef , headBranchRef , ci . DirectComparison , fileOnly )
2019-06-07 16:29:29 -04:00
if err != nil {
ctx . ServerError ( "GetCompareInfo" , err )
2021-09-27 08:19:34 -04:00
return nil
2019-06-07 16:29:29 -04:00
}
2021-09-27 08:19:34 -04:00
ctx . Data [ "BeforeCommitID" ] = ci . CompareInfo . MergeBase
2019-06-07 16:29:29 -04:00
2021-09-27 08:19:34 -04:00
return ci
2019-06-07 16:29:29 -04:00
}
// PrepareCompareDiff renders compare diff page
func PrepareCompareDiff (
ctx * context . Context ,
2021-09-27 08:19:34 -04:00
ci * CompareInfo ,
2022-01-20 12:46:10 -05:00
whitespaceBehavior string ,
) bool {
2019-06-07 16:29:29 -04:00
var (
repo = ctx . Repo . Repository
err error
title string
)
// Get diff information.
2021-09-27 08:19:34 -04:00
ctx . Data [ "CommitRepoLink" ] = ci . HeadRepo . Link ( )
2019-06-07 16:29:29 -04:00
2021-09-27 08:19:34 -04:00
headCommitID := ci . CompareInfo . HeadCommitID
2019-06-07 16:29:29 -04:00
ctx . Data [ "AfterCommitID" ] = headCommitID
2021-09-27 08:19:34 -04:00
if ( headCommitID == ci . CompareInfo . MergeBase && ! ci . DirectComparison ) ||
headCommitID == ci . CompareInfo . BaseCommitID {
2019-06-07 16:29:29 -04:00
ctx . Data [ "IsNothingToCompare" ] = true
2021-11-09 14:57:58 -05:00
if unit , err := repo . GetUnit ( unit . TypePullRequests ) ; err == nil {
2021-03-03 22:41:23 -05:00
config := unit . PullRequestsConfig ( )
2021-03-29 02:58:48 -04:00
2021-03-03 22:41:23 -05:00
if ! config . AutodetectManualMerge {
2021-09-27 08:19:34 -04:00
allowEmptyPr := ! ( ci . BaseBranch == ci . HeadBranch && ctx . Repo . Repository . Name == ci . HeadRepo . Name )
2021-03-29 02:58:48 -04:00
ctx . Data [ "AllowEmptyPr" ] = allowEmptyPr
return ! allowEmptyPr
2021-03-03 22:41:23 -05:00
}
2021-03-29 02:58:48 -04:00
ctx . Data [ "AllowEmptyPr" ] = false
2021-03-03 22:41:23 -05:00
}
2019-06-07 16:29:29 -04:00
return true
}
2021-09-27 08:19:34 -04:00
beforeCommitID := ci . CompareInfo . MergeBase
if ci . DirectComparison {
beforeCommitID = ci . CompareInfo . BaseCommitID
}
2021-11-21 11:51:08 -05:00
maxLines , maxFiles := setting . Git . MaxGitDiffLines , setting . Git . MaxGitDiffFiles
files := ctx . FormStrings ( "files" )
if len ( files ) == 2 || len ( files ) == 1 {
maxLines , maxFiles = - 1 , - 1
}
diff , err := gitdiff . GetDiff ( ci . HeadGitRepo ,
& gitdiff . DiffOptions {
BeforeCommitID : beforeCommitID ,
AfterCommitID : headCommitID ,
SkipTo : ctx . FormString ( "skip-to" ) ,
MaxLines : maxLines ,
MaxLineCharacters : setting . Git . MaxGitDiffLineCharacters ,
MaxFiles : maxFiles ,
WhitespaceBehavior : whitespaceBehavior ,
DirectComparison : ci . DirectComparison ,
} , ctx . FormStrings ( "files" ) ... )
2019-06-07 16:29:29 -04:00
if err != nil {
2021-02-12 23:35:43 -05:00
ctx . ServerError ( "GetDiffRangeWithWhitespaceBehavior" , err )
2019-06-07 16:29:29 -04:00
return false
}
ctx . Data [ "Diff" ] = diff
2020-05-26 01:58:07 -04:00
ctx . Data [ "DiffNotAvailable" ] = diff . NumFiles == 0
2019-06-07 16:29:29 -04:00
2021-09-27 08:19:34 -04:00
headCommit , err := ci . HeadGitRepo . GetCommit ( headCommitID )
2019-06-07 16:29:29 -04:00
if err != nil {
ctx . ServerError ( "GetCommit" , err )
return false
}
2019-09-16 05:03:22 -04:00
baseGitRepo := ctx . Repo . GitRepo
2021-09-27 08:19:34 -04:00
baseCommitID := ci . CompareInfo . BaseCommitID
2019-09-16 05:03:22 -04:00
baseCommit , err := baseGitRepo . GetCommit ( baseCommitID )
if err != nil {
ctx . ServerError ( "GetCommit" , err )
return false
}
2022-06-12 11:51:54 -04:00
commits := git_model . ConvertFromGitCommit ( ci . CompareInfo . Commits , ci . HeadRepo )
2021-08-09 14:08:51 -04:00
ctx . Data [ "Commits" ] = commits
ctx . Data [ "CommitCount" ] = len ( commits )
if len ( commits ) == 1 {
c := commits [ 0 ]
2019-06-07 16:29:29 -04:00
title = strings . TrimSpace ( c . UserCommit . Summary ( ) )
body := strings . Split ( strings . TrimSpace ( c . UserCommit . Message ( ) ) , "\n" )
if len ( body ) > 1 {
ctx . Data [ "content" ] = strings . Join ( body [ 1 : ] , "\n" )
}
} else {
2021-09-27 08:19:34 -04:00
title = ci . HeadBranch
2019-06-07 16:29:29 -04:00
}
2021-07-24 22:59:27 -04:00
if len ( title ) > 255 {
var trailer string
title , trailer = util . SplitStringAtByteN ( title , 255 )
if len ( trailer ) > 0 {
if ctx . Data [ "content" ] != nil {
ctx . Data [ "content" ] = fmt . Sprintf ( "%s\n\n%s" , trailer , ctx . Data [ "content" ] )
} else {
ctx . Data [ "content" ] = trailer + "\n"
}
}
}
2019-06-07 16:29:29 -04:00
ctx . Data [ "title" ] = title
2021-09-27 08:19:34 -04:00
ctx . Data [ "Username" ] = ci . HeadUser . Name
ctx . Data [ "Reponame" ] = ci . HeadRepo . Name
2019-06-07 16:29:29 -04:00
2021-11-16 13:18:25 -05:00
setCompareContext ( ctx , baseCommit , headCommit , ci . HeadUser . Name , repo . Name )
2019-09-16 05:03:22 -04:00
2019-06-07 16:29:29 -04:00
return false
}
2022-01-19 18:26:57 -05:00
func getBranchesAndTagsForRepo ( ctx gocontext . Context , repo * repo_model . Repository ) ( branches , tags [ ] string , err error ) {
2022-03-29 15:13:41 -04:00
gitRepo , err := git . OpenRepository ( ctx , repo . RepoPath ( ) )
2019-10-30 01:58:18 -04:00
if err != nil {
2021-10-15 12:05:33 -04:00
return nil , nil , err
2019-10-30 01:58:18 -04:00
}
2020-05-12 01:52:46 -04:00
defer gitRepo . Close ( )
2019-11-13 02:01:19 -05:00
2021-12-08 14:08:16 -05:00
branches , _ , err = gitRepo . GetBranchNames ( 0 , 0 )
2019-10-30 01:58:18 -04:00
if err != nil {
2021-10-15 12:05:33 -04:00
return nil , nil , err
2019-10-30 01:58:18 -04:00
}
2021-10-15 12:05:33 -04:00
tags , err = gitRepo . GetTags ( 0 , 0 )
2021-05-07 17:10:05 -04:00
if err != nil {
2021-10-15 12:05:33 -04:00
return nil , nil , err
2021-05-07 17:10:05 -04:00
}
2021-10-15 12:05:33 -04:00
return branches , tags , nil
2019-10-30 01:58:18 -04:00
}
2019-06-07 16:29:29 -04:00
// CompareDiff show different from one commit to another commit
func CompareDiff ( ctx * context . Context ) {
2021-09-27 08:19:34 -04:00
ci := ParseCompareInfo ( ctx )
2021-08-31 00:16:23 -04:00
defer func ( ) {
2021-09-30 15:31:02 -04:00
if ci != nil && ci . HeadGitRepo != nil {
2021-09-27 08:19:34 -04:00
ci . HeadGitRepo . Close ( )
2021-08-31 00:16:23 -04:00
}
} ( )
2019-06-07 16:29:29 -04:00
if ctx . Written ( ) {
return
}
2019-11-13 02:01:19 -05:00
2021-12-16 14:01:14 -05:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2021-09-27 08:19:34 -04:00
ctx . Data [ "DirectComparison" ] = ci . DirectComparison
ctx . Data [ "OtherCompareSeparator" ] = ".."
ctx . Data [ "CompareSeparator" ] = "..."
if ci . DirectComparison {
ctx . Data [ "CompareSeparator" ] = ".."
ctx . Data [ "OtherCompareSeparator" ] = "..."
}
nothingToCompare := PrepareCompareDiff ( ctx , ci ,
2021-02-12 23:35:43 -05:00
gitdiff . GetWhitespaceFlag ( ctx . Data [ "WhitespaceBehavior" ] . ( string ) ) )
2019-06-07 16:29:29 -04:00
if ctx . Written ( ) {
return
}
2021-05-07 17:10:05 -04:00
baseGitRepo := ctx . Repo . GitRepo
2021-09-10 13:30:37 -04:00
baseTags , err := baseGitRepo . GetTags ( 0 , 0 )
2021-05-07 17:10:05 -04:00
if err != nil {
ctx . ServerError ( "GetTags" , err )
return
}
ctx . Data [ "Tags" ] = baseTags
2019-08-11 11:23:49 -04:00
2021-10-15 12:05:33 -04:00
fileOnly := ctx . FormBool ( "file-only" )
if fileOnly {
ctx . HTML ( http . StatusOK , tplDiffBox )
return
}
2021-12-08 14:08:16 -05:00
headBranches , _ , err := ci . HeadGitRepo . GetBranchNames ( 0 , 0 )
2021-05-07 17:10:05 -04:00
if err != nil {
ctx . ServerError ( "GetBranches" , err )
return
}
ctx . Data [ "HeadBranches" ] = headBranches
2021-09-27 08:19:34 -04:00
headTags , err := ci . HeadGitRepo . GetTags ( 0 , 0 )
2021-05-07 17:10:05 -04:00
if err != nil {
ctx . ServerError ( "GetTags" , err )
return
}
ctx . Data [ "HeadTags" ] = headTags
if ctx . Data [ "PageIsComparePull" ] == true {
2022-06-13 05:37:59 -04:00
pr , err := issues_model . GetUnmergedPullRequest ( ci . HeadRepo . ID , ctx . Repo . Repository . ID , ci . HeadBranch , ci . BaseBranch , issues_model . PullRequestFlowGithub )
2019-06-07 16:29:29 -04:00
if err != nil {
2022-06-13 05:37:59 -04:00
if ! issues_model . IsErrPullRequestNotExist ( err ) {
2019-06-07 16:29:29 -04:00
ctx . ServerError ( "GetUnmergedPullRequest" , err )
return
}
} else {
ctx . Data [ "HasPullRequest" ] = true
2021-12-24 07:14:42 -05:00
if err := pr . LoadIssue ( ) ; err != nil {
ctx . ServerError ( "LoadIssue" , err )
return
}
2019-06-07 16:29:29 -04:00
ctx . Data [ "PullRequest" ] = pr
2021-04-05 11:30:52 -04:00
ctx . HTML ( http . StatusOK , tplCompareDiff )
2019-06-07 16:29:29 -04:00
return
}
if ! nothingToCompare {
// Setup information for new form.
2020-01-19 01:43:38 -05:00
RetrieveRepoMetas ( ctx , ctx . Repo . Repository , true )
2019-06-07 16:29:29 -04:00
if ctx . Written ( ) {
return
}
}
}
beforeCommitID := ctx . Data [ "BeforeCommitID" ] . ( string )
afterCommitID := ctx . Data [ "AfterCommitID" ] . ( string )
2021-09-27 08:19:34 -04:00
separator := "..."
if ci . DirectComparison {
separator = ".."
}
ctx . Data [ "Title" ] = "Comparing " + base . ShortSha ( beforeCommitID ) + separator + base . ShortSha ( afterCommitID )
2019-06-07 16:29:29 -04:00
ctx . Data [ "IsRepoToolbarCommits" ] = true
ctx . Data [ "IsDiffCompare" ] = true
ctx . Data [ "RequireTribute" ] = true
2022-09-02 03:58:49 -04:00
templateErrs := setTemplateIfExists ( ctx , pullRequestTemplateKey , pullRequestTemplateCandidates )
if len ( templateErrs ) > 0 {
ctx . Flash . Warning ( renderErrorOfTemplates ( ctx , templateErrs ) , true )
}
2022-07-23 23:45:33 -04:00
// If a template content is set, prepend the "content". In this case that's only
// applicable if you have one commit to compare and that commit has a message.
// In that case the commit message will be prepend to the template body.
if templateContent , ok := ctx . Data [ pullRequestTemplateKey ] . ( string ) ; ok && templateContent != "" {
if content , ok := ctx . Data [ "content" ] . ( string ) ; ok && content != "" {
// Re-use the same key as that's priortized over the "content" key.
// Add two new lines between the content to ensure there's always at least
// one empty line between them.
ctx . Data [ pullRequestTemplateKey ] = content + "\n\n" + templateContent
}
}
2020-10-05 01:49:33 -04:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
upload . AddUploadContext ( ctx , "comment" )
2019-06-07 16:29:29 -04:00
2021-11-09 14:57:58 -05:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWrite ( unit . TypePullRequests )
2020-04-04 01:39:48 -04:00
2021-04-05 11:30:52 -04:00
ctx . HTML ( http . StatusOK , tplCompare )
2019-06-07 16:29:29 -04:00
}
2019-11-14 21:52:59 -05:00
// ExcerptBlob render blob excerpt contents
func ExcerptBlob ( ctx * context . Context ) {
commitID := ctx . Params ( "sha" )
2021-07-28 21:42:15 -04:00
lastLeft := ctx . FormInt ( "last_left" )
lastRight := ctx . FormInt ( "last_right" )
idxLeft := ctx . FormInt ( "left" )
idxRight := ctx . FormInt ( "right" )
leftHunkSize := ctx . FormInt ( "left_hunk_size" )
rightHunkSize := ctx . FormInt ( "right_hunk_size" )
2021-08-10 20:31:13 -04:00
anchor := ctx . FormString ( "anchor" )
direction := ctx . FormString ( "direction" )
filePath := ctx . FormString ( "path" )
2019-11-14 21:52:59 -05:00
gitRepo := ctx . Repo . GitRepo
2022-02-05 13:26:12 -05:00
if ctx . FormBool ( "wiki" ) {
var err error
2022-03-29 15:13:41 -04:00
gitRepo , err = git . OpenRepository ( ctx , ctx . Repo . Repository . WikiPath ( ) )
2022-02-05 13:26:12 -05:00
if err != nil {
ctx . ServerError ( "OpenRepository" , err )
return
}
defer gitRepo . Close ( )
}
2020-08-20 10:53:06 -04:00
chunkSize := gitdiff . BlobExcerptChunkSize
2019-11-14 21:52:59 -05:00
commit , err := gitRepo . GetCommit ( commitID )
if err != nil {
2021-04-05 11:30:52 -04:00
ctx . Error ( http . StatusInternalServerError , "GetCommit" )
2019-11-14 21:52:59 -05:00
return
}
section := & gitdiff . DiffSection {
2020-07-12 14:25:05 -04:00
FileName : filePath ,
Name : filePath ,
2019-11-14 21:52:59 -05:00
}
if direction == "up" && ( idxLeft - lastLeft ) > chunkSize {
idxLeft -= chunkSize
idxRight -= chunkSize
leftHunkSize += chunkSize
rightHunkSize += chunkSize
section . Lines , err = getExcerptLines ( commit , filePath , idxLeft - 1 , idxRight - 1 , chunkSize )
} else if direction == "down" && ( idxLeft - lastLeft ) > chunkSize {
section . Lines , err = getExcerptLines ( commit , filePath , lastLeft , lastRight , chunkSize )
lastLeft += chunkSize
lastRight += chunkSize
} else {
2021-06-24 11:47:46 -04:00
offset := - 1
if direction == "down" {
offset = 0
}
section . Lines , err = getExcerptLines ( commit , filePath , lastLeft , lastRight , idxRight - lastRight + offset )
2019-11-14 21:52:59 -05:00
leftHunkSize = 0
rightHunkSize = 0
idxLeft = lastLeft
idxRight = lastRight
}
if err != nil {
2021-04-05 11:30:52 -04:00
ctx . Error ( http . StatusInternalServerError , "getExcerptLines" )
2019-11-14 21:52:59 -05:00
return
}
if idxRight > lastRight {
lineText := " "
if rightHunkSize > 0 || leftHunkSize > 0 {
lineText = fmt . Sprintf ( "@@ -%d,%d +%d,%d @@\n" , idxLeft , leftHunkSize , idxRight , rightHunkSize )
}
lineText = html . EscapeString ( lineText )
lineSection := & gitdiff . DiffLine {
Type : gitdiff . DiffLineSection ,
Content : lineText ,
SectionInfo : & gitdiff . DiffLineSectionInfo {
Path : filePath ,
LastLeftIdx : lastLeft ,
LastRightIdx : lastRight ,
LeftIdx : idxLeft ,
RightIdx : idxRight ,
LeftHunkSize : leftHunkSize ,
RightHunkSize : rightHunkSize ,
2022-01-20 12:46:10 -05:00
} ,
}
2019-11-14 21:52:59 -05:00
if direction == "up" {
section . Lines = append ( [ ] * gitdiff . DiffLine { lineSection } , section . Lines ... )
} else if direction == "down" {
section . Lines = append ( section . Lines , lineSection )
}
}
ctx . Data [ "section" ] = section
2022-05-08 18:29:50 -04:00
ctx . Data [ "FileNameHash" ] = base . EncodeSha1 ( filePath )
2019-11-14 21:52:59 -05:00
ctx . Data [ "AfterCommitID" ] = commitID
ctx . Data [ "Anchor" ] = anchor
2021-04-05 11:30:52 -04:00
ctx . HTML ( http . StatusOK , tplBlobExcerpt )
2019-11-14 21:52:59 -05:00
}
2021-12-19 23:41:31 -05:00
func getExcerptLines ( commit * git . Commit , filePath string , idxLeft , idxRight , chunkSize int ) ( [ ] * gitdiff . DiffLine , error ) {
2019-11-14 21:52:59 -05:00
blob , err := commit . Tree . GetBlobByPath ( filePath )
if err != nil {
return nil , err
}
reader , err := blob . DataAsync ( )
if err != nil {
return nil , err
}
defer reader . Close ( )
scanner := bufio . NewScanner ( reader )
var diffLines [ ] * gitdiff . DiffLine
for line := 0 ; line < idxRight + chunkSize ; line ++ {
if ok := scanner . Scan ( ) ; ! ok {
break
}
if line < idxRight {
continue
}
lineText := scanner . Text ( )
diffLine := & gitdiff . DiffLine {
LeftIdx : idxLeft + ( line - idxRight ) + 1 ,
RightIdx : line + 1 ,
Type : gitdiff . DiffLinePlain ,
Content : " " + lineText ,
}
diffLines = append ( diffLines , diffLine )
}
return diffLines , nil
}