mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-22 18:00:37 +01:00
208 lines
4.3 KiB
Go
208 lines
4.3 KiB
Go
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a MIT-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package models
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"io"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/gogits/git"
|
||
|
|
||
|
"github.com/gogits/gogs/modules/base"
|
||
|
"github.com/gogits/gogs/modules/log"
|
||
|
)
|
||
|
|
||
|
// Diff line types.
|
||
|
const (
|
||
|
DIFF_LINE_PLAIN = iota + 1
|
||
|
DIFF_LINE_ADD
|
||
|
DIFF_LINE_DEL
|
||
|
DIFF_LINE_SECTION
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
DIFF_FILE_ADD = iota + 1
|
||
|
DIFF_FILE_CHANGE
|
||
|
DIFF_FILE_DEL
|
||
|
)
|
||
|
|
||
|
type DiffLine struct {
|
||
|
LeftIdx int
|
||
|
RightIdx int
|
||
|
Type int
|
||
|
Content string
|
||
|
}
|
||
|
|
||
|
func (d DiffLine) GetType() int {
|
||
|
return d.Type
|
||
|
}
|
||
|
|
||
|
type DiffSection struct {
|
||
|
Name string
|
||
|
Lines []*DiffLine
|
||
|
}
|
||
|
|
||
|
type DiffFile struct {
|
||
|
Name string
|
||
|
Addition, Deletion int
|
||
|
Type int
|
||
|
Sections []*DiffSection
|
||
|
}
|
||
|
|
||
|
type Diff struct {
|
||
|
TotalAddition, TotalDeletion int
|
||
|
Files []*DiffFile
|
||
|
}
|
||
|
|
||
|
func (diff *Diff) NumFiles() int {
|
||
|
return len(diff.Files)
|
||
|
}
|
||
|
|
||
|
const DIFF_HEAD = "diff --git "
|
||
|
|
||
|
func ParsePatch(reader io.Reader) (*Diff, error) {
|
||
|
scanner := bufio.NewScanner(reader)
|
||
|
var (
|
||
|
curFile *DiffFile
|
||
|
curSection = &DiffSection{
|
||
|
Lines: make([]*DiffLine, 0, 10),
|
||
|
}
|
||
|
|
||
|
leftLine, rightLine int
|
||
|
)
|
||
|
|
||
|
diff := &Diff{Files: make([]*DiffFile, 0)}
|
||
|
var i int
|
||
|
for scanner.Scan() {
|
||
|
line := scanner.Text()
|
||
|
// fmt.Println(i, line)
|
||
|
if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
i = i + 1
|
||
|
|
||
|
// Diff data too large.
|
||
|
if i == 5000 {
|
||
|
log.Warn("Diff data too large")
|
||
|
return &Diff{}, nil
|
||
|
}
|
||
|
|
||
|
if line == "" {
|
||
|
continue
|
||
|
}
|
||
|
if line[0] == ' ' {
|
||
|
diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
||
|
leftLine++
|
||
|
rightLine++
|
||
|
curSection.Lines = append(curSection.Lines, diffLine)
|
||
|
continue
|
||
|
} else if line[0] == '@' {
|
||
|
curSection = &DiffSection{}
|
||
|
curFile.Sections = append(curFile.Sections, curSection)
|
||
|
ss := strings.Split(line, "@@")
|
||
|
diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line}
|
||
|
curSection.Lines = append(curSection.Lines, diffLine)
|
||
|
|
||
|
// Parse line number.
|
||
|
ranges := strings.Split(ss[len(ss)-2][1:], " ")
|
||
|
leftLine, _ = base.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int()
|
||
|
rightLine, _ = base.StrTo(strings.Split(ranges[1], ",")[0]).Int()
|
||
|
continue
|
||
|
} else if line[0] == '+' {
|
||
|
curFile.Addition++
|
||
|
diff.TotalAddition++
|
||
|
diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
|
||
|
rightLine++
|
||
|
curSection.Lines = append(curSection.Lines, diffLine)
|
||
|
continue
|
||
|
} else if line[0] == '-' {
|
||
|
curFile.Deletion++
|
||
|
diff.TotalDeletion++
|
||
|
diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine}
|
||
|
if leftLine > 0 {
|
||
|
leftLine++
|
||
|
}
|
||
|
curSection.Lines = append(curSection.Lines, diffLine)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Get new file.
|
||
|
if strings.HasPrefix(line, DIFF_HEAD) {
|
||
|
fs := strings.Split(line[len(DIFF_HEAD):], " ")
|
||
|
a := fs[0]
|
||
|
|
||
|
curFile = &DiffFile{
|
||
|
Name: a[strings.Index(a, "/")+1:],
|
||
|
Type: DIFF_FILE_CHANGE,
|
||
|
Sections: make([]*DiffSection, 0, 10),
|
||
|
}
|
||
|
diff.Files = append(diff.Files, curFile)
|
||
|
|
||
|
// Check file diff type.
|
||
|
for scanner.Scan() {
|
||
|
switch {
|
||
|
case strings.HasPrefix(scanner.Text(), "new file"):
|
||
|
curFile.Type = DIFF_FILE_ADD
|
||
|
case strings.HasPrefix(scanner.Text(), "deleted"):
|
||
|
curFile.Type = DIFF_FILE_DEL
|
||
|
case strings.HasPrefix(scanner.Text(), "index"):
|
||
|
curFile.Type = DIFF_FILE_CHANGE
|
||
|
}
|
||
|
if curFile.Type > 0 {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return diff, nil
|
||
|
}
|
||
|
|
||
|
func GetDiff(repoPath, commitid string) (*Diff, error) {
|
||
|
repo, err := git.OpenRepository(repoPath)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
commit, err := repo.GetCommit(commitid)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// First commit of repository.
|
||
|
if commit.ParentCount() == 0 {
|
||
|
rd, wr := io.Pipe()
|
||
|
go func() {
|
||
|
cmd := exec.Command("git", "show", commitid)
|
||
|
cmd.Dir = repoPath
|
||
|
cmd.Stdout = wr
|
||
|
cmd.Stdin = os.Stdin
|
||
|
cmd.Stderr = os.Stderr
|
||
|
cmd.Run()
|
||
|
wr.Close()
|
||
|
}()
|
||
|
defer rd.Close()
|
||
|
return ParsePatch(rd)
|
||
|
}
|
||
|
|
||
|
rd, wr := io.Pipe()
|
||
|
go func() {
|
||
|
c, _ := commit.Parent(0)
|
||
|
cmd := exec.Command("git", "diff", c.Id.String(), commitid)
|
||
|
cmd.Dir = repoPath
|
||
|
cmd.Stdout = wr
|
||
|
cmd.Stdin = os.Stdin
|
||
|
cmd.Stderr = os.Stderr
|
||
|
cmd.Run()
|
||
|
wr.Close()
|
||
|
}()
|
||
|
defer rd.Close()
|
||
|
return ParsePatch(rd)
|
||
|
}
|