diff options
author | Lauris BH <lauris@nix.lv> | 2018-08-06 07:43:22 +0300 |
---|---|---|
committer | Jonas Franz <info@jonasfranz.software> | 2018-08-06 06:43:21 +0200 |
commit | 6e64f9db8eb889f9cc7e8c9576b2f9c89750927e (patch) | |
tree | d76b927295a1d2f69ea73a78e83d9264ce1510f2 /models/git_diff.go | |
parent | 9c354a539ab498ebfdebf7395cf17f95f8b24ac8 (diff) | |
download | gitea-6e64f9db8eb889f9cc7e8c9576b2f9c89750927e.tar.gz gitea-6e64f9db8eb889f9cc7e8c9576b2f9c89750927e.zip |
Pull request review/approval and comment on code (#3748)
* Initial ui components for pull request review
* Add Review
Add IssueComment types
Signed-off-by: Jonas Franz <info@jonasfranz.software>
(cherry picked from commit 2b4daab)
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Replace ReviewComment with Content
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add load functions
Add ReviewID to findComments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add create review comment implementation
Add migration for review
Other small changes
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Simplified create and find functions for review
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Moved "Pending" to first position
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add GetCurrentReview to simplify fetching current review
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Preview for listing comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Move new comment form to its own file
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Implement Review form
Show Review comments on comment stream
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for single comments
Showing buttons in context
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add pending tag to pending review comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add unit tests for Review
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fetch all review ids at once
Add unit tests
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* gofmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Improved comment rendering in "Files" view by adding Comments to DiffLine
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for invalidating comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Switched back to code.gitea.io/git
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Moved review migration from v64 to v65
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Rebuild css
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* gofmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Improve translations
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix unit tests by updating fixtures and updating outdated test
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Comments will be shown at the right place now
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for deleting CodeComments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix problems caused by files in subdirectories
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for showing code comments of reviews in conversation
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for "Show/Hide outdated"
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update code.gitea.io/git
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for new webhooks
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update comparison
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Resolve conflicts
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Minor UI improvements
* update code.gitea.io/git
* Fix ui bug reported by @lunny causing wrong position of add button
Add functionality to "Cancel" button
Add scale effects to add button
Hide "Cancel" button for existing comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Prepare solving conflicts
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Show add button only if no comments already exist for the line
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add missing vendor files
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Check if reviewer is nil
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Show forms only to users who are logged in
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Revert "Show forms only to users who are logged in"
This reverts commit c083682
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Save patch in comment
Render patch for code comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add link to comment in code
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add reply form to comment list
Show forms only to signed in users
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add 'Reply' as translatable
Add CODE_COMMENT_LINES setting
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* gofmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix problems introduced by checking for singed in user
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add v70
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update generated stylesheet
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix preview
Beginn with new review comment patch system
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add new algo to generate diff for line range
Remove old algo used for cutting big diffs (it was very buggy)
* Add documentation and example for CutDiffAroundLine
* Fix example of CutDiffAroundLine
* Fix some comment UI rendering bugs
* Add code comment edit mode
* Send notifications / actions to users until review gets published
Fix diff generation bug
Fix wrong hashtag
* Fix vet errors
* Send notifications also for single comments
* Fix some notification bugs, fix link
* Fix: add comment icon is only shown on code lines
* Add lint comment
* Add unit tests for git diff
* Add more error messages
* Regenerated css
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* fmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Regenerated CSS with latest less version
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix test by updating comment type to new ID
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Introducing CodeComments as type for map[string]map[int64][]*Comment
Other minor code improvements
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix data-tab issues
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Remove unnecessary change
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* refactored checkForInvalidation
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Append comments instead of setting
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Use HeadRepo instead of BaseRepo
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update migration
Signed-off-by: Jonas Franz <info@jonasfranz.de>
* Regenerated CSS
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add copyright
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update index.css
Signed-off-by: Jonas Franz <info@jonasfranz.software>
Diffstat (limited to 'models/git_diff.go')
-rw-r--r-- | models/git_diff.go | 206 |
1 files changed, 195 insertions, 11 deletions
diff --git a/models/git_diff.go b/models/git_diff.go index 7b0b672ff1..006238cd06 100644 --- a/models/git_diff.go +++ b/models/git_diff.go @@ -14,6 +14,8 @@ import ( "io/ioutil" "os" "os/exec" + "regexp" + "sort" "strconv" "strings" @@ -57,6 +59,7 @@ type DiffLine struct { RightIdx int Type DiffLineType Content string + Comments []*Comment } // GetType returns the type of a DiffLine. @@ -64,6 +67,19 @@ func (d *DiffLine) GetType() int { return int(d.Type) } +// CanComment returns whether or not a line can get commented +func (d *DiffLine) CanComment() bool { + return len(d.Comments) == 0 && d.Type != DiffLineSection +} + +// GetCommentSide returns the comment side of the first comment, if not set returns empty string +func (d *DiffLine) GetCommentSide() string { + if len(d.Comments) == 0 { + return "" + } + return d.Comments[0].DiffSide() +} + // DiffSection represents a section of a DiffFile. type DiffSection struct { Name string @@ -225,11 +241,167 @@ type Diff struct { IsIncomplete bool } +// LoadComments loads comments into each line +func (diff *Diff) LoadComments(issue *Issue, currentUser *User) error { + allComments, err := FetchCodeComments(issue, currentUser) + if err != nil { + return err + } + for _, file := range diff.Files { + if lineCommits, ok := allComments[file.Name]; ok { + for _, section := range file.Sections { + for _, line := range section.Lines { + if comments, ok := lineCommits[int64(line.LeftIdx*-1)]; ok { + line.Comments = append(line.Comments, comments...) + } + if comments, ok := lineCommits[int64(line.RightIdx)]; ok { + line.Comments = append(line.Comments, comments...) + } + sort.SliceStable(line.Comments, func(i, j int) bool { + return line.Comments[i].CreatedUnix < line.Comments[j].CreatedUnix + }) + } + } + } + } + return nil +} + // NumFiles returns number of files changes in a diff. func (diff *Diff) NumFiles() int { return len(diff.Files) } +// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9] +var hunkRegex = regexp.MustCompile(`^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@`) + +func isHeader(lof string) bool { + return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++") +} + +// CutDiffAroundLine cuts a diff of a file in way that only the given line + numberOfLine above it will be shown +// it also recalculates hunks and adds the appropriate headers to the new diff. +// Warning: Only one-file diffs are allowed. +func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLine int) string { + if line == 0 || numbersOfLine == 0 { + // no line or num of lines => no diff + return "" + } + scanner := bufio.NewScanner(originalDiff) + hunk := make([]string, 0) + // begin is the start of the hunk containing searched line + // end is the end of the hunk ... + // currentLine is the line number on the side of the searched line (differentiated by old) + // otherLine is the line number on the opposite side of the searched line (differentiated by old) + var begin, end, currentLine, otherLine int64 + var headerLines int + for scanner.Scan() { + lof := scanner.Text() + // Add header to enable parsing + if isHeader(lof) { + hunk = append(hunk, lof) + headerLines++ + } + if currentLine > line { + break + } + // Detect "hunk" with contains commented lof + if strings.HasPrefix(lof, "@@") { + // Already got our hunk. End of hunk detected! + if len(hunk) > headerLines { + break + } + groups := hunkRegex.FindStringSubmatch(lof) + if old { + begin = com.StrTo(groups[1]).MustInt64() + end = com.StrTo(groups[2]).MustInt64() + // init otherLine with begin of opposite side + otherLine = com.StrTo(groups[3]).MustInt64() + } else { + begin = com.StrTo(groups[3]).MustInt64() + end = com.StrTo(groups[4]).MustInt64() + // init otherLine with begin of opposite side + otherLine = com.StrTo(groups[1]).MustInt64() + } + end += begin // end is for real only the number of lines in hunk + // lof is between begin and end + if begin <= line && end >= line { + hunk = append(hunk, lof) + currentLine = begin + continue + } + } else if len(hunk) > headerLines { + hunk = append(hunk, lof) + // Count lines in context + switch lof[0] { + case '+': + if !old { + currentLine++ + } else { + otherLine++ + } + case '-': + if old { + currentLine++ + } else { + otherLine++ + } + default: + currentLine++ + otherLine++ + } + } + } + + // No hunk found + if currentLine == 0 { + return "" + } + // headerLines + hunkLine (1) = totalNonCodeLines + if len(hunk)-headerLines-1 <= numbersOfLine { + // No need to cut the hunk => return existing hunk + return strings.Join(hunk, "\n") + } + var oldBegin, oldNumOfLines, newBegin, newNumOfLines int64 + if old { + oldBegin = currentLine + newBegin = otherLine + } else { + oldBegin = otherLine + newBegin = currentLine + } + // headers + hunk header + newHunk := make([]string, headerLines) + // transfer existing headers + for idx, lof := range hunk[:headerLines] { + newHunk[idx] = lof + } + // transfer last n lines + for _, lof := range hunk[len(hunk)-numbersOfLine-1:] { + newHunk = append(newHunk, lof) + } + // calculate newBegin, ... by counting lines + for i := len(hunk) - 1; i >= len(hunk)-numbersOfLine; i-- { + switch hunk[i][0] { + case '+': + newBegin-- + newNumOfLines++ + case '-': + oldBegin-- + oldNumOfLines++ + default: + oldBegin-- + newBegin-- + newNumOfLines++ + oldNumOfLines++ + } + } + // construct the new hunk header + newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@", + oldBegin, oldNumOfLines, newBegin, newNumOfLines) + return strings.Join(newHunk, "\n") +} + const cmdDiffHead = "diff --git " // ParsePatch builds a Diff object from a io.Reader and some @@ -307,7 +479,6 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D if curFileLinesCount >= maxLines { curFile.IsIncomplete = true } - switch { case line[0] == ' ': diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine} @@ -524,32 +695,46 @@ const ( // GetRawDiff dumps diff results of repository in given commit ID to io.Writer. // TODO: move this function to gogits/git-module func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Writer) error { + return GetRawDiffForFile(repoPath, "", commitID, diffType, "", writer) +} + +// GetRawDiffForFile dumps diff results of file in given commit ID to io.Writer. +// TODO: move this function to gogits/git-module +func GetRawDiffForFile(repoPath, startCommit, endCommit string, diffType RawDiffType, file string, writer io.Writer) error { repo, err := git.OpenRepository(repoPath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } - commit, err := repo.GetCommit(commitID) + commit, err := repo.GetCommit(endCommit) if err != nil { return fmt.Errorf("GetCommit: %v", err) } - + fileArgs := make([]string, 0) + if len(file) > 0 { + fileArgs = append(fileArgs, "--", file) + } var cmd *exec.Cmd switch diffType { case RawDiffNormal: - if commit.ParentCount() == 0 { - cmd = exec.Command("git", "show", commitID) + if len(startCommit) != 0 { + cmd = exec.Command("git", append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)...) + } else if commit.ParentCount() == 0 { + cmd = exec.Command("git", append([]string{"show", endCommit}, fileArgs...)...) } else { c, _ := commit.Parent(0) - cmd = exec.Command("git", "diff", "-M", c.ID.String(), commitID) + cmd = exec.Command("git", append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)...) } case RawDiffPatch: - if commit.ParentCount() == 0 { - cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", "--root", commitID) + if len(startCommit) != 0 { + query := fmt.Sprintf("%s...%s", endCommit, startCommit) + cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)...) + } else if commit.ParentCount() == 0 { + cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)...) } else { c, _ := commit.Parent(0) - query := fmt.Sprintf("%s...%s", commitID, c.ID.String()) - cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", query) + query := fmt.Sprintf("%s...%s", endCommit, c.ID.String()) + cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)...) } default: return fmt.Errorf("invalid diffType: %s", diffType) @@ -560,7 +745,6 @@ func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Write cmd.Dir = repoPath cmd.Stdout = writer cmd.Stderr = stderr - if err = cmd.Run(); err != nil { return fmt.Errorf("Run: %v - %s", err, stderr) } |