You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 lines
4.5 KiB

10 years ago
10 years ago
  1. // Copyright 2014 The Gogs 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. package models
  5. import (
  6. "bufio"
  7. "io"
  8. "os"
  9. "os/exec"
  10. "strings"
  11. "github.com/gogits/git"
  12. "github.com/gogits/gogs/modules/base"
  13. "github.com/gogits/gogs/modules/log"
  14. )
  15. // Diff line types.
  16. const (
  17. DIFF_LINE_PLAIN = iota + 1
  18. DIFF_LINE_ADD
  19. DIFF_LINE_DEL
  20. DIFF_LINE_SECTION
  21. )
  22. const (
  23. DIFF_FILE_ADD = iota + 1
  24. DIFF_FILE_CHANGE
  25. DIFF_FILE_DEL
  26. )
  27. type DiffLine struct {
  28. LeftIdx int
  29. RightIdx int
  30. Type int
  31. Content string
  32. }
  33. func (d DiffLine) GetType() int {
  34. return d.Type
  35. }
  36. type DiffSection struct {
  37. Name string
  38. Lines []*DiffLine
  39. }
  40. type DiffFile struct {
  41. Name string
  42. Index int
  43. Addition, Deletion int
  44. Type int
  45. IsBin bool
  46. Sections []*DiffSection
  47. }
  48. type Diff struct {
  49. TotalAddition, TotalDeletion int
  50. Files []*DiffFile
  51. }
  52. func (diff *Diff) NumFiles() int {
  53. return len(diff.Files)
  54. }
  55. const DIFF_HEAD = "diff --git "
  56. func ParsePatch(reader io.Reader) (*Diff, error) {
  57. scanner := bufio.NewScanner(reader)
  58. var (
  59. curFile *DiffFile
  60. curSection = &DiffSection{
  61. Lines: make([]*DiffLine, 0, 10),
  62. }
  63. leftLine, rightLine int
  64. )
  65. diff := &Diff{Files: make([]*DiffFile, 0)}
  66. var i int
  67. for scanner.Scan() {
  68. line := scanner.Text()
  69. // fmt.Println(i, line)
  70. if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") {
  71. continue
  72. }
  73. i = i + 1
  74. // Diff data too large.
  75. if i == 5000 {
  76. log.Warn("Diff data too large")
  77. return &Diff{}, nil
  78. }
  79. if line == "" {
  80. continue
  81. }
  82. switch {
  83. case line[0] == ' ':
  84. diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
  85. leftLine++
  86. rightLine++
  87. curSection.Lines = append(curSection.Lines, diffLine)
  88. continue
  89. case line[0] == '@':
  90. curSection = &DiffSection{}
  91. curFile.Sections = append(curFile.Sections, curSection)
  92. ss := strings.Split(line, "@@")
  93. diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line}
  94. curSection.Lines = append(curSection.Lines, diffLine)
  95. // Parse line number.
  96. ranges := strings.Split(ss[len(ss)-2][1:], " ")
  97. leftLine, _ = base.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int()
  98. rightLine, _ = base.StrTo(strings.Split(ranges[1], ",")[0]).Int()
  99. continue
  100. case line[0] == '+':
  101. curFile.Addition++
  102. diff.TotalAddition++
  103. diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
  104. rightLine++
  105. curSection.Lines = append(curSection.Lines, diffLine)
  106. continue
  107. case line[0] == '-':
  108. curFile.Deletion++
  109. diff.TotalDeletion++
  110. diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine}
  111. if leftLine > 0 {
  112. leftLine++
  113. }
  114. curSection.Lines = append(curSection.Lines, diffLine)
  115. case strings.HasPrefix(line, "Binary"):
  116. curFile.IsBin = true
  117. continue
  118. }
  119. // Get new file.
  120. if strings.HasPrefix(line, DIFF_HEAD) {
  121. fs := strings.Split(line[len(DIFF_HEAD):], " ")
  122. a := fs[0]
  123. curFile = &DiffFile{
  124. Name: a[strings.Index(a, "/")+1:],
  125. Index: len(diff.Files) + 1,
  126. Type: DIFF_FILE_CHANGE,
  127. Sections: make([]*DiffSection, 0, 10),
  128. }
  129. diff.Files = append(diff.Files, curFile)
  130. // Check file diff type.
  131. for scanner.Scan() {
  132. switch {
  133. case strings.HasPrefix(scanner.Text(), "new file"):
  134. curFile.Type = DIFF_FILE_ADD
  135. case strings.HasPrefix(scanner.Text(), "deleted"):
  136. curFile.Type = DIFF_FILE_DEL
  137. case strings.HasPrefix(scanner.Text(), "index"):
  138. curFile.Type = DIFF_FILE_CHANGE
  139. }
  140. if curFile.Type > 0 {
  141. break
  142. }
  143. }
  144. }
  145. }
  146. return diff, nil
  147. }
  148. func GetDiff(repoPath, commitid string) (*Diff, error) {
  149. repo, err := git.OpenRepository(repoPath)
  150. if err != nil {
  151. return nil, err
  152. }
  153. commit, err := repo.GetCommit(commitid)
  154. if err != nil {
  155. return nil, err
  156. }
  157. // First commit of repository.
  158. if commit.ParentCount() == 0 {
  159. rd, wr := io.Pipe()
  160. go func() {
  161. cmd := exec.Command("git", "show", commitid)
  162. cmd.Dir = repoPath
  163. cmd.Stdout = wr
  164. cmd.Stdin = os.Stdin
  165. cmd.Stderr = os.Stderr
  166. cmd.Run()
  167. wr.Close()
  168. }()
  169. defer rd.Close()
  170. return ParsePatch(rd)
  171. }
  172. rd, wr := io.Pipe()
  173. go func() {
  174. c, _ := commit.Parent(0)
  175. cmd := exec.Command("git", "diff", c.Id.String(), commitid)
  176. cmd.Dir = repoPath
  177. cmd.Stdout = wr
  178. cmd.Stdin = os.Stdin
  179. cmd.Stderr = os.Stderr
  180. cmd.Run()
  181. wr.Close()
  182. }()
  183. defer rd.Close()
  184. return ParsePatch(rd)
  185. }