|
6 | 6 | package git
|
7 | 7 |
|
8 | 8 | import (
|
| 9 | + "bufio" |
9 | 10 | "bytes"
|
| 11 | + "errors" |
10 | 12 | "fmt"
|
11 | 13 | "io"
|
| 14 | + "io/ioutil" |
| 15 | + "os" |
| 16 | + "path/filepath" |
12 | 17 | "regexp"
|
13 | 18 | "strconv"
|
14 | 19 | "strings"
|
@@ -188,6 +193,8 @@ func GetDiffShortStat(repoPath string, args ...string) (numFiles, totalAdditions
|
188 | 193 | var shortStatFormat = regexp.MustCompile(
|
189 | 194 | `\s*(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?`)
|
190 | 195 |
|
| 196 | +var patchCommits = regexp.MustCompile(`^From\s(\w+)\s`) |
| 197 | + |
191 | 198 | func parseDiffStat(stdout string) (numFiles, totalAdditions, totalDeletions int, err error) {
|
192 | 199 | if len(stdout) == 0 || stdout == "\n" {
|
193 | 200 | return 0, 0, 0, nil
|
@@ -267,3 +274,57 @@ func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) err
|
267 | 274 | }
|
268 | 275 | return err
|
269 | 276 | }
|
| 277 | + |
| 278 | +// ReadPullHead will fetch a pull ref if possible or return an error |
| 279 | +func (repo *Repository) ReadPullHead(prID int64) (commitSHA string, err error) { |
| 280 | + headPath := fmt.Sprintf("refs/pull/%d/head", prID) |
| 281 | + fullHeadPath := filepath.Join(repo.Path, headPath) |
| 282 | + loadHead, err := os.Open(fullHeadPath) |
| 283 | + if err != nil { |
| 284 | + return "", err |
| 285 | + } |
| 286 | + defer loadHead.Close() |
| 287 | + // Read only the first line of the patch - usually it contains the first commit made in patch |
| 288 | + scanner := bufio.NewScanner(loadHead) |
| 289 | + scanner.Scan() |
| 290 | + commitHead := scanner.Text() |
| 291 | + if len(commitHead) != 40 { |
| 292 | + return "", errors.New("head file doesn't contain valid commit ID") |
| 293 | + } |
| 294 | + return commitHead, nil |
| 295 | +} |
| 296 | + |
| 297 | +// ReadPatchCommit will check if a diff patch exists and return stats |
| 298 | +func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error) { |
| 299 | + // Migrated repositories download patches to "pulls" location |
| 300 | + patchFile := fmt.Sprintf("pulls/%d.patch", prID) |
| 301 | + loadPatch, err := os.Open(filepath.Join(repo.Path, patchFile)) |
| 302 | + if err != nil { |
| 303 | + return "", err |
| 304 | + } |
| 305 | + defer loadPatch.Close() |
| 306 | + // Read only the first line of the patch - usually it contains the first commit made in patch |
| 307 | + scanner := bufio.NewScanner(loadPatch) |
| 308 | + scanner.Scan() |
| 309 | + // Parse the Patch stats, sometimes Migration returns a 404 for the patch file |
| 310 | + commitSHAGroups := patchCommits.FindStringSubmatch(scanner.Text()) |
| 311 | + if len(commitSHAGroups) != 0 { |
| 312 | + commitSHA = commitSHAGroups[1] |
| 313 | + } else { |
| 314 | + return "", errors.New("patch file doesn't contain valid commit ID") |
| 315 | + } |
| 316 | + return commitSHA, nil |
| 317 | +} |
| 318 | + |
| 319 | +// WritePullHead will populate a PR head retrieved from patch file |
| 320 | +func (repo *Repository) WritePullHead(prID int64, commitSHA string) error { |
| 321 | + headPath := fmt.Sprintf("refs/pull/%d", prID) |
| 322 | + fullHeadPath := filepath.Join(repo.Path, headPath) |
| 323 | + // Create missing directory just in case |
| 324 | + if err := os.MkdirAll(fullHeadPath, os.ModePerm); err != nil { |
| 325 | + return err |
| 326 | + } |
| 327 | + commitBytes := []byte(commitSHA) |
| 328 | + pullPath := filepath.Join(fullHeadPath, "head") |
| 329 | + return ioutil.WriteFile(pullPath, commitBytes, os.ModePerm) |
| 330 | +} |
0 commit comments