Skip to content

Commit

Permalink
Create a page for comparing patchsets using 'git range-diff'. Fixes g…
Browse files Browse the repository at this point in the history
…o-gitea#12800

Also add warnings if a '/compare/base...head' is y shaped and offer to
show the raw difference (where the inverse of new commits on base are
included in the diff) and also offer to compare the y shaped junction
with range-diff.

The rangediff page uses the interdiff tool to work out the changes
between corresponding commits.
  • Loading branch information
05storm26 committed Oct 26, 2020
1 parent f088317 commit fd43e72
Show file tree
Hide file tree
Showing 31 changed files with 2,253 additions and 469 deletions.
35 changes: 25 additions & 10 deletions models/commit_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
Expand Down Expand Up @@ -249,6 +250,29 @@ type SignCommitWithStatuses struct {
*SignCommit
}

// ParseGitCommitWithStatus parse a simple git.Commit directly into SignCommitWithStatuses
func ParseGitCommitWithStatus(commit *git.Commit, repo *Repository, emailCache *map[string]*User, keyMapCache *map[string]bool) SignCommitWithStatuses {
withEmail := ValidateCommitWithEmails(commit, emailCache)
withSignature := ParseUserCommitWithSignature(withEmail, repo, keyMapCache)

return ParseCommitWithStatus(withSignature, repo)
}

// ParseCommitWithStatus checks the commit's latest status and calculates its worst status state
func ParseCommitWithStatus(commit SignCommit, repo *Repository) SignCommitWithStatuses {
signCommitWithStatus := SignCommitWithStatuses{
SignCommit: &commit,
}
statuses, err := GetLatestCommitStatus(repo, signCommitWithStatus.ID.String(), 0)
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
} else {
signCommitWithStatus.Status = CalcCommitStatus(statuses)
}

return signCommitWithStatus
}

// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state
func ParseCommitsWithStatus(oldCommits *list.List, repo *Repository) *list.List {
var (
Expand All @@ -258,17 +282,8 @@ func ParseCommitsWithStatus(oldCommits *list.List, repo *Repository) *list.List

for e != nil {
c := e.Value.(SignCommit)
commit := SignCommitWithStatuses{
SignCommit: &c,
}
statuses, err := GetLatestCommitStatus(repo, commit.ID.String(), 0)
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
} else {
commit.Status = CalcCommitStatus(statuses)
}

newCommits.PushBack(commit)
newCommits.PushBack(ParseCommitWithStatus(c, repo))
e = e.Next()
}
return newCommits
Expand Down
26 changes: 18 additions & 8 deletions models/gpg_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,23 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature,
return nil
}

// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
// ParseUserCommitWithSignature checks if signature of a commit is corresponding to users gpg keys.
func ParseUserCommitWithSignature(commit UserCommit, repository *Repository, keyMapCache *map[string]bool) SignCommit {
if keyMapCache == nil {
keyMapCache = &map[string]bool{}
}

signCommit := SignCommit{
UserCommit: &commit,
Verification: ParseCommitWithSignature(commit.Commit),
}

_ = CalculateTrustStatus(signCommit.Verification, repository, keyMapCache)

return signCommit
}

// ParseCommitsWithSignature checks if signature of commits are corresponding to users gpg keys.
func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *list.List {
var (
newCommits = list.New()
Expand All @@ -835,14 +851,8 @@ func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *l

for e != nil {
c := e.Value.(UserCommit)
signCommit := SignCommit{
UserCommit: &c,
Verification: ParseCommitWithSignature(c.Commit),
}

_ = CalculateTrustStatus(signCommit.Verification, repository, &keyMap)

newCommits.PushBack(signCommit)
newCommits.PushBack(ParseUserCommitWithSignature(c, repository, &keyMap))
e = e.Next()
}
return newCommits
Expand Down
42 changes: 26 additions & 16 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -1325,32 +1325,42 @@ func ValidateCommitWithEmail(c *git.Commit) *User {
return u
}

// ValidateCommitWithEmails checks if authors' e-mails of commits are corresponding to users.
func ValidateCommitWithEmails(commit *git.Commit, emailCache *map[string]*User) UserCommit {
var u *User

if emailCache == nil {
emailCache = &map[string]*User{}
}

if commit.Author != nil {
if v, ok := (*emailCache)[commit.Author.Email]; !ok {
u, _ = GetUserByEmail(commit.Author.Email)
(*emailCache)[commit.Author.Email] = u
} else {
u = v
}
} else {
u = nil
}

return UserCommit{
User: u,
Commit: commit,
}
}

// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
var (
u *User
emails = map[string]*User{}
newCommits = list.New()
e = oldCommits.Front()
)
for e != nil {
c := e.Value.(*git.Commit)

if c.Author != nil {
if v, ok := emails[c.Author.Email]; !ok {
u, _ = GetUserByEmail(c.Author.Email)
emails[c.Author.Email] = u
} else {
u = v
}
} else {
u = nil
}

newCommits.PushBack(UserCommit{
User: u,
Commit: c,
})
newCommits.PushBack(ValidateCommitWithEmails(c, &emails))
e = e.Next()
}
return newCommits
Expand Down
46 changes: 43 additions & 3 deletions modules/git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
return nil, nil
}

func (c *Commit) nameRev(prefix string, excludeTags, excludeHeads, excludePullRefs, excludePullHeads bool) (string, error) {
func (c *Commit) nameRev(prefix string, excludeTags, excludeHeads, excludePullRefs, excludePullHeads bool, excludes ...string) (string, error) {
err := LoadGitVersion()
if err != nil {
return "", fmt.Errorf("Git version missing: %v", err)
Expand Down Expand Up @@ -495,6 +495,10 @@ func (c *Commit) nameRev(prefix string, excludeTags, excludeHeads, excludePullRe
args = append(args, "--exclude", "refs/pull/*/head")
}
args = append(args, "--exclude", "refs/pull/*/revision/latest")

for _, exclude := range excludes {
args = append(args, "--exclude", exclude)
}
}

args = append(args, "--name-only", "--no-undefined", c.ID.String())
Expand All @@ -514,8 +518,8 @@ func (c *Commit) nameRev(prefix string, excludeTags, excludeHeads, excludePullRe
}

// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) {
name, err := c.nameRev("refs/heads/*", true, false, true, true)
func (c *Commit) GetBranchName(exclude ...string) (string, error) {
name, err := c.nameRev("refs/heads/*", true, false, true, true, exclude...)

if err != nil {
return "", err
Expand All @@ -524,6 +528,42 @@ func (c *Commit) GetBranchName() (string, error) {
return name, nil
}

// GetRevisionRef gets the git revision ref (as returned by 'git name-rev --name-only')
func (c *Commit) GetRevisionRef(exclude ...string) (string, error) {
name, err := c.nameRev("refs/pull/*/revision/*", true, true, false, true, exclude...)

if err != nil {
return "", err
}

if name == "" {
return "", nil
}

return "refs/" + name, nil
}

// GetRevisionRefs gets the git revision refs (as returned by 'git name-rev --name-only')
func (c *Commit) GetRevisionRefs() ([]string, error) {
var refs []string

for {
name, err := c.GetRevisionRef(refs...)

if err != nil {
return nil, err
}

if name == "" {
break
}

refs = append(refs, name)
}

return refs, nil
}

// LoadBranchName load branch name for commit
func (c *Commit) LoadBranchName() (err error) {
if len(c.Branch) != 0 {
Expand Down
19 changes: 19 additions & 0 deletions modules/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ var (
// Could be updated to an absolute path while initialization
GitExecutable = "git"

// Interdiff is the command to diff diffs.
// Could be updated to an absolute path while initialization
Interdiff = "interdiff"

// DefaultContext is the default context to run git commands in
DefaultContext = context.Background()

Expand Down Expand Up @@ -121,6 +125,21 @@ func SetExecutablePath(path string) error {
return nil
}

// SetInterdiffPath changes the path of the interdiff executable.
func SetInterdiffPath(path string) error {
// If path is empty, we use the default value of Interdiff "interdiff" to search for the location of interfdiff.
if path != "" {
Interdiff = path
}
absPath, err := exec.LookPath(Interdiff)
if err != nil {
return fmt.Errorf("Interdiff not found: %v", err)
}
Interdiff = absPath

return nil
}

// Init initializes git module
func Init(ctx context.Context) error {
DefaultContext = ctx
Expand Down
83 changes: 83 additions & 0 deletions modules/git/interdiff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2020 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 git

import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"

"code.gitea.io/gitea/modules/process"
)

// GetInterdiff get the difference of two diffs.
func GetInterdiff(diff1, diff2 string) (string, error) {
f1 := "/dev/null"
f2 := "/dev/null"

if diff1 != "" {
f, err := ioutil.TempFile("", "d1")
if err != nil {
return "", err
}
f1 = f.Name()
defer os.Remove(f1)

_, err = f.WriteString(diff1)

if err != nil {
return "", err
}
}

if diff2 != "" {
f, err := ioutil.TempFile("", "d2")
if err != nil {
return "", err
}

_, err = f.WriteString(diff2)

if err != nil {
return "", err
}

f2 = f.Name()
defer os.Remove(f2)
}

ctx, cancel := context.WithCancel(DefaultContext)
defer cancel()
var cmd = exec.CommandContext(ctx, Interdiff, "-q", f1, f2)
cmd.Stderr = os.Stderr

stdout, err := cmd.StdoutPipe()
if err != nil {
return "", fmt.Errorf("StdoutPipe: %v", err)
}

if err = cmd.Start(); err != nil {
return "", fmt.Errorf("Start: %v", err)
}

pid := process.GetManager().Add("GetInterdiff ", cancel)
defer process.GetManager().Remove(pid)

buf := new(strings.Builder)
_, err = io.Copy(buf, stdout)
if err != nil {
return "", err
}

if err = cmd.Wait(); err != nil {
return "", fmt.Errorf("Wait: %v", err)
}

return buf.String(), nil
}
Loading

0 comments on commit fd43e72

Please sign in to comment.