diff --git a/src/main/scala/gitbucket/core/util/JGitUtil.scala b/src/main/scala/gitbucket/core/util/JGitUtil.scala index 69b8c6f..096db1e 100644 --- a/src/main/scala/gitbucket/core/util/JGitUtil.scala +++ b/src/main/scala/gitbucket/core/util/JGitUtil.scala @@ -100,7 +100,8 @@ def isDifferentFromAuthor: Boolean = authorName != committerName || authorEmailAddress != committerEmailAddress } - case class DiffInfo(changeType: ChangeType, oldPath: String, newPath: String, oldContent: Option[String], newContent: Option[String]) + case class DiffInfo(changeType: ChangeType, oldPath: String, newPath: String, oldContent: Option[String], newContent: Option[String], + oldIsImage: Boolean, newIsImage: Boolean, oldObjectId: Option[String], newObjectId: Option[String]) /** * The file content data for the file content view of the repository viewer. @@ -459,11 +460,13 @@ treeWalk.addTree(revCommit.getTree) val buffer = new scala.collection.mutable.ListBuffer[DiffInfo]() while(treeWalk.next){ + val newIsImage = FileUtil.isImage(treeWalk.getPathString) buffer.append((if(!fetchContent){ - DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None, None) + DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None, None, false, newIsImage, None, Option(treeWalk.getObjectId(0)).map(_.name)) } else { DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None, - JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).filter(FileUtil.isText).map(convertFromByteArray)) + JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).filter(FileUtil.isText).map(convertFromByteArray), + false, newIsImage, None, Option(treeWalk.getObjectId(0)).map(_.name)) })) } (buffer.toList, None) @@ -483,12 +486,15 @@ import scala.collection.JavaConverters._ git.getRepository.getConfig.setString("diff", null, "renames", "copies") git.diff.setNewTree(newTreeIter).setOldTree(oldTreeIter).call.asScala.map { diff => - if(!fetchContent || FileUtil.isImage(diff.getOldPath) || FileUtil.isImage(diff.getNewPath)){ - DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, None, None) + val oldIsImage = FileUtil.isImage(diff.getOldPath) + val newIsImage = FileUtil.isImage(diff.getNewPath) + if(!fetchContent || oldIsImage || newIsImage){ + DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, None, None, oldIsImage, newIsImage, Option(diff.getOldId).map(_.name), Option(diff.getNewId).map(_.name)) } else { DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, JGitUtil.getContentFromId(git, diff.getOldId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray), - JGitUtil.getContentFromId(git, diff.getNewId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray)) + JGitUtil.getContentFromId(git, diff.getNewId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray), + oldIsImage, newIsImage, Option(diff.getOldId).map(_.name), Option(diff.getNewId).map(_.name)) } }.toList } diff --git a/src/main/twirl/gitbucket/core/helper/diff.scala.html b/src/main/twirl/gitbucket/core/helper/diff.scala.html index 441884e..42b87c0 100644 --- a/src/main/twirl/gitbucket/core/helper/diff.scala.html +++ b/src/main/twirl/gitbucket/core/helper/diff.scala.html @@ -90,13 +90,32 @@ - @if(diff.newContent != None || diff.oldContent != None){ + @if(diff.oldObjectId == diff.newObjectId){ +
File renamed without changes
+ } else { @if(diff.newContent != None || diff.oldContent != None){
+ } else { @if(diff.newIsImage || diff.oldIsImage){ +
+ @if(oldCommitId.isDefined && diff.oldIsImage){ +
+ } else { + @if(diff.changeType != ChangeType.ADD){ + Not supported + } + } + @if(newCommitId.isDefined && diff.newIsImage){ +
+ } else { + @if(diff.changeType != ChangeType.DELETE){ + Not supported + } + } +
} else { Not supported - } + } } } diff --git a/src/main/webapp/assets/common/css/gitbucket.css b/src/main/webapp/assets/common/css/gitbucket.css index 925897b..31ac5de 100644 --- a/src/main/webapp/assets/common/css/gitbucket.css +++ b/src/main/webapp/assets/common/css/gitbucket.css @@ -1151,6 +1151,149 @@ white-space: nowrap; letter-spacing: 0px; } +.diff-same{ + background: #DDD; + color: #BBB; + font-size: 16px; + padding: 20px; + font-weight: bold; + text-align: center; +} +/* ------- for imageDiff */ +.diff-image-frame{ + display: table-cell; + *float: left; /* for ie7 */ + vertical-align: middle; + padding: 20px; + background-color: #eee; +} +.diff-image-frame.diff-old{ + padding-right: 2px; +} +.diff-image-frame.diff-new{ + padding-left: 2px; +} +.diff-image-frame .diff-meta{ + margin-top: 12px; + color: #999; + font-family: Helvetica,arial,freesans,clean,sans-serif; + font-size: 12px; +} +.diff-image-frame.diff-old .diff-meta .diff{ + color: #bd2c00; +} +.diff-image-frame.diff-new .diff-meta .diff{ + color: #55a532; +} +.diff-image-frame img{ + max-height: 410px; + max-width: 410px; + background: url(../images/checker.png); +} +.diff-image-render.diff2up{ + width:100%; + text-align: center; + display: table; +} +.diff-image-frame.diff-new img{ + border: 1px solid #55a532; +} +.diff-image-frame.diff-old img{ + border: 1px solid #bd2c00; +} +.diff-image-stack{ + position: relative; + background: #EEE; + padding-top: 20px; +} +.diff-image-stack .diff-old, +.diff-image-stack .diff-new{ + position:absolute; + overflow: hidden; + margin:0 20px; +} +.diff-image-stack img { + max-width: none; + background: url(../images/checker.png); +} +.diff-image-stack .diff-new{ + border: 1px solid #55a532; + background: #EEE; +} +.diff-image-stack .diff-old{ + border: 1px solid #bd2c00; +} +.diff-swipe-handle{ + position:absolute; + margin-left: 325px; + left: 100px; +} +.diff-silde-bar{ + width: 200px; + position: absolute; + left: 325px; + margin: 6px 0 0 7px; + box-sizing: border-box; + border: 1px solid gray; + height: 8px; +} +.image-diff-tools{ + text-align: center; + padding: 4px; + background: #f7f7f7; +} +.image-diff-tools{ + font-family: 'Helvetica Neue', Helvetica, arial, freesans, clean, sans-serif; + font-size: 12px; + background: #f7f7f7; + margin: 0; + text-align: center; +} +.image-diff-tools li{ + background: none; + display: inline; + cursor: pointer; + border-right: 1px solid #ccc; + padding: 0 5px; + position: relative; + color: #666; +} +.image-diff-tools li:last-child{ + border-right: none; +} +.image-diff-tools li.active { + font-weight: bold; +} +.no-canvas .need-canvas{ + display: none; +} +.diff-image-stack.swipe .diff-new{ + border-right: 1px solid #888; +} +.diff-image-stack.swipe .diff-swipe-handle{ + margin-left: 15px; + left: 410px; +} +.diff-image-stack.swipe .diff-silde-bar{ + display: none; +} + +.diff-image-stack.onion .diff-silde-bar{ + background: -ms-linear-gradient(left, #bd2c00 0%,#55a532 100%); /* IE10+ */ + background: linear-gradient(to right, #bd2c00 0%,#55a532 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#bd2c00', endColorstr='#55a532',GradientType=1 ); /* IE6-9 */ +} + +.diff-image-stack.blink .diff-silde-bar{ + border-style: dotted; + background-image: linear-gradient(to right, #bd2c00, #bd2c00 50%, #55a532 50%, #55a532 100%); + background-size: 2px 2px; +} + +.diff-image-stack.difference { + padding-bottom: 18px; + text-align: center; +} /****************************************************************************/ /* Repository Settings */ /****************************************************************************/ diff --git a/src/main/webapp/assets/common/images/checker.png b/src/main/webapp/assets/common/images/checker.png new file mode 100644 index 0000000..07e2115 --- /dev/null +++ b/src/main/webapp/assets/common/images/checker.png Binary files differ diff --git a/src/main/webapp/assets/common/js/gitbucket.js b/src/main/webapp/assets/common/js/gitbucket.js index e10b2fb..47ec3a9 100644 --- a/src/main/webapp/assets/common/js/gitbucket.js +++ b/src/main/webapp/assets/common/js/gitbucket.js @@ -334,3 +334,211 @@ return ret; } }); +/****************************************************************************/ +/* Diff */ +/****************************************************************************/ +// add naturalWidth and naturalHeight for ie 8 +function setNatural(img) { + if(typeof img.naturalWidth == 'undefined'){ + var tmp = new Image(); + tmp.src = img.src; + img.naturalWidth = tmp.width; + img.naturalHeight = tmp.height; + } +} +/** + * onload handler + * @param img + */ +function onLoadedDiffImages(img){ + setNatural(img); + img = $(img); + img.show(); + var tb = img.parents(".diff-image-render"); + // Find images. If the image has not loaded yet, value is undefined. + var old = tb.find(".diff-old img.diff-image:visible")[0]; + var neo = tb.find(".diff-new img.diff-image:visible")[0]; + imageDiff.appendImageMeta(tb, old, neo); + if(old && neo){ + imageDiff.createToolSelector(old, neo).appendTo(tb.parent()); + } +} +var imageDiff ={ + /** append image meta div after image nodes. + * @param tb
+ * @param old ||undefined + * @param neo ||undefined + */ + appendImageMeta:function(tb, old, neo){ + old = old || {}; + neo = neo || {}; + tb.find(".diff-meta").remove(); + // before loaded, image is not visible. + tb.find("img.diff-image:visible").each(function(){ + var div = $('

W: | W:

'); + div.find('.w').text(this.naturalWidth+"px").toggleClass("diff", old.naturalWidth != neo.naturalWidth); + div.find('.h').text(this.naturalHeight+"px").toggleClass("diff", old.naturalHeight != neo.naturalHeight); + div.appendTo(this.parentNode); + }); + }, + /** check this browser can use canvas tag. + */ + hasCanvasSupport:function(){ + if(!this.hasCanvasSupport.hasOwnProperty('resultCache')){ + this.hasCanvasSupport.resultCache = (typeof $('')[0].getContext)=='function'; + } + return this.hasCanvasSupport.resultCache; + }, + /** create toolbar + * @param old + * @param neo + * @return jQuery(