Skip to content

Commit fd43e72

Browse files
committed
Create a page for comparing patchsets using 'git range-diff'. Fixes #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.
1 parent f088317 commit fd43e72

31 files changed

+2253
-469
lines changed

models/commit_status.go

+25-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"time"
1313

14+
"code.gitea.io/gitea/modules/git"
1415
"code.gitea.io/gitea/modules/log"
1516
"code.gitea.io/gitea/modules/setting"
1617
api "code.gitea.io/gitea/modules/structs"
@@ -249,6 +250,29 @@ type SignCommitWithStatuses struct {
249250
*SignCommit
250251
}
251252

253+
// ParseGitCommitWithStatus parse a simple git.Commit directly into SignCommitWithStatuses
254+
func ParseGitCommitWithStatus(commit *git.Commit, repo *Repository, emailCache *map[string]*User, keyMapCache *map[string]bool) SignCommitWithStatuses {
255+
withEmail := ValidateCommitWithEmails(commit, emailCache)
256+
withSignature := ParseUserCommitWithSignature(withEmail, repo, keyMapCache)
257+
258+
return ParseCommitWithStatus(withSignature, repo)
259+
}
260+
261+
// ParseCommitWithStatus checks the commit's latest status and calculates its worst status state
262+
func ParseCommitWithStatus(commit SignCommit, repo *Repository) SignCommitWithStatuses {
263+
signCommitWithStatus := SignCommitWithStatuses{
264+
SignCommit: &commit,
265+
}
266+
statuses, err := GetLatestCommitStatus(repo, signCommitWithStatus.ID.String(), 0)
267+
if err != nil {
268+
log.Error("GetLatestCommitStatus: %v", err)
269+
} else {
270+
signCommitWithStatus.Status = CalcCommitStatus(statuses)
271+
}
272+
273+
return signCommitWithStatus
274+
}
275+
252276
// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state
253277
func ParseCommitsWithStatus(oldCommits *list.List, repo *Repository) *list.List {
254278
var (
@@ -258,17 +282,8 @@ func ParseCommitsWithStatus(oldCommits *list.List, repo *Repository) *list.List
258282

259283
for e != nil {
260284
c := e.Value.(SignCommit)
261-
commit := SignCommitWithStatuses{
262-
SignCommit: &c,
263-
}
264-
statuses, err := GetLatestCommitStatus(repo, commit.ID.String(), 0)
265-
if err != nil {
266-
log.Error("GetLatestCommitStatus: %v", err)
267-
} else {
268-
commit.Status = CalcCommitStatus(statuses)
269-
}
270285

271-
newCommits.PushBack(commit)
286+
newCommits.PushBack(ParseCommitWithStatus(c, repo))
272287
e = e.Next()
273288
}
274289
return newCommits

models/gpg_key.go

+18-8
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,23 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature,
825825
return nil
826826
}
827827

828-
// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
828+
// ParseUserCommitWithSignature checks if signature of a commit is corresponding to users gpg keys.
829+
func ParseUserCommitWithSignature(commit UserCommit, repository *Repository, keyMapCache *map[string]bool) SignCommit {
830+
if keyMapCache == nil {
831+
keyMapCache = &map[string]bool{}
832+
}
833+
834+
signCommit := SignCommit{
835+
UserCommit: &commit,
836+
Verification: ParseCommitWithSignature(commit.Commit),
837+
}
838+
839+
_ = CalculateTrustStatus(signCommit.Verification, repository, keyMapCache)
840+
841+
return signCommit
842+
}
843+
844+
// ParseCommitsWithSignature checks if signature of commits are corresponding to users gpg keys.
829845
func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *list.List {
830846
var (
831847
newCommits = list.New()
@@ -835,14 +851,8 @@ func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *l
835851

836852
for e != nil {
837853
c := e.Value.(UserCommit)
838-
signCommit := SignCommit{
839-
UserCommit: &c,
840-
Verification: ParseCommitWithSignature(c.Commit),
841-
}
842-
843-
_ = CalculateTrustStatus(signCommit.Verification, repository, &keyMap)
844854

845-
newCommits.PushBack(signCommit)
855+
newCommits.PushBack(ParseUserCommitWithSignature(c, repository, &keyMap))
846856
e = e.Next()
847857
}
848858
return newCommits

models/user.go

+26-16
Original file line numberDiff line numberDiff line change
@@ -1325,32 +1325,42 @@ func ValidateCommitWithEmail(c *git.Commit) *User {
13251325
return u
13261326
}
13271327

1328+
// ValidateCommitWithEmails checks if authors' e-mails of commits are corresponding to users.
1329+
func ValidateCommitWithEmails(commit *git.Commit, emailCache *map[string]*User) UserCommit {
1330+
var u *User
1331+
1332+
if emailCache == nil {
1333+
emailCache = &map[string]*User{}
1334+
}
1335+
1336+
if commit.Author != nil {
1337+
if v, ok := (*emailCache)[commit.Author.Email]; !ok {
1338+
u, _ = GetUserByEmail(commit.Author.Email)
1339+
(*emailCache)[commit.Author.Email] = u
1340+
} else {
1341+
u = v
1342+
}
1343+
} else {
1344+
u = nil
1345+
}
1346+
1347+
return UserCommit{
1348+
User: u,
1349+
Commit: commit,
1350+
}
1351+
}
1352+
13281353
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
13291354
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
13301355
var (
1331-
u *User
13321356
emails = map[string]*User{}
13331357
newCommits = list.New()
13341358
e = oldCommits.Front()
13351359
)
13361360
for e != nil {
13371361
c := e.Value.(*git.Commit)
13381362

1339-
if c.Author != nil {
1340-
if v, ok := emails[c.Author.Email]; !ok {
1341-
u, _ = GetUserByEmail(c.Author.Email)
1342-
emails[c.Author.Email] = u
1343-
} else {
1344-
u = v
1345-
}
1346-
} else {
1347-
u = nil
1348-
}
1349-
1350-
newCommits.PushBack(UserCommit{
1351-
User: u,
1352-
Commit: c,
1353-
})
1363+
newCommits.PushBack(ValidateCommitWithEmails(c, &emails))
13541364
e = e.Next()
13551365
}
13561366
return newCommits

modules/git/commit.go

+43-3
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
467467
return nil, nil
468468
}
469469

470-
func (c *Commit) nameRev(prefix string, excludeTags, excludeHeads, excludePullRefs, excludePullHeads bool) (string, error) {
470+
func (c *Commit) nameRev(prefix string, excludeTags, excludeHeads, excludePullRefs, excludePullHeads bool, excludes ...string) (string, error) {
471471
err := LoadGitVersion()
472472
if err != nil {
473473
return "", fmt.Errorf("Git version missing: %v", err)
@@ -495,6 +495,10 @@ func (c *Commit) nameRev(prefix string, excludeTags, excludeHeads, excludePullRe
495495
args = append(args, "--exclude", "refs/pull/*/head")
496496
}
497497
args = append(args, "--exclude", "refs/pull/*/revision/latest")
498+
499+
for _, exclude := range excludes {
500+
args = append(args, "--exclude", exclude)
501+
}
498502
}
499503

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

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

520524
if err != nil {
521525
return "", err
@@ -524,6 +528,42 @@ func (c *Commit) GetBranchName() (string, error) {
524528
return name, nil
525529
}
526530

531+
// GetRevisionRef gets the git revision ref (as returned by 'git name-rev --name-only')
532+
func (c *Commit) GetRevisionRef(exclude ...string) (string, error) {
533+
name, err := c.nameRev("refs/pull/*/revision/*", true, true, false, true, exclude...)
534+
535+
if err != nil {
536+
return "", err
537+
}
538+
539+
if name == "" {
540+
return "", nil
541+
}
542+
543+
return "refs/" + name, nil
544+
}
545+
546+
// GetRevisionRefs gets the git revision refs (as returned by 'git name-rev --name-only')
547+
func (c *Commit) GetRevisionRefs() ([]string, error) {
548+
var refs []string
549+
550+
for {
551+
name, err := c.GetRevisionRef(refs...)
552+
553+
if err != nil {
554+
return nil, err
555+
}
556+
557+
if name == "" {
558+
break
559+
}
560+
561+
refs = append(refs, name)
562+
}
563+
564+
return refs, nil
565+
}
566+
527567
// LoadBranchName load branch name for commit
528568
func (c *Commit) LoadBranchName() (err error) {
529569
if len(c.Branch) != 0 {

modules/git/git.go

+19
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ var (
3131
// Could be updated to an absolute path while initialization
3232
GitExecutable = "git"
3333

34+
// Interdiff is the command to diff diffs.
35+
// Could be updated to an absolute path while initialization
36+
Interdiff = "interdiff"
37+
3438
// DefaultContext is the default context to run git commands in
3539
DefaultContext = context.Background()
3640

@@ -121,6 +125,21 @@ func SetExecutablePath(path string) error {
121125
return nil
122126
}
123127

128+
// SetInterdiffPath changes the path of the interdiff executable.
129+
func SetInterdiffPath(path string) error {
130+
// If path is empty, we use the default value of Interdiff "interdiff" to search for the location of interfdiff.
131+
if path != "" {
132+
Interdiff = path
133+
}
134+
absPath, err := exec.LookPath(Interdiff)
135+
if err != nil {
136+
return fmt.Errorf("Interdiff not found: %v", err)
137+
}
138+
Interdiff = absPath
139+
140+
return nil
141+
}
142+
124143
// Init initializes git module
125144
func Init(ctx context.Context) error {
126145
DefaultContext = ctx

modules/git/interdiff.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package git
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"io"
11+
"io/ioutil"
12+
"os"
13+
"os/exec"
14+
"strings"
15+
16+
"code.gitea.io/gitea/modules/process"
17+
)
18+
19+
// GetInterdiff get the difference of two diffs.
20+
func GetInterdiff(diff1, diff2 string) (string, error) {
21+
f1 := "/dev/null"
22+
f2 := "/dev/null"
23+
24+
if diff1 != "" {
25+
f, err := ioutil.TempFile("", "d1")
26+
if err != nil {
27+
return "", err
28+
}
29+
f1 = f.Name()
30+
defer os.Remove(f1)
31+
32+
_, err = f.WriteString(diff1)
33+
34+
if err != nil {
35+
return "", err
36+
}
37+
}
38+
39+
if diff2 != "" {
40+
f, err := ioutil.TempFile("", "d2")
41+
if err != nil {
42+
return "", err
43+
}
44+
45+
_, err = f.WriteString(diff2)
46+
47+
if err != nil {
48+
return "", err
49+
}
50+
51+
f2 = f.Name()
52+
defer os.Remove(f2)
53+
}
54+
55+
ctx, cancel := context.WithCancel(DefaultContext)
56+
defer cancel()
57+
var cmd = exec.CommandContext(ctx, Interdiff, "-q", f1, f2)
58+
cmd.Stderr = os.Stderr
59+
60+
stdout, err := cmd.StdoutPipe()
61+
if err != nil {
62+
return "", fmt.Errorf("StdoutPipe: %v", err)
63+
}
64+
65+
if err = cmd.Start(); err != nil {
66+
return "", fmt.Errorf("Start: %v", err)
67+
}
68+
69+
pid := process.GetManager().Add("GetInterdiff ", cancel)
70+
defer process.GetManager().Remove(pid)
71+
72+
buf := new(strings.Builder)
73+
_, err = io.Copy(buf, stdout)
74+
if err != nil {
75+
return "", err
76+
}
77+
78+
if err = cmd.Wait(); err != nil {
79+
return "", fmt.Errorf("Wait: %v", err)
80+
}
81+
82+
return buf.String(), nil
83+
}

0 commit comments

Comments
 (0)