Skip to content

Commit a372363

Browse files
saitholunny
authored andcommitted
Adds side-by-side diff for images (#6784)
* Adds side-by-side diff for images Signed-off-by: Mario Lubenka <[email protected]> * Explain blank imports Signed-off-by: Mario Lubenka <[email protected]> * Use complete word for width and height labels on image compare Signed-off-by: Mario Lubenka <[email protected]> * Update index.css from master Signed-off-by: Mario Lubenka <[email protected]> * Moves ImageInfo to git commit file Signed-off-by: Mario Lubenka <[email protected]> * Assign ImageInfo function for template and sets correct target for BeforeSourcePath Signed-off-by: Mario Lubenka <[email protected]> * Adds missing comment Signed-off-by: Mario Lubenka <[email protected]> * Return error if ImageInfo failed Signed-off-by: Mario Lubenka <[email protected]> * Avoid template panic when ImageInfo failed for some reason Signed-off-by: Mario Lubenka <[email protected]> * Show file size on image diff Signed-off-by: Mario Lubenka <[email protected]> * Removes unused helper function Signed-off-by: Mario Lubenka <[email protected]> * Reverts copyright year change Signed-off-by: Mario Lubenka <[email protected]> * Close file reader Signed-off-by: Mario Lubenka <[email protected]> * Update commit.go Sets correct data key * Moves reader.Close() up a few lines * Updates index.css * Updates CSS file Signed-off-by: Mario Lubenka <[email protected]> * Transfers adjustments for image compare to compare.go file Signed-off-by: Mario Lubenka <[email protected]> * Adjusts variable name Signed-off-by: Mario Lubenka <[email protected]> * Apply lesshint recommendations Signed-off-by: Mario Lubenka <[email protected]> * Do not show old image on image compare if it is not in index of base commit Signed-off-by: Mario Lubenka <[email protected]> * Change file size text Signed-off-by: Mario Lubenka <[email protected]>
1 parent a5f87fe commit a372363

File tree

10 files changed

+262
-15
lines changed

10 files changed

+262
-15
lines changed

modules/git/commit.go

+49
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import (
1010
"bytes"
1111
"container/list"
1212
"fmt"
13+
"image"
14+
"image/color"
15+
_ "image/gif" // for processing gif images
16+
_ "image/jpeg" // for processing jpeg images
17+
_ "image/png" // for processing png images
1318
"io"
1419
"net/http"
1520
"strconv"
@@ -158,6 +163,43 @@ func (c *Commit) IsImageFile(name string) bool {
158163
return isImage
159164
}
160165

166+
// ImageMetaData represents metadata of an image file
167+
type ImageMetaData struct {
168+
ColorModel color.Model
169+
Width int
170+
Height int
171+
ByteSize int64
172+
}
173+
174+
// ImageInfo returns information about the dimensions of an image
175+
func (c *Commit) ImageInfo(name string) (*ImageMetaData, error) {
176+
if !c.IsImageFile(name) {
177+
return nil, nil
178+
}
179+
180+
blob, err := c.GetBlobByPath(name)
181+
if err != nil {
182+
return nil, err
183+
}
184+
reader, err := blob.DataAsync()
185+
if err != nil {
186+
return nil, err
187+
}
188+
defer reader.Close()
189+
config, _, err := image.DecodeConfig(reader)
190+
if err != nil {
191+
return nil, err
192+
}
193+
194+
metadata := ImageMetaData{
195+
ColorModel: config.ColorModel,
196+
Width: config.Width,
197+
Height: config.Height,
198+
ByteSize: blob.Size(),
199+
}
200+
return &metadata, nil
201+
}
202+
161203
// GetCommitByPath return the commit of relative path object.
162204
func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) {
163205
return c.repo.getCommitByPathWithID(c.ID, relpath)
@@ -310,6 +352,13 @@ func (c *Commit) FileChangedSinceCommit(filename, pastCommit string) (bool, erro
310352
return c.repo.FileChangedBetweenCommits(filename, pastCommit, c.ID.String())
311353
}
312354

355+
// HasFile returns true if the file given exists on this commit
356+
// This does only mean it's there - it does not mean the file was changed during the commit.
357+
func (c *Commit) HasFile(filename string) (bool, error) {
358+
result, err := c.repo.LsFiles(filename)
359+
return result[0] == filename, err
360+
}
361+
313362
// GetSubModules get all the sub modules of current revision git tree
314363
func (c *Commit) GetSubModules() (*ObjectCache, error) {
315364
if c.submoduleCache != nil {

options/locale/locale_en-US.ini

+5
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,11 @@ diff.whitespace_ignore_at_eol = Ignore changes in whitespace at EOL
13581358
diff.stats_desc = <strong> %d changed files</strong> with <strong>%d additions</strong> and <strong>%d deletions</strong>
13591359
diff.bin = BIN
13601360
diff.view_file = View File
1361+
diff.file_before = Before
1362+
diff.file_after = After
1363+
diff.file_image_width = Width
1364+
diff.file_image_height = Height
1365+
diff.file_byte_size = Size
13611366
diff.file_suppressed = File diff suppressed because it is too large
13621367
diff.too_many_files = Some files were not shown because too many files changed in this diff
13631368
diff.comment.placeholder = Leave a comment

public/css/index.css

+11
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ a{cursor:pointer}
140140
.ui .migrate{color:#888!important;opacity:.5}
141141
.ui .migrate a{color:#444!important}
142142
.ui .migrate a:hover{color:#000!important}
143+
.ui .border{border:1px solid}
144+
.ui .border.red{border-color:#d95c5c!important}
145+
.ui .border.blue{border-color:#428bca!important}
146+
.ui .border.black{border-color:#444}
147+
.ui .border.grey{border-color:#767676!important}
148+
.ui .border.light.grey{border-color:#888!important}
149+
.ui .border.green{border-color:#6cc644!important}
150+
.ui .border.purple{border-color:#6e5494!important}
151+
.ui .border.yellow{border-color:#fbbd08!important}
152+
.ui .border.gold{border-color:#a1882b!important}
143153
.ui .branch-tag-choice{line-height:20px}
144154
@media only screen and (max-width:767px){.ui.pagination.menu .item.navigation span.navigation_label,.ui.pagination.menu .item:not(.active):not(.navigation){display:none}
145155
}
@@ -670,6 +680,7 @@ footer .ui.left,footer .ui.right{line-height:40px}
670680
.repository .diff-file-box .code-diff td{padding:0 0 0 10px!important;border-top:0}
671681
.repository .diff-file-box .code-diff .lines-num{border-color:#d4d4d5;border-right-width:1px;border-right-style:solid;padding:0 5px!important}
672682
.repository .diff-file-box .code-diff tbody tr td.halfwidth{width:49%}
683+
.repository .diff-file-box .code-diff tbody tr td.center{text-align:center}
673684
.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}
674685
.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}
675686
.repository .diff-file-box .code-diff tbody tr [data-line-num]::before{content:attr(data-line-num);text-align:right}

public/less/_base.less

+39
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,45 @@ code,
600600
}
601601
}
602602

603+
.border {
604+
border: 1px solid;
605+
&.red {
606+
border-color: #d95c5c !important;
607+
}
608+
609+
&.blue {
610+
border-color: #428bca !important;
611+
}
612+
613+
&.black {
614+
border-color: #444444;
615+
}
616+
617+
&.grey {
618+
border-color: #767676 !important;
619+
}
620+
621+
&.light.grey {
622+
border-color: #888888 !important;
623+
}
624+
625+
&.green {
626+
border-color: #6cc644 !important;
627+
}
628+
629+
&.purple {
630+
border-color: #6e5494 !important;
631+
}
632+
633+
&.yellow {
634+
border-color: #fbbd08 !important;
635+
}
636+
637+
&.gold {
638+
border-color: #a1882b !important;
639+
}
640+
}
641+
603642
.branch-tag-choice {
604643
line-height: 20px;
605644
}

public/less/_repository.less

+4
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,10 @@
13641364
width: 49%;
13651365
}
13661366

1367+
td.center {
1368+
text-align: center;
1369+
}
1370+
13671371
.removed-code {
13681372
background-color: #ff9999;
13691373
}

routers/repo/commit.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,23 @@ func Diff(ctx *context.Context) {
240240
ctx.Data["Username"] = userName
241241
ctx.Data["Reponame"] = repoName
242242
ctx.Data["IsImageFile"] = commit.IsImageFile
243+
ctx.Data["ImageInfo"] = func(name string) *git.ImageMetaData {
244+
result, err := commit.ImageInfo(name)
245+
if err != nil {
246+
log.Error("ImageInfo failed: %v", err)
247+
return nil
248+
}
249+
return result
250+
}
251+
ctx.Data["ImageInfoBase"] = ctx.Data["ImageInfo"]
252+
if commit.ParentCount() > 0 {
253+
parentCommit, err := ctx.Repo.GitRepo.GetCommit(parents[0])
254+
if err != nil {
255+
ctx.NotFound("GetParentCommit", err)
256+
return
257+
}
258+
ctx.Data["ImageInfo"] = parentCommit.ImageInfo
259+
}
243260
ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
244261
ctx.Data["Commit"] = commit
245262
ctx.Data["Verification"] = models.ParseCommitWithSignature(commit)
@@ -248,6 +265,7 @@ func Diff(ctx *context.Context) {
248265
ctx.Data["Parents"] = parents
249266
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
250267
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", commitID)
268+
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", commitID)
251269

252270
note := &git.Note{}
253271
err = git.GetNote(ctx.Repo.GitRepo, commitID, note)
@@ -259,8 +277,8 @@ func Diff(ctx *context.Context) {
259277

260278
if commit.ParentCount() > 0 {
261279
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", parents[0])
280+
ctx.Data["BeforeRawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", parents[0])
262281
}
263-
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", commitID)
264282
ctx.Data["BranchName"], err = commit.GetBranchName()
265283
if err != nil {
266284
ctx.ServerError("commit.GetBranchName", err)

routers/repo/compare.go

+53-1
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,26 @@ func PrepareCompareDiff(
247247
return false
248248
}
249249

250+
baseGitRepo := ctx.Repo.GitRepo
251+
baseCommitID := baseBranch
252+
if ctx.Data["BaseIsCommit"] == false {
253+
if ctx.Data["BaseIsTag"] == true {
254+
baseCommitID, err = baseGitRepo.GetTagCommitID(baseBranch)
255+
} else {
256+
baseCommitID, err = baseGitRepo.GetBranchCommitID(baseBranch)
257+
}
258+
if err != nil {
259+
ctx.ServerError("GetRefCommitID", err)
260+
return false
261+
}
262+
}
263+
264+
baseCommit, err := baseGitRepo.GetCommit(baseCommitID)
265+
if err != nil {
266+
ctx.ServerError("GetCommit", err)
267+
return false
268+
}
269+
250270
compareInfo.Commits = models.ValidateCommitsWithEmails(compareInfo.Commits)
251271
compareInfo.Commits = models.ParseCommitsWithSignature(compareInfo.Commits)
252272
compareInfo.Commits = models.ParseCommitsWithStatus(compareInfo.Commits, headRepo)
@@ -272,11 +292,43 @@ func PrepareCompareDiff(
272292
ctx.Data["Username"] = headUser.Name
273293
ctx.Data["Reponame"] = headRepo.Name
274294
ctx.Data["IsImageFile"] = headCommit.IsImageFile
295+
ctx.Data["ImageInfo"] = func(name string) *git.ImageMetaData {
296+
result, err := headCommit.ImageInfo(name)
297+
if err != nil {
298+
log.Error("ImageInfo failed: %v", err)
299+
return nil
300+
}
301+
return result
302+
}
303+
ctx.Data["FileExistsInBaseCommit"] = func(filename string) bool {
304+
result, err := baseCommit.HasFile(filename)
305+
if err != nil {
306+
log.Error(
307+
"Error while checking if file \"%s\" exists in base commit \"%s\" (repo: %s): %v",
308+
filename,
309+
baseCommit,
310+
baseGitRepo.Path,
311+
err)
312+
return false
313+
}
314+
return result
315+
}
316+
ctx.Data["ImageInfoBase"] = func(name string) *git.ImageMetaData {
317+
result, err := baseCommit.ImageInfo(name)
318+
if err != nil {
319+
log.Error("ImageInfo failed: %v", err)
320+
return nil
321+
}
322+
return result
323+
}
275324

276325
headTarget := path.Join(headUser.Name, repo.Name)
326+
baseTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
277327
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", headCommitID)
278-
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", compareInfo.MergeBase)
279328
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", "commit", headCommitID)
329+
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(baseTarget, "src", "commit", baseCommitID)
330+
ctx.Data["BeforeRawPath"] = setting.AppSubURL + "/" + path.Join(baseTarget, "raw", "commit", baseCommitID)
331+
280332
return false
281333
}
282334

routers/repo/pull.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -535,16 +535,41 @@ func ViewPullFiles(ctx *context.Context) {
535535
ctx.Data["Diff"] = diff
536536
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
537537

538+
baseCommit, err := ctx.Repo.GitRepo.GetCommit(startCommitID)
539+
if err != nil {
540+
ctx.ServerError("GetCommit", err)
541+
return
542+
}
538543
commit, err := gitRepo.GetCommit(endCommitID)
539544
if err != nil {
540545
ctx.ServerError("GetCommit", err)
541546
return
542547
}
543548

544549
ctx.Data["IsImageFile"] = commit.IsImageFile
550+
ctx.Data["ImageInfoBase"] = func(name string) *git.ImageMetaData {
551+
result, err := baseCommit.ImageInfo(name)
552+
if err != nil {
553+
log.Error("ImageInfo failed: %v", err)
554+
return nil
555+
}
556+
return result
557+
}
558+
ctx.Data["ImageInfo"] = func(name string) *git.ImageMetaData {
559+
result, err := commit.ImageInfo(name)
560+
if err != nil {
561+
log.Error("ImageInfo failed: %v", err)
562+
return nil
563+
}
564+
return result
565+
}
566+
567+
baseTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
545568
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", endCommitID)
546-
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", startCommitID)
547569
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", "commit", endCommitID)
570+
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(baseTarget, "src", "commit", startCommitID)
571+
ctx.Data["BeforeRawPath"] = setting.AppSubURL + "/" + path.Join(baseTarget, "raw", "commit", startCommitID)
572+
548573
ctx.Data["RequireHighlightJS"] = true
549574
ctx.Data["RequireTribute"] = true
550575
if ctx.Data["Assignees"], err = ctx.Repo.Repository.GetAssignees(); err != nil {

templates/repo/diff/box.tmpl

+10-12
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,12 @@
107107
<div class="ui attached unstackable table segment">
108108
{{if ne $file.Type 4}}
109109
{{$isImage := (call $.IsImageFile $file.Name)}}
110-
{{if and $isImage}}
111-
<div class="center">
112-
<img src="{{$.RawPath}}/{{EscapePound .Name}}">
113-
</div>
114-
{{else}}
115-
<div class="file-body file-code code-view code-diff {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}}">
116-
<table>
117-
<tbody>
110+
<div class="file-body file-code code-view code-diff {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}}">
111+
<table>
112+
<tbody>
113+
{{if $isImage}}
114+
{{template "repo/diff/image_diff" dict "file" . "root" $}}
115+
{{else}}
118116
{{if $.IsSplitStyle}}
119117
{{$highlightClass := $file.GetHighlightClass}}
120118
{{range $j, $section := $file.Sections}}
@@ -164,10 +162,10 @@
164162
{{else}}
165163
{{template "repo/diff/section_unified" dict "file" . "root" $}}
166164
{{end}}
167-
</tbody>
168-
</table>
169-
</div>
170-
{{end}}
165+
{{end}}
166+
</tbody>
167+
</table>
168+
</div>
171169
{{end}}
172170
</div>
173171
</div>

0 commit comments

Comments
 (0)