git ssb

30+

cel / git-ssb-web



Commit 7ee90b10d38650077e6b52b7008b751f3c63a856

Render diffs for commits

Close %2s6DPrwQ/RzR22MPYWGxDasdcGwn8hha32kkrtH4+7M=.sha256
Charles Lehner committed on 4/1/2016, 1:50:54 AM
Parent: 2989c2f89ee6bdf43744b5a88ca904afd86b92a3

Files changed

index.jschanged
package.jsonchanged
static/styles.csschanged
index.jsView
@@ -19,8 +19,9 @@
1919 var paramap = require('pull-paramap')
2020 var gitPack = require('pull-git-pack')
2121 var Mentions = require('ssb-mentions')
2222 var Highlight = require('highlight.js')
23+var JsDiff = require('diff')
2324
2425 // render links to git objects and ssb objects
2526 var blockRenderer = new marked.Renderer()
2627 blockRenderer.urltransform = function (url) {
@@ -231,8 +232,23 @@
231232 })
232233 }
233234 }
234235
236+function readObjectString(obj, cb) {
237+ pull(obj.read, pull.collect(function (err, bufs) {
238+ if (err) return cb(err)
239+ cb(null, Buffer.concat(bufs, obj.length).toString('utf8'))
240+ }))
241+}
242+
243+function getRepoObjectString(repo, id, cb) {
244+ if (!id) return cb(null, '')
245+ repo.getObjectFromAny(id, function (err, obj) {
246+ if (err) return cb(err)
247+ readObjectString(obj, cb)
248+ })
249+}
250+
235251 function compareMsgs(a, b) {
236252 return (a.value.timestamp - b.value.timestamp) || (a.key - b.key)
237253 }
238254
@@ -633,15 +649,15 @@
633649
634650 function renderObjectData(obj, filename, repo) {
635651 var ext = (/\.([^.]+)$/.exec(filename) || [,filename])[1]
636652 return readOnce(function (cb) {
637- pull(obj.read, pull.collect(function (err, bufs) {
653+ readObjectString(obj, function (err, buf) {
654+ buf = buf.toString('utf8')
638655 if (err) return cb(err)
639- var buf = Buffer.concat(bufs, obj.length).toString('utf8')
640656 cb(null, (ext == 'md' || ext == 'markdown')
641657 ? markdown(buf, repo)
642658 : '<pre>' + highlight(buf, ext) + '</pre>')
643- }))
659+ })
644660 })
645661 }
646662
647663 /* Feed */
@@ -1171,9 +1187,9 @@
11711187 return 'Parent: ' + link([repo.id, 'commit', id], id)
11721188 }).join('<br>') + '</p>' +
11731189 (commit.tree ? 'Tree: ' + link(treePath) : 'No tree') +
11741190 '</section>'),
1175- renderDiffStat(repo, commit.tree, commit.parents)
1191+ renderDiffStat(repo, commit.id, commit.parents)
11761192 ]))
11771193 })
11781194 })
11791195 ]))
@@ -1184,39 +1200,83 @@
11841200 function renderDiffStat(repo, id, parentIds) {
11851201 if (parentIds.length == 0) parentIds = [null]
11861202 var lastI = parentIds.length
11871203 var oldTree = parentIds[0]
1204+ var changedFiles = []
11881205 return cat([
11891206 pull.once('<section><h3>Files changed</h3>'),
11901207 pull(
11911208 repo.diffTrees(parentIds.concat(id), true),
11921209 pull.map(function (item) {
1193- var filename = escapeHTML(item.path.join('/'))
1210+ var filename = item.filename = escapeHTML(item.path.join('/'))
11941211 var oldId = item.id && item.id[0]
11951212 var newId = item.id && item.id[lastI]
1196- var oldMode = item.mode && item.mode[0].toString(8)
1197- var newMode = item.mode && item.mode[lastI].toString(8)
1213+ var oldMode = item.mode && item.mode[0]
1214+ var newMode = item.mode && item.mode[lastI]
11981215 var action =
1199- !oldId && newId ? 'new' :
1216+ !oldId && newId ? 'added' :
12001217 oldId && !newId ? 'deleted' :
12011218 oldMode != newMode ?
1202- 'changed mode from ' + oldMode + ' to ' + newMode :
1203- ''
1204- var newLink = newId ?
1205- link([repo.id, 'blob', id].concat(item.path), 'new') : ''
1206- var oldLink = oldId ?
1207- link([repo.id, 'blob', oldTree].concat(item.path), 'old') : ''
1208- var links = [oldLink, newLink]
1209- var fileLink = newLink || oldLink ? filename :
1210- link([repo.id, 'blob', id].concat(item.path), filename)
1211- return [fileLink, action, links.filter(Boolean).join(', ')]
1219+ 'changed mode from ' + oldMode.toString(8) +
1220+ ' to ' + newMode.toString(8) :
1221+ 'changed'
1222+ if (item.id)
1223+ changedFiles.push(item)
1224+ var fileHref = item.id ?
1225+ '#' + encodeURIComponent(item.path.join('/')) :
1226+ encodeLink([repo.id, 'blob', id].concat(item.path))
1227+ return ['<a href="' + fileHref + '">' + filename + '</a>', action]
12121228 }),
12131229 table()
12141230 ),
1231+ pull(
1232+ pull.values(changedFiles),
1233+ paramap(function (item, cb) {
1234+ var done = multicb({ pluck: 1, spread: true })
1235+ getRepoObjectString(repo, item.id[0], done())
1236+ getRepoObjectString(repo, item.id[lastI], done())
1237+ done(function (err, strOld, strNew) {
1238+ if (err) return cb(err)
1239+ var commitId = item.id[lastI] ? id : parentIds.filter(Boolean)[0]
1240+ cb(null, htmlLineDiff(item.filename, item.filename,
1241+ strOld, strNew,
1242+ encodeLink([repo.id, 'blob', commitId].concat(item.path))))
1243+ })
1244+ }, 4)
1245+ ),
12151246 pull.once('</section>'),
12161247 ])
12171248 }
12181249
1250+ function htmlLineDiff(filename, anchor, oldStr, newStr, blobHref, rawHref) {
1251+ var diff = JsDiff.structuredPatch('', '', oldStr, newStr)
1252+ var groups = diff.hunks.map(function (hunk) {
1253+ var oldLine = hunk.oldStart
1254+ var newLine = hunk.newStart
1255+ var header = '<tr class="diff-hunk-header"><td colspan=2></td><td>' +
1256+ '@@ -' + oldLine + ',' + hunk.oldLines + ' ' +
1257+ '+' + newLine + ',' + hunk.newLines + ' @@' +
1258+ '</td></tr>'
1259+ return [header].concat(hunk.lines.map(function (line) {
1260+ var s = line[0]
1261+ if (s == '\\') return
1262+ var html = escapeHTML(line)
1263+ var trClass = s == '+' ? 'diff-new' : s == '-' ? 'diff-old' : ''
1264+ return '<tr' + (trClass ? ' class="' + trClass + '"' : '') + '>' +
1265+ '<td class="diff-linenum">' + (s == '+' ? '' : oldLine++) + '</td>' +
1266+ '<td class="diff-linenum">' + (s == '-' ? '' : newLine++) + '</td>' +
1267+ '<td class="diff-text">' + html + '</td></tr>'
1268+ }))
1269+ })
1270+ return '<pre><table class="diff">' +
1271+ '<tr><th colspan=3><a name="' + anchor + '">' + filename + '</a>' +
1272+ '<span class="right-bar">' +
1273+ '<a href="' + blobHref + '">View</a> ' +
1274+ '</span></th></tr>' +
1275+ [].concat.apply([], groups).join('') +
1276+ '</table></pre>'
1277+ }
1278+
12191279 /* An unknown message linking to a repo */
12201280
12211281 function serveRepoSomething(req, repo, id, msg, path) {
12221282 return renderRepoPage(repo, null, null,
package.jsonView
@@ -4,8 +4,9 @@
44 "description": "web server for browsing git repos on ssb",
55 "bin": "server.js",
66 "dependencies": {
77 "asyncmemo": "^0.1.0",
8+ "diff": "^2.2.2",
89 "highlight.js": "^9.2.0",
910 "multicb": "^1.2.1",
1011 "pull-cat": "^1.1.8",
1112 "pull-git-pack": "^0.1.2",
static/styles.cssView
@@ -316,4 +316,44 @@
316316 background-color: #ff6;
317317 padding: .25ex .5ex;
318318 margin: -.25ex -.5ex;
319319 }
320+
321+/* diffs */
322+
323+.diff {
324+ border-collapse: collapse;
325+ text-align: left;
326+}
327+
328+.diff th {
329+ font-family: sans-serif;
330+ font-size: 1.25em;
331+ padding-bottom: .5ex;
332+}
333+
334+.diff-hunk-header {
335+ background-color: #eee;
336+ color: grey;
337+}
338+
339+.diff td {
340+ padding: 0 1ex 0;
341+}
342+
343+.diff tr:hover {
344+ background-color: #fff8d2;
345+}
346+
347+.diff-linenum {
348+ text-align: right;
349+ color: grey;
350+ border-right: 1px solid rgba(0, 0, 0, 0.06);
351+}
352+
353+.diff-text {
354+ white-space: pre;
355+ width: 100%;
356+}
357+
358+.diff-old { background-color: #ffe2dd; }
359+.diff-new { background-color: #d1ffd6; }

Built with git-ssb-web