Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

git_diff.go 4.6KB

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