* Show git-notes
* Make git-notes heading text localizable
* Refactor git-notes data fetching to a separate function
* Display the author and time of git notes
* Move note bubble inside the commit bubble
* Revert "Move note bubble inside the commit bubble"
This reverts commit c0951fe0e3
.
* Add test for git-notes
* testing ui
* Polish CSS
* Apply suggestions from code review
Co-Authored-By: Lauris BH <lauris@nix.lv>
tags/v1.9.0-rc1
// Copyright 2019 The Gitea 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 git | |||||
import ( | |||||
"io/ioutil" | |||||
"gopkg.in/src-d/go-git.v4/plumbing" | |||||
) | |||||
// NotesRef is the git ref where Gitea will look for git-notes data. | |||||
// The value ("refs/notes/commits") is the default ref used by git-notes. | |||||
const NotesRef = "refs/notes/commits" | |||||
// Note stores information about a note created using git-notes. | |||||
type Note struct { | |||||
Message []byte | |||||
Commit *Commit | |||||
} | |||||
// GetNote retrieves the git-notes data for a given commit. | |||||
func GetNote(repo *Repository, commitID string, note *Note) error { | |||||
notes, err := repo.GetCommit(NotesRef) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
entry, err := notes.GetTreeEntryByPath(commitID) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
blob := entry.Blob() | |||||
dataRc, err := blob.DataAsync() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
defer dataRc.Close() | |||||
d, err := ioutil.ReadAll(dataRc) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
note.Message = d | |||||
commit, err := repo.gogitRepo.CommitObject(plumbing.Hash(notes.ID)) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
lastCommits, err := getLastCommitForPaths(commit, "", []string{commitID}) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
note.Commit = convertCommit(lastCommits[commitID]) | |||||
return nil | |||||
} |
// Copyright 2019 The Gitea 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 git | |||||
import ( | |||||
"path/filepath" | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func TestGetNotes(t *testing.T) { | |||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") | |||||
bareRepo1, err := OpenRepository(bareRepo1Path) | |||||
assert.NoError(t, err) | |||||
note := Note{} | |||||
err = GetNote(bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) | |||||
assert.NoError(t, err) | |||||
assert.Equal(t, []byte("Note contents\n"), note.Message) | |||||
assert.Equal(t, "Vladimir Panteleev", note.Commit.Author.Name) | |||||
} |
refs, err := bareRepo1.GetRefs() | refs, err := bareRepo1.GetRefs() | ||||
assert.NoError(t, err) | assert.NoError(t, err) | ||||
assert.Len(t, refs, 4) | |||||
assert.Len(t, refs, 5) | |||||
expectedRefs := []string{ | expectedRefs := []string{ | ||||
BranchPrefix + "branch1", | BranchPrefix + "branch1", | ||||
BranchPrefix + "branch2", | BranchPrefix + "branch2", | ||||
BranchPrefix + "master", | BranchPrefix + "master", | ||||
TagPrefix + "test", | TagPrefix + "test", | ||||
NotesRef, | |||||
} | } | ||||
for _, ref := range refs { | for _, ref := range refs { |
x¥ŽM | |||||
Â0F]ç³ëB�&&m"ž@\¹Of¦6ÐHG¥··ô | |||||
~Ë·xïÃy³€Ñþ …Œ?[—Œ¶èBÓ& | |||||
H<bÛyß™NGtåÚ¨ø–~.ð"å1xÄIx`þÀå•å&=㚸,}¤ù{šX® �ó¶ �p¬·)ÜãÂjÔ}^ 1AZ¡ÚÀ´3¦,•ú½ÀI0 |
ca6b5ddf303169a72d2a2971acde4f6eea194e5c |
"RenderCommitMessage": RenderCommitMessage, | "RenderCommitMessage": RenderCommitMessage, | ||||
"RenderCommitMessageLink": RenderCommitMessageLink, | "RenderCommitMessageLink": RenderCommitMessageLink, | ||||
"RenderCommitBody": RenderCommitBody, | "RenderCommitBody": RenderCommitBody, | ||||
"RenderNote": RenderNote, | |||||
"IsMultilineCommitMessage": IsMultilineCommitMessage, | "IsMultilineCommitMessage": IsMultilineCommitMessage, | ||||
"ThemeColorMetaTag": func() string { | "ThemeColorMetaTag": func() string { | ||||
return setting.UI.ThemeColorMetaTag | return setting.UI.ThemeColorMetaTag | ||||
return template.HTML(strings.Join(body[1:], "\n")) | return template.HTML(strings.Join(body[1:], "\n")) | ||||
} | } | ||||
// RenderNote renders the contents of a git-notes file as a commit message. | |||||
func RenderNote(msg, urlPrefix string, metas map[string]string) template.HTML { | |||||
cleanMsg := template.HTMLEscapeString(msg) | |||||
fullMessage, err := markup.RenderCommitMessage([]byte(cleanMsg), urlPrefix, "", metas) | |||||
if err != nil { | |||||
log.Error("RenderNote: %v", err) | |||||
return "" | |||||
} | |||||
return template.HTML(string(fullMessage)) | |||||
} | |||||
// IsMultilineCommitMessage checks to see if a commit message contains multiple lines. | // IsMultilineCommitMessage checks to see if a commit message contains multiple lines. | ||||
func IsMultilineCommitMessage(msg string) bool { | func IsMultilineCommitMessage(msg string) bool { | ||||
return strings.Count(strings.TrimSpace(msg), "\n") >= 1 | return strings.Count(strings.TrimSpace(msg), "\n") >= 1 |
diff.browse_source = Browse Source | diff.browse_source = Browse Source | ||||
diff.parent = parent | diff.parent = parent | ||||
diff.commit = commit | diff.commit = commit | ||||
diff.git-notes = Notes | |||||
diff.data_not_available = Diff Content Not Available | diff.data_not_available = Diff Content Not Available | ||||
diff.show_diff_stats = Show Diff Stats | diff.show_diff_stats = Show Diff Stats | ||||
diff.show_split_view = Split View | diff.show_split_view = Split View |
.stats-table .table-cell.tiny{height:.5em} | .stats-table .table-cell.tiny{height:.5em} | ||||
tbody.commit-list{vertical-align:baseline} | tbody.commit-list{vertical-align:baseline} | ||||
.commit-body{white-space:pre-wrap} | .commit-body{white-space:pre-wrap} | ||||
.git-notes.top{text-align:left} | |||||
.git-notes .commit-body{margin:0} | |||||
@media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px} | @media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px} | ||||
.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0} | .ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0} | ||||
} | } |
white-space: pre-wrap; | white-space: pre-wrap; | ||||
} | } | ||||
.git-notes { | |||||
&.top { | |||||
text-align: left; | |||||
} | |||||
.commit-body { | |||||
margin: 0; | |||||
} | |||||
} | |||||
@media only screen and (max-width: 767px) { | @media only screen and (max-width: 767px) { | ||||
.ui.stackable.menu { | .ui.stackable.menu { | ||||
&.mobile--margin-between-items > .item { | &.mobile--margin-between-items > .item { |
"code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"code.gitea.io/gitea/modules/templates" | |||||
) | ) | ||||
const ( | const ( | ||||
ctx.Data["Parents"] = parents | ctx.Data["Parents"] = parents | ||||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 | ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 | ||||
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", commitID) | ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", commitID) | ||||
note := &git.Note{} | |||||
err = git.GetNote(ctx.Repo.GitRepo, commitID, note) | |||||
if err == nil { | |||||
ctx.Data["Note"] = string(templates.ToUTF8WithFallback(note.Message)) | |||||
ctx.Data["NoteCommit"] = note.Commit | |||||
ctx.Data["NoteAuthor"] = models.ValidateCommitWithEmail(note.Commit) | |||||
} | |||||
if commit.ParentCount() > 0 { | if commit.ParentCount() > 0 { | ||||
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", parents[0]) | ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", parents[0]) | ||||
} | } |
</div> | </div> | ||||
{{end}} | {{end}} | ||||
{{end}} | {{end}} | ||||
{{if .Note}} | |||||
<div class="ui top attached info segment message git-notes"> | |||||
<i class="sticky note icon"></i> | |||||
{{.i18n.Tr "repo.diff.git-notes"}}: | |||||
{{if .NoteAuthor}} | |||||
<a href="{{.NoteAuthor.HomeLink}}"> | |||||
{{if .NoteAuthor.FullName}} | |||||
<strong>{{.NoteAuthor.FullName}}</strong> | |||||
{{else}} | |||||
<strong>{{.NoteCommit.Author.Name}}</strong> | |||||
{{end}} | |||||
</a> | |||||
{{else}} | |||||
<strong>{{.NoteCommit.Author.Name}}</strong> | |||||
{{end}} | |||||
<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When $.Lang}}</span> | |||||
</div> | |||||
<div class="ui bottom attached info segment git-notes"> | |||||
<pre class="commit-body">{{RenderNote .Note $.RepoLink $.Repository.ComposeMetas}}</pre> | |||||
</div> | |||||
{{end}} | |||||
{{end}} | {{end}} | ||||
{{template "repo/diff/box" .}} | {{template "repo/diff/box" .}} |