diff options
author | slene <vslene@gmail.com> | 2014-04-13 09:35:36 +0800 |
---|---|---|
committer | slene <vslene@gmail.com> | 2014-04-13 09:35:36 +0800 |
commit | 52b4ab2aa589cf892b24e95872cbac7b6e78ed3a (patch) | |
tree | efa6799dd4b78d6e70a28ac9feaa4351836c201a /models/git_diff.go | |
parent | 9ffa8a40836a5e3341267affbaef08acf4765a74 (diff) | |
download | gitea-52b4ab2aa589cf892b24e95872cbac7b6e78ed3a.tar.gz gitea-52b4ab2aa589cf892b24e95872cbac7b6e78ed3a.zip |
update with new git
Diffstat (limited to 'models/git_diff.go')
-rw-r--r-- | models/git_diff.go | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/models/git_diff.go b/models/git_diff.go new file mode 100644 index 0000000000..de9d1de7b1 --- /dev/null +++ b/models/git_diff.go @@ -0,0 +1,207 @@ +// 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) +} |