* 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
@@ -0,0 +1,60 @@ | |||
// 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 | |||
} |
@@ -0,0 +1,24 @@ | |||
// 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) | |||
} |
@@ -19,13 +19,14 @@ func TestRepository_GetRefs(t *testing.T) { | |||
refs, err := bareRepo1.GetRefs() | |||
assert.NoError(t, err) | |||
assert.Len(t, refs, 4) | |||
assert.Len(t, refs, 5) | |||
expectedRefs := []string{ | |||
BranchPrefix + "branch1", | |||
BranchPrefix + "branch2", | |||
BranchPrefix + "master", | |||
TagPrefix + "test", | |||
NotesRef, | |||
} | |||
for _, ref := range refs { |
@@ -0,0 +1,4 @@ | |||
x¥ŽM | |||
Â0F]ç³ëB�&&m"ž@\¹Of¦6ÐHG¥··ô | |||
~Ë·xïÃy³€Ñþ …Œ?[—Œ¶èBÓ& | |||
H<bÛyß™NGtåÚ¨ø–~.ð"å1xÄIx`þÀå•å&=㚸,}¤ù{šX® �ó¶ �p¬·)ÜãÂjÔ}^ 1AZ¡ÚÀ´3¦,•ú½ÀI0 |
@@ -0,0 +1 @@ | |||
ca6b5ddf303169a72d2a2971acde4f6eea194e5c |
@@ -125,6 +125,7 @@ func NewFuncMap() []template.FuncMap { | |||
"RenderCommitMessage": RenderCommitMessage, | |||
"RenderCommitMessageLink": RenderCommitMessageLink, | |||
"RenderCommitBody": RenderCommitBody, | |||
"RenderNote": RenderNote, | |||
"IsMultilineCommitMessage": IsMultilineCommitMessage, | |||
"ThemeColorMetaTag": func() string { | |||
return setting.UI.ThemeColorMetaTag | |||
@@ -392,6 +393,17 @@ func RenderCommitBody(msg, urlPrefix string, metas map[string]string) template.H | |||
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. | |||
func IsMultilineCommitMessage(msg string) bool { | |||
return strings.Count(strings.TrimSpace(msg), "\n") >= 1 |
@@ -1314,6 +1314,7 @@ settings.unarchive.error = An error occured while trying to un-archive the repo. | |||
diff.browse_source = Browse Source | |||
diff.parent = parent | |||
diff.commit = commit | |||
diff.git-notes = Notes | |||
diff.data_not_available = Diff Content Not Available | |||
diff.show_diff_stats = Show Diff Stats | |||
diff.show_split_view = Split View |
@@ -803,6 +803,8 @@ footer .ui.left,footer .ui.right{line-height:40px} | |||
.stats-table .table-cell.tiny{height:.5em} | |||
tbody.commit-list{vertical-align:baseline} | |||
.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} | |||
.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0} | |||
} |
@@ -2219,6 +2219,15 @@ tbody.commit-list { | |||
white-space: pre-wrap; | |||
} | |||
.git-notes { | |||
&.top { | |||
text-align: left; | |||
} | |||
.commit-body { | |||
margin: 0; | |||
} | |||
} | |||
@media only screen and (max-width: 767px) { | |||
.ui.stackable.menu { | |||
&.mobile--margin-between-items > .item { |
@@ -15,6 +15,7 @@ import ( | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/templates" | |||
) | |||
const ( | |||
@@ -246,6 +247,15 @@ func Diff(ctx *context.Context) { | |||
ctx.Data["Parents"] = parents | |||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 | |||
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 { | |||
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", parents[0]) | |||
} |
@@ -65,6 +65,27 @@ | |||
</div> | |||
{{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}} | |||
{{template "repo/diff/box" .}} |