|
|
- // 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
- Index int
- Addition, Deletion int
- Type int
- IsBin bool
- 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
- }
-
- switch {
- case line[0] == ' ':
- diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
- leftLine++
- rightLine++
- curSection.Lines = append(curSection.Lines, diffLine)
- continue
- case 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
- case line[0] == '+':
- curFile.Addition++
- diff.TotalAddition++
- diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
- rightLine++
- curSection.Lines = append(curSection.Lines, diffLine)
- continue
- case 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)
- case strings.HasPrefix(line, "Binary"):
- curFile.IsBin = true
- 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:],
- Index: len(diff.Files) + 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)
- }
|