mirror of https://github.com/harness/drone.git
[code-1288] new diff api features [paths, line expanders] (#968)
parent
db7b16dd81
commit
7f7f8bf625
|
@ -34,11 +34,6 @@ linters-settings:
|
|||
# Default: 40
|
||||
statements: 50
|
||||
|
||||
gocognit:
|
||||
# Minimal code complexity to report
|
||||
# Default: 30 (but we recommend 10-20)
|
||||
min-complexity: 20
|
||||
|
||||
gocritic:
|
||||
# Settings passed to gocritic.
|
||||
# The settings key is the name of a supported gocritic checker.
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/git"
|
||||
gittypes "github.com/harness/gitness/git/types"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
@ -28,11 +29,12 @@ import (
|
|||
// RawDiff writes raw git diff to writer w.
|
||||
func (c *Controller) RawDiff(
|
||||
ctx context.Context,
|
||||
w io.Writer,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
pullreqNum int64,
|
||||
setSHAs func(sourceSHA, mergeBaseSHA string),
|
||||
w io.Writer,
|
||||
files ...gittypes.FileDiffRequest,
|
||||
) error {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
|
||||
if err != nil {
|
||||
|
@ -48,12 +50,12 @@ func (c *Controller) RawDiff(
|
|||
setSHAs(pr.SourceSHA, pr.MergeBaseSHA)
|
||||
}
|
||||
|
||||
return c.git.RawDiff(ctx, &git.DiffParams{
|
||||
return c.git.RawDiff(ctx, w, &git.DiffParams{
|
||||
ReadParams: git.CreateReadParams(repo),
|
||||
BaseRef: pr.MergeBaseSHA,
|
||||
HeadRef: pr.SourceSHA,
|
||||
MergeBase: true,
|
||||
}, w)
|
||||
}, files...)
|
||||
}
|
||||
|
||||
func (c *Controller) Diff(
|
||||
|
@ -63,6 +65,7 @@ func (c *Controller) Diff(
|
|||
pullreqNum int64,
|
||||
setSHAs func(sourceSHA, mergeBaseSHA string),
|
||||
includePatch bool,
|
||||
files ...gittypes.FileDiffRequest,
|
||||
) (types.Stream[*git.FileDiff], error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
|
||||
if err != nil {
|
||||
|
@ -84,7 +87,7 @@ func (c *Controller) Diff(
|
|||
HeadRef: pr.SourceSHA,
|
||||
MergeBase: true,
|
||||
IncludePatch: includePatch,
|
||||
}))
|
||||
}, files...))
|
||||
|
||||
return reader, nil
|
||||
}
|
||||
|
|
|
@ -23,16 +23,18 @@ import (
|
|||
"github.com/harness/gitness/app/api/usererror"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/git"
|
||||
gittypes "github.com/harness/gitness/git/types"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
func (c *Controller) RawDiff(
|
||||
ctx context.Context,
|
||||
w io.Writer,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
path string,
|
||||
w io.Writer,
|
||||
files ...gittypes.FileDiffRequest,
|
||||
) error {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView, true)
|
||||
if err != nil {
|
||||
|
@ -44,12 +46,12 @@ func (c *Controller) RawDiff(
|
|||
return err
|
||||
}
|
||||
|
||||
return c.git.RawDiff(ctx, &git.DiffParams{
|
||||
return c.git.RawDiff(ctx, w, &git.DiffParams{
|
||||
ReadParams: git.CreateReadParams(repo),
|
||||
BaseRef: info.BaseRef,
|
||||
HeadRef: info.HeadRef,
|
||||
MergeBase: info.MergeBase,
|
||||
}, w)
|
||||
}, files...)
|
||||
}
|
||||
|
||||
func (c *Controller) CommitDiff(
|
||||
|
@ -130,6 +132,7 @@ func (c *Controller) Diff(
|
|||
repoRef string,
|
||||
path string,
|
||||
includePatch bool,
|
||||
files ...gittypes.FileDiffRequest,
|
||||
) (types.Stream[*git.FileDiff], error) {
|
||||
repo, err := c.repoStore.FindByRef(ctx, repoRef)
|
||||
if err != nil {
|
||||
|
@ -151,7 +154,7 @@ func (c *Controller) Diff(
|
|||
HeadRef: info.HeadRef,
|
||||
MergeBase: info.MergeBase,
|
||||
IncludePatch: includePatch,
|
||||
}))
|
||||
}, files...))
|
||||
|
||||
return reader, nil
|
||||
}
|
||||
|
|
|
@ -15,12 +15,16 @@
|
|||
package pullreq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/errors"
|
||||
gittypes "github.com/harness/gitness/git/types"
|
||||
)
|
||||
|
||||
// HandleDiff returns a http.HandlerFunc that returns diff.
|
||||
|
@ -45,9 +49,21 @@ func HandleDiff(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
|||
w.Header().Set("X-Source-Sha", sourceSHA)
|
||||
w.Header().Set("X-Merge-Base-Sha", mergeBaseSHA)
|
||||
}
|
||||
files := gittypes.FileDiffRequests{}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
if err = json.NewDecoder(r.Body).Decode(&files); err != nil && !errors.Is(err, io.EOF) {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
case http.MethodGet:
|
||||
// TBD: this will be removed in future because of URL limit in browser to 2048 chars.
|
||||
files = request.GetFileDiffFromQuery(r)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.Header.Get("Accept"), "text/plain") {
|
||||
err := pullreqCtrl.RawDiff(ctx, session, repoRef, pullreqNumber, setSHAs, w)
|
||||
err := pullreqCtrl.RawDiff(ctx, w, session, repoRef, pullreqNumber, setSHAs, files...)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusOK)
|
||||
}
|
||||
|
@ -55,7 +71,7 @@ func HandleDiff(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
|||
}
|
||||
|
||||
_, includePatch := request.QueryParam(r, "include_patch")
|
||||
stream, err := pullreqCtrl.Diff(ctx, session, repoRef, pullreqNumber, setSHAs, includePatch)
|
||||
stream, err := pullreqCtrl.Diff(ctx, session, repoRef, pullreqNumber, setSHAs, includePatch, files...)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -15,12 +15,16 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/errors"
|
||||
gittypes "github.com/harness/gitness/git/types"
|
||||
)
|
||||
|
||||
// HandleDiff returns the diff between two commits, branches or tags.
|
||||
|
@ -36,8 +40,20 @@ func HandleDiff(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
|
||||
path := request.GetOptionalRemainderFromPath(r)
|
||||
|
||||
files := gittypes.FileDiffRequests{}
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
if err = json.NewDecoder(r.Body).Decode(&files); err != nil && !errors.Is(err, io.EOF) {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
case http.MethodGet:
|
||||
// TBD: this will be removed in future because of URL limit in browser to 2048 chars.
|
||||
files = request.GetFileDiffFromQuery(r)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.Header.Get("Accept"), "text/plain") {
|
||||
err := repoCtrl.RawDiff(ctx, session, repoRef, path, w)
|
||||
err := repoCtrl.RawDiff(ctx, w, session, repoRef, path, files...)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusOK)
|
||||
}
|
||||
|
@ -45,7 +61,7 @@ func HandleDiff(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
}
|
||||
|
||||
_, includePatch := request.QueryParam(r, "include_patch")
|
||||
stream, err := repoCtrl.Diff(ctx, session, repoRef, path, includePatch)
|
||||
stream, err := repoCtrl.Diff(ctx, session, repoRef, path, includePatch, files...)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -89,3 +89,9 @@ func Generate() *openapi3.Spec {
|
|||
|
||||
return reflector.Spec
|
||||
}
|
||||
|
||||
func panicOnErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/app/api/usererror"
|
||||
"github.com/harness/gitness/git"
|
||||
gittypes "github.com/harness/gitness/git/types"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
|
@ -122,6 +123,16 @@ type fileViewDeletePullReqRequest struct {
|
|||
Path string `path:"file_path"`
|
||||
}
|
||||
|
||||
type getRawPRDiffRequest struct {
|
||||
pullReqRequest
|
||||
Path []string `query:"path" description:"provide path for diff operation"`
|
||||
}
|
||||
|
||||
type postRawPRDiffRequest struct {
|
||||
pullReqRequest
|
||||
gittypes.FileDiffRequests
|
||||
}
|
||||
|
||||
var queryParameterQueryPullRequest = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamQuery,
|
||||
|
@ -549,11 +560,24 @@ func pullReqOperations(reflector *openapi3.Reflector) {
|
|||
opDiff := openapi3.Operation{}
|
||||
opDiff.WithTags("pullreq")
|
||||
opDiff.WithMapOfAnything(map[string]interface{}{"operationId": "diffPullReq"})
|
||||
_ = reflector.SetStringResponse(&opDiff, http.StatusOK, "text/plain")
|
||||
_ = reflector.SetJSONResponse(&opDiff, new([]git.FileDiff), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/pullreq/{pullreq_number}/diff", opDiff)
|
||||
panicOnErr(reflector.SetRequest(&opDiff, new(getRawPRDiffRequest), http.MethodGet))
|
||||
panicOnErr(reflector.SetStringResponse(&opDiff, http.StatusOK, "text/plain"))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new([]git.FileDiff), http.StatusOK))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusInternalServerError))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusUnauthorized))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusForbidden))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusNotFound))
|
||||
panicOnErr(reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/pullreq/{pullreq_number}/diff", opDiff))
|
||||
|
||||
opPostDiff := openapi3.Operation{}
|
||||
opPostDiff.WithTags("pullreq")
|
||||
opPostDiff.WithMapOfAnything(map[string]interface{}{"operationId": "diffPullReqPost"})
|
||||
panicOnErr(reflector.SetRequest(&opPostDiff, new(postRawPRDiffRequest), http.MethodPost))
|
||||
panicOnErr(reflector.SetStringResponse(&opPostDiff, http.StatusOK, "text/plain"))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new([]git.FileDiff), http.StatusOK))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new(usererror.Error), http.StatusInternalServerError))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new(usererror.Error), http.StatusUnauthorized))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new(usererror.Error), http.StatusForbidden))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new(usererror.Error), http.StatusNotFound))
|
||||
panicOnErr(reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/pullreq/{pullreq_number}/diff", opPostDiff))
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/harness/gitness/app/api/usererror"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/git"
|
||||
gittypes "github.com/harness/gitness/git/types"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
|
@ -154,6 +155,13 @@ type deleteTagRequest struct {
|
|||
|
||||
type getRawDiffRequest struct {
|
||||
repoRequest
|
||||
Range string `path:"range" example:"main..dev"`
|
||||
Path []string `query:"path" description:"provide path for diff operation"`
|
||||
}
|
||||
|
||||
type postRawDiffRequest struct {
|
||||
repoRequest
|
||||
gittypes.FileDiffRequests
|
||||
Range string `path:"range" example:"main..dev"`
|
||||
}
|
||||
|
||||
|
@ -720,13 +728,24 @@ func repoOperations(reflector *openapi3.Reflector) {
|
|||
opDiff := openapi3.Operation{}
|
||||
opDiff.WithTags("repository")
|
||||
opDiff.WithMapOfAnything(map[string]interface{}{"operationId": "rawDiff"})
|
||||
_ = reflector.SetRequest(&opDiff, new(getRawDiffRequest), http.MethodGet)
|
||||
_ = reflector.SetStringResponse(&opDiff, http.StatusOK, "text/plain")
|
||||
_ = reflector.SetJSONResponse(&opDiff, []git.FileDiff{}, http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/diff/{range}", opDiff)
|
||||
panicOnErr(reflector.SetRequest(&opDiff, new(getRawDiffRequest), http.MethodGet))
|
||||
panicOnErr(reflector.SetStringResponse(&opDiff, http.StatusOK, "text/plain"))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, []git.FileDiff{}, http.StatusOK))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusInternalServerError))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusUnauthorized))
|
||||
panicOnErr(reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusForbidden))
|
||||
panicOnErr(reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/diff/{range}", opDiff))
|
||||
|
||||
opPostDiff := openapi3.Operation{}
|
||||
opPostDiff.WithTags("repository")
|
||||
opPostDiff.WithMapOfAnything(map[string]interface{}{"operationId": "rawDiffPost"})
|
||||
panicOnErr(reflector.SetRequest(&opPostDiff, new(postRawDiffRequest), http.MethodPost))
|
||||
panicOnErr(reflector.SetStringResponse(&opPostDiff, http.StatusOK, "text/plain"))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, []git.FileDiff{}, http.StatusOK))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new(usererror.Error), http.StatusInternalServerError))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new(usererror.Error), http.StatusUnauthorized))
|
||||
panicOnErr(reflector.SetJSONResponse(&opPostDiff, new(usererror.Error), http.StatusForbidden))
|
||||
panicOnErr(reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/diff/{range}", opPostDiff))
|
||||
|
||||
opCommitDiff := openapi3.Operation{}
|
||||
opCommitDiff.WithTags("repository")
|
||||
|
|
|
@ -17,9 +17,11 @@ package request
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/app/api/usererror"
|
||||
gittypes "github.com/harness/gitness/git/types"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
@ -132,3 +134,29 @@ func GetGitServiceTypeFromQuery(r *http.Request) (enum.GitServiceType, error) {
|
|||
|
||||
return enum.ParseGitServiceType(val[len(gitPrefix):])
|
||||
}
|
||||
|
||||
func GetFileDiffFromQuery(r *http.Request) (files gittypes.FileDiffRequests) {
|
||||
paths, _ := QueryParamList(r, "path")
|
||||
ranges, _ := QueryParamList(r, "range")
|
||||
|
||||
for i, filepath := range paths {
|
||||
start := 0
|
||||
end := 0
|
||||
if i < len(ranges) {
|
||||
linesRange := ranges[i]
|
||||
parts := strings.Split(linesRange, ":")
|
||||
if len(parts) > 1 {
|
||||
end, _ = strconv.Atoi(parts[1])
|
||||
}
|
||||
if len(parts) > 0 {
|
||||
start, _ = strconv.Atoi(parts[0])
|
||||
}
|
||||
}
|
||||
files = append(files, gittypes.FileDiffRequest{
|
||||
Path: filepath,
|
||||
StartLine: start,
|
||||
EndLine: end,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/harness/gitness/git/types"
|
||||
)
|
||||
|
||||
func TestGetFileDiffRequestsFromQuery(t *testing.T) {
|
||||
type args struct {
|
||||
r *http.Request
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantFiles types.FileDiffRequests
|
||||
}{
|
||||
{
|
||||
name: "full range",
|
||||
args: args{
|
||||
r: &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "/diff",
|
||||
RawQuery: "path=file.txt&range=1:20",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFiles: types.FileDiffRequests{
|
||||
{
|
||||
Path: "file.txt",
|
||||
StartLine: 1,
|
||||
EndLine: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "start range",
|
||||
args: args{
|
||||
r: &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "/diff",
|
||||
RawQuery: "path=file.txt&range=1",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFiles: types.FileDiffRequests{
|
||||
{
|
||||
Path: "file.txt",
|
||||
StartLine: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "end range",
|
||||
args: args{
|
||||
r: &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "/diff",
|
||||
RawQuery: "path=file.txt&range=:20",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFiles: types.FileDiffRequests{
|
||||
{
|
||||
Path: "file.txt",
|
||||
EndLine: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi path",
|
||||
args: args{
|
||||
r: &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "/diff",
|
||||
RawQuery: "path=file.txt&range=:20&path=file1.txt&range=&path=file2.txt&range=1:15",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFiles: types.FileDiffRequests{
|
||||
{
|
||||
Path: "file.txt",
|
||||
EndLine: 20,
|
||||
},
|
||||
{
|
||||
Path: "file1.txt",
|
||||
},
|
||||
{
|
||||
Path: "file2.txt",
|
||||
StartLine: 1,
|
||||
EndLine: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi path without some range",
|
||||
args: args{
|
||||
r: &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "/diff",
|
||||
RawQuery: "path=file.txt&range=:20&path=file1.txt&path=file2.txt&range=1:15",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFiles: types.FileDiffRequests{
|
||||
{
|
||||
Path: "file.txt",
|
||||
EndLine: 20,
|
||||
},
|
||||
{
|
||||
Path: "file1.txt",
|
||||
StartLine: 1,
|
||||
EndLine: 15,
|
||||
},
|
||||
{
|
||||
Path: "file2.txt",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotFiles := GetFileDiffFromQuery(tt.args.r); !reflect.DeepEqual(gotFiles, tt.wantFiles) {
|
||||
t.Errorf("GetFileDiffFromQuery() = %v, want %v", gotFiles, tt.wantFiles)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -320,6 +320,7 @@ func setupRepos(r chi.Router,
|
|||
// diffs
|
||||
r.Route("/diff", func(r chi.Router) {
|
||||
r.Get("/*", handlerrepo.HandleDiff(repoCtrl))
|
||||
r.Post("/*", handlerrepo.HandleDiff(repoCtrl))
|
||||
})
|
||||
r.Route("/diff-stats", func(r chi.Router) {
|
||||
r.Get("/*", handlerrepo.HandleDiffStats(repoCtrl))
|
||||
|
@ -518,6 +519,7 @@ func SetupPullReq(r chi.Router, pullreqCtrl *pullreq.Controller) {
|
|||
})
|
||||
r.Get("/codeowners", handlerpullreq.HandleCodeOwner(pullreqCtrl))
|
||||
r.Get("/diff", handlerpullreq.HandleDiff(pullreqCtrl))
|
||||
r.Post("/diff", handlerpullreq.HandleDiff(pullreqCtrl))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -88,11 +88,12 @@ type Adapter interface {
|
|||
headBranch string) (string, error)
|
||||
|
||||
RawDiff(ctx context.Context,
|
||||
w io.Writer,
|
||||
repoPath,
|
||||
base,
|
||||
head string,
|
||||
mergeBase bool,
|
||||
w io.Writer) error
|
||||
paths ...types.FileDiffRequest) error
|
||||
|
||||
CommitDiff(ctx context.Context,
|
||||
repoPath,
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestBlameEmptyFile(t *testing.T) {
|
|||
|
||||
baseBranch := "main"
|
||||
// write empty file to main branch
|
||||
parentSHA := writeFile(t, repo, "file.txt", "", nil)
|
||||
_, parentSHA := writeFile(t, repo, "file.txt", "", nil)
|
||||
|
||||
err := repo.SetReference("refs/heads/"+baseBranch, parentSHA.String())
|
||||
if err != nil {
|
||||
|
|
|
@ -15,10 +15,13 @@
|
|||
package adapter
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/errors"
|
||||
|
@ -28,13 +31,122 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
)
|
||||
|
||||
// modifyHeader needs to modify diff hunk header with the new start line
|
||||
// and end line with calculated span.
|
||||
// if diff hunk header is -100, 50 +100, 50 and startLine = 120, endLine=140
|
||||
// then we need to modify header to -120,20 +120,20.
|
||||
// warning: changes are possible and param endLine may not exist in the future.
|
||||
func modifyHeader(hunk types.HunkHeader, startLine, endLine int) []byte {
|
||||
oldStartLine := hunk.OldLine
|
||||
newStartLine := hunk.NewLine
|
||||
oldSpan := hunk.OldSpan
|
||||
newSpan := hunk.NewSpan
|
||||
|
||||
oldEndLine := oldStartLine + oldSpan
|
||||
newEndLine := newStartLine + newSpan
|
||||
|
||||
if startLine > 0 {
|
||||
if startLine < oldEndLine {
|
||||
oldStartLine = startLine
|
||||
}
|
||||
|
||||
if startLine < newEndLine {
|
||||
newStartLine = startLine
|
||||
}
|
||||
}
|
||||
|
||||
if endLine > 0 {
|
||||
if endLine < oldEndLine {
|
||||
oldSpan = endLine - startLine
|
||||
} else if oldEndLine > startLine {
|
||||
oldSpan = oldEndLine - startLine
|
||||
}
|
||||
|
||||
if endLine < newEndLine {
|
||||
newSpan = endLine - startLine
|
||||
} else if newEndLine > startLine {
|
||||
newSpan = newEndLine - startLine
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf("@@ -%d,%d +%d,%d @@",
|
||||
oldStartLine, oldSpan, newStartLine, newSpan))
|
||||
}
|
||||
|
||||
// cutLinesFromFullFileDiff reads from r and writes to w headers and between
|
||||
// startLine and endLine. if startLine and endLine is equal to 0 then it uses io.Copy
|
||||
// warning: changes are possible and param endLine may not exist in the future
|
||||
func cutLinesFromFullFileDiff(w io.Writer, r io.Reader, startLine, endLine int) error {
|
||||
if startLine < 0 {
|
||||
startLine = 0
|
||||
}
|
||||
|
||||
if endLine < 0 {
|
||||
endLine = 0
|
||||
}
|
||||
|
||||
if startLine == 0 && endLine > 0 {
|
||||
startLine = 1
|
||||
}
|
||||
|
||||
if endLine < startLine {
|
||||
endLine = 0
|
||||
}
|
||||
|
||||
// no need for processing lines just copy the data
|
||||
if startLine == 0 && endLine == 0 {
|
||||
_, err := io.Copy(w, r)
|
||||
return err
|
||||
}
|
||||
|
||||
linePos := 0
|
||||
start := false
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
|
||||
if start {
|
||||
linePos++
|
||||
}
|
||||
|
||||
if endLine > 0 && linePos > endLine {
|
||||
break
|
||||
}
|
||||
|
||||
if linePos > 0 &&
|
||||
(startLine > 0 && linePos < startLine) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(line) >= 2 && bytes.HasPrefix(line, []byte{'@', '@'}) {
|
||||
hunk, ok := parser.ParseDiffHunkHeader(string(line)) // TBD: maybe reader?
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to extract lines from diff, range [%d,%d] : %w",
|
||||
startLine, endLine, ErrParseDiffHunkHeader)
|
||||
}
|
||||
line = modifyHeader(hunk, startLine, endLine)
|
||||
start = true
|
||||
}
|
||||
|
||||
if _, err := w.Write(line); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte{'\n'}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
func (a Adapter) RawDiff(
|
||||
ctx context.Context,
|
||||
w io.Writer,
|
||||
repoPath string,
|
||||
baseRef string,
|
||||
headRef string,
|
||||
mergeBase bool,
|
||||
w io.Writer,
|
||||
files ...types.FileDiffRequest,
|
||||
) error {
|
||||
if repoPath == "" {
|
||||
return ErrRepositoryPathEmpty
|
||||
|
@ -55,8 +167,78 @@ func (a Adapter) RawDiff(
|
|||
if mergeBase {
|
||||
args = append(args, "--merge-base")
|
||||
}
|
||||
args = append(args, baseRef, headRef)
|
||||
perFileDiffRequired := false
|
||||
paths := make([]string, 0, len(files))
|
||||
if len(files) > 0 {
|
||||
for _, file := range files {
|
||||
paths = append(paths, file.Path)
|
||||
if file.StartLine > 0 || file.EndLine > 0 {
|
||||
perFileDiffRequired = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processed := 0
|
||||
|
||||
again:
|
||||
startLine := 0
|
||||
endLine := 0
|
||||
newargs := make([]string, len(args), len(args)+8)
|
||||
copy(newargs, args)
|
||||
|
||||
if len(files) > 0 {
|
||||
startLine = files[processed].StartLine
|
||||
endLine = files[processed].EndLine
|
||||
}
|
||||
|
||||
if perFileDiffRequired {
|
||||
if startLine > 0 || endLine > 0 {
|
||||
newargs = append(newargs, "-U"+strconv.Itoa(math.MaxInt32))
|
||||
}
|
||||
paths = []string{files[processed].Path}
|
||||
}
|
||||
|
||||
newargs = append(newargs, baseRef, headRef)
|
||||
|
||||
if len(paths) > 0 {
|
||||
newargs = append(newargs, "--")
|
||||
newargs = append(newargs, paths...)
|
||||
}
|
||||
|
||||
pipeRead, pipeWrite := io.Pipe()
|
||||
go func() {
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
// If running of the command below fails, make the pipe reader also fail with the same error.
|
||||
_ = pipeWrite.CloseWithError(err)
|
||||
}()
|
||||
|
||||
err = a.rawDiff(ctx, pipeWrite, repoPath, baseRef, headRef, newargs...)
|
||||
}()
|
||||
|
||||
if err = cutLinesFromFullFileDiff(w, pipeRead, startLine, endLine); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if perFileDiffRequired {
|
||||
processed++
|
||||
if processed < len(files) {
|
||||
goto again
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a Adapter) rawDiff(
|
||||
ctx context.Context,
|
||||
w io.Writer,
|
||||
repoPath string,
|
||||
baseRef string,
|
||||
headRef string,
|
||||
args ...string,
|
||||
) error {
|
||||
cmd := git.NewCommand(ctx, args...)
|
||||
cmd.SetDescription(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath))
|
||||
errbuf := bytes.Buffer{}
|
||||
|
@ -68,7 +250,7 @@ func (a Adapter) RawDiff(
|
|||
if errbuf.Len() > 0 {
|
||||
err = &runStdError{err: err, stderr: errbuf.String()}
|
||||
}
|
||||
return processGiteaErrorf(err, "git diff failed between '%s' and '%s'", baseRef, headRef)
|
||||
return processGiteaErrorf(err, "git diff failed between %q and %q", baseRef, headRef)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package adapter_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAdapter_RawDiff(t *testing.T) {
|
||||
git := setupGit(t)
|
||||
repo, teardown := setupRepo(t, git, "testrawdiff")
|
||||
defer teardown()
|
||||
|
||||
testFileName := "file.txt"
|
||||
|
||||
baseBranch := "main"
|
||||
// write file to main branch
|
||||
oid1, parentSHA := writeFile(t, repo, testFileName, "some content", nil)
|
||||
|
||||
err := repo.SetReference("refs/heads/"+baseBranch, parentSHA.String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed updating reference '%s': %v", baseBranch, err)
|
||||
}
|
||||
|
||||
baseTag := "0.0.1"
|
||||
err = repo.CreateAnnotatedTag(baseTag, "test tag 1", parentSHA.String())
|
||||
if err != nil {
|
||||
t.Fatalf("error creating annotated tag '%s': %v", baseTag, err)
|
||||
}
|
||||
|
||||
headBranch := "dev"
|
||||
|
||||
// create branch
|
||||
err = repo.CreateBranch(headBranch, baseBranch)
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating a branch '%s': %v", headBranch, err)
|
||||
}
|
||||
|
||||
// write file to main branch
|
||||
oid2, sha := writeFile(t, repo, testFileName, "new content", []string{parentSHA.String()})
|
||||
|
||||
err = repo.SetReference("refs/heads/"+headBranch, sha.String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed updating reference '%s': %v", headBranch, err)
|
||||
}
|
||||
|
||||
headTag := "0.0.2"
|
||||
err = repo.CreateAnnotatedTag(headTag, "test tag 2", sha.String())
|
||||
if err != nil {
|
||||
t.Fatalf("error creating annotated tag '%s': %v", headTag, err)
|
||||
}
|
||||
|
||||
want := `diff --git a/` + testFileName + ` b/` + testFileName + `
|
||||
index ` + oid1.String() + `..` + oid2.String() + ` 100644
|
||||
--- a/` + testFileName + `
|
||||
+++ b/` + testFileName + `
|
||||
@@ -1 +1 @@
|
||||
-some content
|
||||
\ No newline at end of file
|
||||
+new content
|
||||
\ No newline at end of file
|
||||
`
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
repoPath string
|
||||
baseRef string
|
||||
headRef string
|
||||
mergeBase bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantW string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test branches",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseBranch,
|
||||
headRef: headBranch,
|
||||
mergeBase: false,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test annotated tag",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseTag,
|
||||
headRef: headTag,
|
||||
mergeBase: false,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test branches using merge-base",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseBranch,
|
||||
headRef: headBranch,
|
||||
mergeBase: true,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test annotated tag using merge-base",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseTag,
|
||||
headRef: headTag,
|
||||
mergeBase: true,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := &bytes.Buffer{}
|
||||
err := git.RawDiff(tt.args.ctx, w, tt.args.repoPath, tt.args.baseRef, tt.args.headRef, tt.args.mergeBase)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("RawDiff() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotW := w.String(); gotW != tt.wantW {
|
||||
t.Errorf("RawDiff() gotW = %v, want %v", gotW, tt.wantW)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -12,146 +12,233 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package adapter_test
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/harness/gitness/git/adapter"
|
||||
"github.com/harness/gitness/git/types"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestAdapter_RawDiff(t *testing.T) {
|
||||
git := setupGit(t)
|
||||
repo, teardown := setupRepo(t, git, "testrawdiff")
|
||||
defer teardown()
|
||||
|
||||
baseBranch := "main"
|
||||
// write file to main branch
|
||||
parentSHA := writeFile(t, repo, "file.txt", "some content", nil)
|
||||
|
||||
err := repo.SetReference("refs/heads/"+baseBranch, parentSHA.String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed updating reference '%s': %v", baseBranch, err)
|
||||
}
|
||||
|
||||
baseTag := "0.0.1"
|
||||
err = repo.CreateAnnotatedTag(baseTag, "test tag 1", parentSHA.String())
|
||||
if err != nil {
|
||||
t.Fatalf("error creating annotated tag '%s': %v", baseTag, err)
|
||||
}
|
||||
|
||||
headBranch := "dev"
|
||||
|
||||
// create branch
|
||||
err = repo.CreateBranch(headBranch, baseBranch)
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating a branch '%s': %v", headBranch, err)
|
||||
}
|
||||
|
||||
// write file to main branch
|
||||
sha := writeFile(t, repo, "file.txt", "new content", []string{parentSHA.String()})
|
||||
|
||||
err = repo.SetReference("refs/heads/"+headBranch, sha.String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed updating reference '%s': %v", headBranch, err)
|
||||
}
|
||||
|
||||
headTag := "0.0.2"
|
||||
err = repo.CreateAnnotatedTag(headTag, "test tag 2", sha.String())
|
||||
if err != nil {
|
||||
t.Fatalf("error creating annotated tag '%s': %v", headTag, err)
|
||||
}
|
||||
|
||||
want := `diff --git a/file.txt b/file.txt
|
||||
index f0eec86f614944a81f87d879ebdc9a79aea0d7ea..47d2739ba2c34690248c8f91b84bb54e8936899a 100644
|
||||
--- a/file.txt
|
||||
+++ b/file.txt
|
||||
@@ -1 +1 @@
|
||||
-some content
|
||||
\ No newline at end of file
|
||||
+new content
|
||||
\ No newline at end of file
|
||||
`
|
||||
|
||||
func Test_modifyHeader(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
repoPath string
|
||||
baseRef string
|
||||
headRef string
|
||||
mergeBase bool
|
||||
hunk types.HunkHeader
|
||||
startLine int
|
||||
endLine int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []byte
|
||||
}{
|
||||
{
|
||||
name: "test empty",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 0,
|
||||
OldSpan: 0,
|
||||
NewLine: 0,
|
||||
NewSpan: 0,
|
||||
},
|
||||
startLine: 2,
|
||||
endLine: 10,
|
||||
},
|
||||
want: []byte("@@ -0,0 +0,0 @@"),
|
||||
},
|
||||
{
|
||||
name: "test empty 1",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 0,
|
||||
OldSpan: 0,
|
||||
NewLine: 0,
|
||||
NewSpan: 0,
|
||||
},
|
||||
startLine: 0,
|
||||
endLine: 0,
|
||||
},
|
||||
want: []byte("@@ -0,0 +0,0 @@"),
|
||||
},
|
||||
{
|
||||
name: "test empty old",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 0,
|
||||
OldSpan: 0,
|
||||
NewLine: 1,
|
||||
NewSpan: 10,
|
||||
},
|
||||
startLine: 2,
|
||||
endLine: 10,
|
||||
},
|
||||
want: []byte("@@ -0,0 +2,8 @@"),
|
||||
},
|
||||
{
|
||||
name: "test empty new",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 1,
|
||||
OldSpan: 10,
|
||||
NewLine: 0,
|
||||
NewSpan: 0,
|
||||
},
|
||||
startLine: 2,
|
||||
endLine: 10,
|
||||
},
|
||||
want: []byte("@@ -2,8 +0,0 @@"),
|
||||
},
|
||||
{
|
||||
name: "test 1",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 2,
|
||||
OldSpan: 20,
|
||||
NewLine: 2,
|
||||
NewSpan: 20,
|
||||
},
|
||||
startLine: 5,
|
||||
endLine: 10,
|
||||
},
|
||||
want: []byte("@@ -5,5 +5,5 @@"),
|
||||
},
|
||||
{
|
||||
name: "test 2",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 2,
|
||||
OldSpan: 20,
|
||||
NewLine: 2,
|
||||
NewSpan: 20,
|
||||
},
|
||||
startLine: 15,
|
||||
endLine: 25,
|
||||
},
|
||||
want: []byte("@@ -15,7 +15,7 @@"),
|
||||
},
|
||||
{
|
||||
name: "test 4",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 1,
|
||||
OldSpan: 10,
|
||||
NewLine: 1,
|
||||
NewSpan: 10,
|
||||
},
|
||||
startLine: 15,
|
||||
endLine: 20,
|
||||
},
|
||||
want: []byte("@@ -1,10 +1,10 @@"),
|
||||
},
|
||||
{
|
||||
name: "test 5",
|
||||
args: args{
|
||||
hunk: types.HunkHeader{
|
||||
OldLine: 1,
|
||||
OldSpan: 108,
|
||||
NewLine: 1,
|
||||
NewSpan: 108,
|
||||
},
|
||||
startLine: 5,
|
||||
endLine: 0,
|
||||
},
|
||||
want: []byte("@@ -5,108 +5,108 @@"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := modifyHeader(tt.args.hunk, tt.args.startLine, tt.args.endLine); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("modifyHeader() = %s, want %s", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cutLinesFromFullDiff(t *testing.T) {
|
||||
type args struct {
|
||||
r io.Reader
|
||||
startLine int
|
||||
endLine int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
adapter adapter.Adapter
|
||||
args args
|
||||
wantW string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test branches",
|
||||
adapter: git,
|
||||
name: "test empty",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseBranch,
|
||||
headRef: headBranch,
|
||||
mergeBase: false,
|
||||
r: strings.NewReader(`diff --git a/file.txt b/file.txt
|
||||
index f0eec86f614944a81f87d879ebdc9a79aea0d7ea..47d2739ba2c34690248c8f91b84bb54e8936899a 100644
|
||||
--- a/file.txt
|
||||
+++ b/file.txt
|
||||
@@ -0,0 +0,0 @@
|
||||
`),
|
||||
startLine: 2,
|
||||
endLine: 10,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
wantW: `diff --git a/file.txt b/file.txt
|
||||
index f0eec86f614944a81f87d879ebdc9a79aea0d7ea..47d2739ba2c34690248c8f91b84bb54e8936899a 100644
|
||||
--- a/file.txt
|
||||
+++ b/file.txt
|
||||
@@ -0,0 +0,0 @@
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "test annotated tag",
|
||||
adapter: git,
|
||||
name: "test 1",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseTag,
|
||||
headRef: headTag,
|
||||
mergeBase: false,
|
||||
r: strings.NewReader(`diff --git a/file.txt b/file.txt
|
||||
index f0eec86f614944a81f87d879ebdc9a79aea0d7ea..47d2739ba2c34690248c8f91b84bb54e8936899a 100644
|
||||
--- a/file.txt
|
||||
+++ b/file.txt
|
||||
@@ -1,9 +1,9 @@
|
||||
some content
|
||||
some content
|
||||
some content
|
||||
-some content
|
||||
+some content
|
||||
some content
|
||||
some content
|
||||
some content
|
||||
some content
|
||||
some content
|
||||
`),
|
||||
startLine: 2,
|
||||
endLine: 10,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test branches using merge-base",
|
||||
adapter: git,
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseBranch,
|
||||
headRef: headBranch,
|
||||
mergeBase: true,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test annotated tag using merge-base",
|
||||
adapter: git,
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
repoPath: repo.Path,
|
||||
baseRef: baseTag,
|
||||
headRef: headTag,
|
||||
mergeBase: true,
|
||||
},
|
||||
wantW: want,
|
||||
wantErr: false,
|
||||
wantW: `diff --git a/file.txt b/file.txt
|
||||
index f0eec86f614944a81f87d879ebdc9a79aea0d7ea..47d2739ba2c34690248c8f91b84bb54e8936899a 100644
|
||||
--- a/file.txt
|
||||
+++ b/file.txt
|
||||
@@ -2,8 +2,8 @@
|
||||
some content
|
||||
some content
|
||||
-some content
|
||||
+some content
|
||||
some content
|
||||
some content
|
||||
some content
|
||||
some content
|
||||
some content
|
||||
`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := &bytes.Buffer{}
|
||||
err := tt.adapter.RawDiff(tt.args.ctx, tt.args.repoPath, tt.args.baseRef, tt.args.headRef, tt.args.mergeBase, w)
|
||||
err := cutLinesFromFullFileDiff(w, tt.args.r, tt.args.startLine, tt.args.endLine)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("RawDiff() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("cutLinesFromFullFileDiff() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotW := w.String(); gotW != tt.wantW {
|
||||
t.Errorf("RawDiff() gotW = %v, want %v", gotW, tt.wantW)
|
||||
t.Errorf("cutLinesFromFullFileDiff() gotW = %v, want %v, diff: %s", gotW, tt.wantW, cmp.Diff(gotW, tt.wantW))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
var (
|
||||
ErrRepositoryPathEmpty = errors.InvalidArgument("repository path cannot be empty")
|
||||
ErrBranchNameEmpty = errors.InvalidArgument("branch name cannot be empty")
|
||||
ErrParseDiffHunkHeader = errors.Internal("failed to parse diff hunk header")
|
||||
)
|
||||
|
||||
type runStdError struct {
|
||||
|
|
|
@ -28,7 +28,7 @@ func TestAdapter_GetMergeBase(t *testing.T) {
|
|||
|
||||
baseBranch := "main"
|
||||
// write file to main branch
|
||||
parentSHA := writeFile(t, repo, "file1.txt", "some content", nil)
|
||||
_, parentSHA := writeFile(t, repo, "file1.txt", "some content", nil)
|
||||
|
||||
err := repo.SetReference("refs/heads/"+baseBranch, parentSHA.String())
|
||||
if err != nil {
|
||||
|
@ -50,7 +50,7 @@ func TestAdapter_GetMergeBase(t *testing.T) {
|
|||
}
|
||||
|
||||
// write file to main branch
|
||||
sha := writeFile(t, repo, "file1.txt", "new content", []string{parentSHA.String()})
|
||||
_, sha := writeFile(t, repo, "file1.txt", "new content", []string{parentSHA.String()})
|
||||
|
||||
err = repo.SetReference("refs/heads/"+headBranch, sha.String())
|
||||
if err != nil {
|
||||
|
|
|
@ -108,14 +108,14 @@ func writeFile(
|
|||
path string,
|
||||
content string,
|
||||
parents []string,
|
||||
) gitea.SHA1 {
|
||||
) (oid gitea.SHA1, commitSha gitea.SHA1) {
|
||||
t.Helper()
|
||||
sha, err := repo.HashObject(strings.NewReader(content))
|
||||
oid, err := repo.HashObject(strings.NewReader(content))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hash object: %v", err)
|
||||
}
|
||||
|
||||
err = repo.AddObjectToIndex("100644", sha, path)
|
||||
err = repo.AddObjectToIndex("100644", oid, path)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add object to index: %v", err)
|
||||
}
|
||||
|
@ -125,12 +125,12 @@ func writeFile(
|
|||
t.Fatalf("failed to write tree: %v", err)
|
||||
}
|
||||
|
||||
sha, err = repo.CommitTree(testAuthor, testCommitter, tree, gitea.CommitTreeOpts{
|
||||
commitSha, err = repo.CommitTree(testAuthor, testCommitter, tree, gitea.CommitTreeOpts{
|
||||
Message: "write file operation",
|
||||
Parents: parents,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit tree: %v", err)
|
||||
}
|
||||
return sha
|
||||
return oid, commitSha
|
||||
}
|
||||
|
|
16
git/diff.go
16
git/diff.go
|
@ -48,18 +48,23 @@ func (p DiffParams) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) RawDiff(ctx context.Context, params *DiffParams, out io.Writer) error {
|
||||
return s.rawDiff(ctx, params, out)
|
||||
func (s *Service) RawDiff(
|
||||
ctx context.Context,
|
||||
out io.Writer,
|
||||
params *DiffParams,
|
||||
files ...types.FileDiffRequest,
|
||||
) error {
|
||||
return s.rawDiff(ctx, out, params, files...)
|
||||
}
|
||||
|
||||
func (s *Service) rawDiff(ctx context.Context, params *DiffParams, w io.Writer) error {
|
||||
func (s *Service) rawDiff(ctx context.Context, w io.Writer, params *DiffParams, files ...types.FileDiffRequest) error {
|
||||
if err := params.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
|
||||
|
||||
err := s.adapter.RawDiff(ctx, repoPath, params.BaseRef, params.HeadRef, params.MergeBase, w)
|
||||
err := s.adapter.RawDiff(ctx, w, repoPath, params.BaseRef, params.HeadRef, params.MergeBase, files...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -336,6 +341,7 @@ func parseFileDiffStatus(ftype diff.FileType) FileDiffStatus {
|
|||
func (s *Service) Diff(
|
||||
ctx context.Context,
|
||||
params *DiffParams,
|
||||
files ...types.FileDiffRequest,
|
||||
) (<-chan *FileDiff, <-chan error) {
|
||||
wg := sync.WaitGroup{}
|
||||
ch := make(chan *FileDiff)
|
||||
|
@ -353,7 +359,7 @@ func (s *Service) Diff(
|
|||
return
|
||||
}
|
||||
|
||||
err := s.rawDiff(ctx, params, pw)
|
||||
err := s.rawDiff(ctx, pw, params, files...)
|
||||
if err != nil {
|
||||
cherr <- err
|
||||
return
|
||||
|
|
|
@ -17,6 +17,8 @@ package git
|
|||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/harness/gitness/git/types"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
|
@ -66,8 +68,8 @@ type Interface interface {
|
|||
/*
|
||||
* Diff services
|
||||
*/
|
||||
RawDiff(ctx context.Context, in *DiffParams, w io.Writer) error
|
||||
Diff(ctx context.Context, in *DiffParams) (<-chan *FileDiff, <-chan error)
|
||||
RawDiff(ctx context.Context, w io.Writer, in *DiffParams, files ...types.FileDiffRequest) error
|
||||
Diff(ctx context.Context, in *DiffParams, files ...types.FileDiffRequest) (<-chan *FileDiff, <-chan error)
|
||||
DiffFileNames(ctx context.Context, in *DiffParams) (DiffFileNamesOutput, error)
|
||||
CommitDiff(ctx context.Context, params *GetCommitParams, w io.Writer) error
|
||||
DiffShortStat(ctx context.Context, params *DiffParams) (DiffShortStatOutput, error)
|
||||
|
|
|
@ -383,3 +383,11 @@ type ObjectCount struct {
|
|||
Garbage int
|
||||
SizeGarbage int64
|
||||
}
|
||||
|
||||
type FileDiffRequest struct {
|
||||
Path string `json:"path"`
|
||||
StartLine int `json:"start_line"`
|
||||
EndLine int `json:"-"` // warning: changes are possible and this field may not exist in the future
|
||||
}
|
||||
|
||||
type FileDiffRequests []FileDiffRequest
|
||||
|
|
Loading…
Reference in New Issue