5
5
package repo
6
6
7
7
import (
8
- "bytes"
9
8
"container/list"
10
9
"fmt"
11
10
"html"
@@ -18,7 +17,6 @@ import (
18
17
"code.gitea.io/gitea/modules/context"
19
18
"code.gitea.io/gitea/modules/git"
20
19
"code.gitea.io/gitea/modules/highlight"
21
- "code.gitea.io/gitea/modules/log"
22
20
"code.gitea.io/gitea/modules/templates"
23
21
"code.gitea.io/gitea/modules/timeutil"
24
22
)
@@ -27,6 +25,20 @@ const (
27
25
tplBlame base.TplName = "repo/home"
28
26
)
29
27
28
+ type blameRow struct {
29
+ RowNumber int
30
+ Avatar gotemplate.HTML
31
+ RepoLink string
32
+ PartSha string
33
+ PreviousSha string
34
+ PreviousShaURL string
35
+ IsFirstCommit bool
36
+ CommitURL string
37
+ CommitMessage string
38
+ CommitSince gotemplate.HTML
39
+ Code gotemplate.HTML
40
+ }
41
+
30
42
// RefBlame render blame page
31
43
func RefBlame (ctx * context.Context ) {
32
44
fileName := ctx .Repo .TreePath
@@ -39,19 +51,6 @@ func RefBlame(ctx *context.Context) {
39
51
repoName := ctx .Repo .Repository .Name
40
52
commitID := ctx .Repo .CommitID
41
53
42
- commit , err := ctx .Repo .GitRepo .GetCommit (commitID )
43
- if err != nil {
44
- if git .IsErrNotExist (err ) {
45
- ctx .NotFound ("Repo.GitRepo.GetCommit" , err )
46
- } else {
47
- ctx .ServerError ("Repo.GitRepo.GetCommit" , err )
48
- }
49
- return
50
- }
51
- if len (commitID ) != 40 {
52
- commitID = commit .ID .String ()
53
- }
54
-
55
54
branchLink := ctx .Repo .RepoLink + "/src/" + ctx .Repo .BranchNameSubURL ()
56
55
treeLink := branchLink
57
56
rawLink := ctx .Repo .RepoLink + "/raw/" + ctx .Repo .BranchNameSubURL ()
@@ -74,25 +73,6 @@ func RefBlame(ctx *context.Context) {
74
73
}
75
74
}
76
75
77
- // Show latest commit info of repository in table header,
78
- // or of directory if not in root directory.
79
- latestCommit := ctx .Repo .Commit
80
- if len (ctx .Repo .TreePath ) > 0 {
81
- latestCommit , err = ctx .Repo .Commit .GetCommitByPath (ctx .Repo .TreePath )
82
- if err != nil {
83
- ctx .ServerError ("GetCommitByPath" , err )
84
- return
85
- }
86
- }
87
- ctx .Data ["LatestCommit" ] = latestCommit
88
- ctx .Data ["LatestCommitVerification" ] = models .ParseCommitWithSignature (latestCommit )
89
- ctx .Data ["LatestCommitUser" ] = models .ValidateCommitWithEmail (latestCommit )
90
-
91
- statuses , err := models .GetLatestCommitStatus (ctx .Repo .Repository .ID , ctx .Repo .Commit .ID .String (), models.ListOptions {})
92
- if err != nil {
93
- log .Error ("GetLatestCommitStatus: %v" , err )
94
- }
95
-
96
76
// Get current entry user currently looking at.
97
77
entry , err := ctx .Repo .Commit .GetTreeEntryByPath (ctx .Repo .TreePath )
98
78
if err != nil {
@@ -102,9 +82,6 @@ func RefBlame(ctx *context.Context) {
102
82
103
83
blob := entry .Blob ()
104
84
105
- ctx .Data ["LatestCommitStatus" ] = models .CalcCommitStatus (statuses )
106
- ctx .Data ["LatestCommitStatuses" ] = statuses
107
-
108
85
ctx .Data ["Paths" ] = paths
109
86
ctx .Data ["TreeLink" ] = treeLink
110
87
ctx .Data ["TreeNames" ] = treeNames
@@ -145,70 +122,112 @@ func RefBlame(ctx *context.Context) {
145
122
blameParts = append (blameParts , * blamePart )
146
123
}
147
124
125
+ // Get Topics of this repo
126
+ renderRepoTopics (ctx )
127
+ if ctx .Written () {
128
+ return
129
+ }
130
+
131
+ commitNames , previousCommits := processBlameParts (ctx , blameParts )
132
+ if ctx .Written () {
133
+ return
134
+ }
135
+
136
+ renderBlame (ctx , blameParts , commitNames , previousCommits )
137
+
138
+ ctx .HTML (http .StatusOK , tplBlame )
139
+ }
140
+
141
+ func processBlameParts (ctx * context.Context , blameParts []git.BlamePart ) (map [string ]models.UserCommit , map [string ]string ) {
142
+ // store commit data by SHA to look up avatar info etc
148
143
commitNames := make (map [string ]models.UserCommit )
144
+ // previousCommits contains links from SHA to parent SHA,
145
+ // if parent also contains the current TreePath.
146
+ previousCommits := make (map [string ]string )
147
+ // and as blameParts can reference the same commits multiple
148
+ // times, we cache the lookup work locally
149
149
commits := list .New ()
150
+ commitCache := map [string ]* git.Commit {}
151
+ commitCache [ctx .Repo .Commit .ID .String ()] = ctx .Repo .Commit
150
152
151
153
for _ , part := range blameParts {
152
154
sha := part .Sha
153
155
if _ , ok := commitNames [sha ]; ok {
154
156
continue
155
157
}
156
158
157
- commit , err := ctx .Repo .GitRepo .GetCommit (sha )
158
- if err != nil {
159
- if git .IsErrNotExist (err ) {
160
- ctx .NotFound ("Repo.GitRepo.GetCommit" , err )
161
- } else {
162
- ctx .ServerError ("Repo.GitRepo.GetCommit" , err )
159
+ // find the blamePart commit, to look up parent & email address for avatars
160
+ commit , ok := commitCache [sha ]
161
+ var err error
162
+ if ! ok {
163
+ commit , err = ctx .Repo .GitRepo .GetCommit (sha )
164
+ if err != nil {
165
+ if git .IsErrNotExist (err ) {
166
+ ctx .NotFound ("Repo.GitRepo.GetCommit" , err )
167
+ } else {
168
+ ctx .ServerError ("Repo.GitRepo.GetCommit" , err )
169
+ }
170
+ return nil , nil
171
+ }
172
+ commitCache [sha ] = commit
173
+ }
174
+
175
+ // find parent commit
176
+ if commit .ParentCount () > 0 {
177
+ psha := commit .Parents [0 ]
178
+ previousCommit , ok := commitCache [psha .String ()]
179
+ if ! ok {
180
+ previousCommit , _ = commit .Parent (0 )
181
+ if previousCommit != nil {
182
+ commitCache [psha .String ()] = previousCommit
183
+ }
184
+ }
185
+ // only store parent commit ONCE, if it has the file
186
+ if previousCommit != nil {
187
+ if haz1 , _ := previousCommit .HasFile (ctx .Repo .TreePath ); haz1 {
188
+ previousCommits [commit .ID .String ()] = previousCommit .ID .String ()
189
+ }
163
190
}
164
- return
165
191
}
166
192
167
193
commits .PushBack (commit )
168
194
169
195
commitNames [commit .ID .String ()] = models.UserCommit {}
170
196
}
171
197
198
+ // populate commit email addresses to later look up avatars.
172
199
commits = models .ValidateCommitsWithEmails (commits )
173
-
174
200
for e := commits .Front (); e != nil ; e = e .Next () {
175
201
c := e .Value .(models.UserCommit )
176
-
177
202
commitNames [c .ID .String ()] = c
178
203
}
179
204
180
- // Get Topics of this repo
181
- renderRepoTopics (ctx )
182
- if ctx .Written () {
183
- return
184
- }
185
-
186
- renderBlame (ctx , blameParts , commitNames )
187
-
188
- ctx .HTML (http .StatusOK , tplBlame )
205
+ return commitNames , previousCommits
189
206
}
190
207
191
- func renderBlame (ctx * context.Context , blameParts []git.BlamePart , commitNames map [string ]models.UserCommit ) {
208
+ func renderBlame (ctx * context.Context , blameParts []git.BlamePart , commitNames map [string ]models.UserCommit , previousCommits map [ string ] string ) {
192
209
repoLink := ctx .Repo .RepoLink
193
210
194
211
var lines = make ([]string , 0 )
195
-
196
- var commitInfo bytes.Buffer
197
- var lineNumbers bytes.Buffer
198
- var codeLines bytes.Buffer
212
+ rows := make ([]* blameRow , 0 )
199
213
200
214
var i = 0
201
- for pi , part := range blameParts {
215
+ var commitCnt = 0
216
+ for _ , part := range blameParts {
202
217
for index , line := range part .Lines {
203
218
i ++
204
219
lines = append (lines , line )
205
220
206
- var attr = ""
207
- if len (part .Lines )- 1 == index && len (blameParts )- 1 != pi {
208
- attr = " bottom-line"
221
+ br := & blameRow {
222
+ RowNumber : i ,
209
223
}
224
+
210
225
commit := commitNames [part .Sha ]
226
+ previousSha := previousCommits [part .Sha ]
211
227
if index == 0 {
228
+ // Count commit number
229
+ commitCnt ++
230
+
212
231
// User avatar image
213
232
commitSince := timeutil .TimeSinceUnix (timeutil .TimeStamp (commit .Author .When .Unix ()), ctx .Data ["Lang" ].(string ))
214
233
@@ -219,33 +238,27 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
219
238
avatar = string (templates .AvatarByEmail (commit .Author .Email , commit .Author .Name , 18 , "mr-3" ))
220
239
}
221
240
222
- commitInfo .WriteString (fmt .Sprintf (`<div class="blame-info%s"><div class="blame-data"><div class="blame-avatar">%s</div><div class="blame-message"><a href="%s/commit/%s" title="%[5]s">%[5]s</a></div><div class="blame-time">%s</div></div></div>` , attr , avatar , repoLink , part .Sha , html .EscapeString (commit .CommitMessage ), commitSince ))
223
- } else {
224
- commitInfo .WriteString (fmt .Sprintf (`<div class="blame-info%s">​</div>` , attr ))
225
- }
226
-
227
- //Line number
228
- if len (part .Lines )- 1 == index && len (blameParts )- 1 != pi {
229
- lineNumbers .WriteString (fmt .Sprintf (`<span id="L%d" data-line-number="%d" class="bottom-line"></span>` , i , i ))
230
- } else {
231
- lineNumbers .WriteString (fmt .Sprintf (`<span id="L%d" data-line-number="%d"></span>` , i , i ))
241
+ br .Avatar = gotemplate .HTML (avatar )
242
+ br .RepoLink = repoLink
243
+ br .PartSha = part .Sha
244
+ br .PreviousSha = previousSha
245
+ br .PreviousShaURL = fmt .Sprintf ("%s/blame/commit/%s/%s" , repoLink , previousSha , ctx .Repo .TreePath )
246
+ br .CommitURL = fmt .Sprintf ("%s/commit/%s" , repoLink , part .Sha )
247
+ br .CommitMessage = html .EscapeString (commit .CommitMessage )
248
+ br .CommitSince = commitSince
232
249
}
233
250
234
251
if i != len (lines )- 1 {
235
252
line += "\n "
236
253
}
237
254
fileName := fmt .Sprintf ("%v" , ctx .Data ["FileName" ])
238
255
line = highlight .Code (fileName , line )
239
- line = `<code class="code-inner">` + line + `</code>`
240
- if len (part .Lines )- 1 == index && len (blameParts )- 1 != pi {
241
- codeLines .WriteString (fmt .Sprintf (`<li class="L%d bottom-line" rel="L%d">%s</li>` , i , i , line ))
242
- } else {
243
- codeLines .WriteString (fmt .Sprintf (`<li class="L%d" rel="L%d">%s</li>` , i , i , line ))
244
- }
256
+
257
+ br .Code = gotemplate .HTML (line )
258
+ rows = append (rows , br )
245
259
}
246
260
}
247
261
248
- ctx .Data ["BlameContent" ] = gotemplate .HTML (codeLines .String ())
249
- ctx .Data ["BlameCommitInfo" ] = gotemplate .HTML (commitInfo .String ())
250
- ctx .Data ["BlameLineNums" ] = gotemplate .HTML (lineNumbers .String ())
262
+ ctx .Data ["BlameRows" ] = rows
263
+ ctx .Data ["CommitCnt" ] = commitCnt
251
264
}
0 commit comments