Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Close #2: Support for binary files, includes explicit support for ima…

…ges.

Also adds support for submodule changes in the commit diff view (#1).
Refs #3.
  • Loading branch information...
commit fba5dfe485e6bc6f5088c92fe26774e05cf84306 1 parent db99842
@jonashaag authored
View
2  README.rst
@@ -20,7 +20,7 @@ Requirements
* Python 2.7
* Jinja2_
* Pygments_
-* dulwich_
+* dulwich_ (>= 0.7.1)
* Nano_ (shipped as submodule, do a ``git submodule update --init`` to fetch)
.. _Jinja2: http://jinja.pocoo.org/
View
1  TODO
@@ -3,7 +3,6 @@ definitely
* branch selector
* file history
* pagination: replace 'newer' by page numbers.
-* binary file support (commit, blob, ...)
maybe
-----
View
38 klaus.py
@@ -1,6 +1,8 @@
import os
import stat
import time
+import mimetypes
+from future_builtins import map
from functools import wraps
from dulwich.objects import Commit
@@ -92,13 +94,24 @@ def timesince(when, now=time.time):
return ', '.join('%d %s%s' % (n, unit, 's' if n != 1 else '')
for n, unit in result[:2])
+def guess_is_binary(data):
+ if isinstance(data, basestring):
+ return '\0' in data
+ else:
+ return any(map(guess_is_binary, data))
+
+def guess_is_image(filename):
+ mime, encoding = mimetypes.guess_type(filename)
+ if mime is None:
+ return False
+ return mime.startswith('image/')
+
app.jinja_env.filters['timesince'] = timesince
app.jinja_env.filters['shorten_id'] = lambda id: id[:7] if len(id) in {20, 40} else id
app.jinja_env.filters['shorten_message'] = lambda msg: msg.split('\n')[0]
app.jinja_env.filters['pygmentize'] = pygmentize
-
-def guess_is_binary(data):
- return '\0' in data
+app.jinja_env.filters['is_binary'] = guess_is_binary
+app.jinja_env.filters['is_image'] = guess_is_image
def subpaths(path):
seen = []
@@ -238,9 +251,22 @@ def get_parent_directory(self):
class RawBlob(BaseBlobView):
def view(self):
super(RawBlob, self).view()
- mime = 'application/octet-stream' \
- if guess_is_binary(self['filename']) else 'text/plain'
- self.direct_response('200 yo', {'Content-Type': mime}, self['blob'].data)
+ mime, encoding = self.get_mimetype_and_encoding()
+ headers = {'Content-Type': mime}
+ if encoding:
+ headers['Content-Encoding'] = encoding
+ self.direct_response('200 yo', headers, self['blob'].chunked)
+
+
+ def get_mimetype_and_encoding(self):
+ if guess_is_binary(self['blob'].chunked):
+ mime, encoding = mimetypes.guess_type(self['filename'])
+ if mime is None:
+ mime = 'appliication/octet-stream'
+ return mime, encoding
+ else:
+ return 'text/plain', 'utf-8'
+
@route('/:repo:/commit/:commit_id:/', 'view_commit')
class CommitView(BaseRepoView):
View
35 repo.py
@@ -90,15 +90,40 @@ def listdir(self, commit, root=None):
return ((entry.path, entry.in_path(root)) for entry in tree.iteritems())
def commit_diff(self, commit):
+ from klaus import guess_is_binary
+
if commit.parents:
parent_tree = self[commit.parents[0]].tree
else:
parent_tree = None
- stringio = StringIO()
- dulwich.patch.write_tree_diff(stringio, self.object_store,
- parent_tree, commit.tree)
- return prepare_udiff(stringio.getvalue().decode('utf-8'),
- want_header=False)
+
+ changes = self.object_store.tree_changes(parent_tree, commit.tree)
+ for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
+ try:
+ if newsha and guess_is_binary(newsha) or \
+ oldsha and guess_is_binary(oldsha):
+ yield {
+ 'is_header': False,
+ 'is_binary': True,
+ 'old_filename': oldpath,
+ 'new_filename': newpath,
+ 'chunks': [[{'line' : 'Binary diff not shown'}]]
+ }
+ continue
+ except KeyError:
+ # newsha/oldsha are probably related to submodules.
+ # Dulwich will handle that.
+ pass
+
+ stringio = StringIO()
+ dulwich.patch.write_object_diff(stringio, self.object_store,
+ (oldpath, oldmode, oldsha),
+ (newpath, newmode, newsha))
+ files = prepare_udiff(stringio.getvalue().decode('utf-8'),
+ want_header=False)
+ assert len(files) == 1
+ yield files[0]
+
def Repo(name, path, _cache={}):
repo = _cache.get(path)
View
8 static/klaus.css
@@ -94,10 +94,8 @@ footer a:hover { text-decoration: none; }
/* Blob View */
.blobview h2 span { font-size: 11pt; }
-.blobview table {
- background-color: #fefefe;
- border: 1px solid #e0e0e0;
-}
+.blobview img { max-width: 100%; padding: 1px; }
+.blobview table, .blobview img { border: 1px solid #e0e0e0; }
.blobview .linenos {
padding: 0 6px 0 6px;
border: 1px solid #e0e0e0;
@@ -128,7 +126,7 @@ footer a:hover { text-decoration: none; }
.diff .filename:before { margin-right: 0; opacity: 0.3; }
.diff table {
border: 1px solid #e0e0e0;
- background-color: #fefefe;
+ background-color: #fdfdfd;
width: 100%;
}
.diff td {
View
10 templates/view_blob.html
@@ -11,7 +11,15 @@
(<a href="{{ raw_url }}">raw</a>)
</span>
</h2>
- {{ blob.data.decode('utf-8')|pygmentize(filename=filename) }}
+ {% if blob.chunked|is_binary %}
+ {% if filename|is_image %}
+ <a href="{{ raw_url }}"><img src="{{ raw_url }}"></a>
+ {% else %}
+ <div class=binary-warning>(Binary data not shown)</div>
+ {% endif %}
+ {% else %}
+ {{ blob.data.decode('utf-8')|pygmentize(filename=filename) }}
+ {% endif %}
</div>
{% endblock %}
Please sign in to comment.
Something went wrong with that request. Please try again.