mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Show git-notes (#6984)
* 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>
			
			
This commit is contained in:
		
				
					committed by
					
						 Lauris BH
						Lauris BH
					
				
			
			
				
	
			
			
			
						parent
						
							d5a98a2969
						
					
				
				
					commit
					a98e085031
				
			
							
								
								
									
										60
									
								
								modules/git/notes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								modules/git/notes.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										24
									
								
								modules/git/notes_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								modules/git/notes_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 { | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -0,0 +1,4 @@ | ||||
| x<01><>M | ||||
| <EFBFBD>0F]<5D><14><>B<EFBFBD>&&m"<22>@\<5C>Of<4F>6<EFBFBD>HG<48><47><EFBFBD><EFBFBD> | ||||
| ~˷x<CBB7><78>y<1C><><EFBFBD><EFBFBD> <20><19>?[<5B><><EFBFBD><EFBFBD>B<EFBFBD>& | ||||
| H<b<>yߙNGt<47><74>ڨ<EFBFBD><DAA8>~.<2E>"<22>1x<>Ix`<60><><EFBFBD><EFBFBD><EFBFBD>&=㚸,}<7D><>{<7B>X<EFBFBD><58><EFBFBD><EFBFBD><EFBFBD>	<09>p<><70>)<29><><EFBFBD>j<7F>}^ 1AZ<41><5A><0E><>3<EFBFBD>,<2C><><01><>I0 | ||||
							
								
								
									
										1
									
								
								modules/git/tests/repos/repo1_bare/refs/notes/commits
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								modules/git/tests/repos/repo1_bare/refs/notes/commits
									
									
									
									
									
										Normal file
									
								
							| @@ -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" .}} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user