New application: pygit - web interface for Git.

portnov [2008-06-10 04:28:11]
New application: pygit - web interface for Git.
Filename
media/css/main.css
mgmt/rights.py
pygit/__init__.py
pygit/models.py
pygit/pygit.py
pygit/views.py
templates/comments.html
templates/git_blame.html
templates/git_diff.html
templates/git_tree.html
templates/repo.html
templates/repos.html
urls.py
diff --git a/media/css/main.css b/media/css/main.css
index b1a7826..1332236 100644
--- a/media/css/main.css
+++ b/media/css/main.css
@@ -1,4 +1,14 @@

+body {
+  font-family: Helvetica,Arial,sans-serif;
+}
+
+td {
+  border-right: 1px #ddd solid;
+  border-bottom: 1px #888 solid;
+  padding: 0.6ex;
+}
+
 #body {
   padding: 1ex;
 }
@@ -47,6 +57,10 @@ ul.menu li {
   margin-right: 1ex;
 }

+.block-last_comments ul {
+  padding-left: 1em;
+}
+
 .comment {
   border-top: 1px #888 solid;
   margin-bottom: 1em;
diff --git a/mgmt/rights.py b/mgmt/rights.py
index 2e8de01..567f774 100644
--- a/mgmt/rights.py
+++ b/mgmt/rights.py
@@ -26,8 +26,6 @@ def can_change_bug_state(user,bug,new_state):
 def can(user,action,object=None,target=None):
   if action in ['comment','send bug']:
     return user.is_authenticated()
-#   if action in ['create', 'edit', 'delete']:
-#     return user.is_authenticated() and user.has_perm('mgmt.%s.%s' % (object,action))
   if action == 'edit':
     if object.__class__.__name__ == 'Project':
       return user in object.admins.iterator()
diff --git a/pygit/__init__.py b/pygit/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pygit/models.py b/pygit/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/pygit/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/pygit/pygit.py b/pygit/pygit.py
new file mode 100644
index 0000000..8d2cca9
--- /dev/null
+++ b/pygit/pygit.py
@@ -0,0 +1,83 @@
+
+from os.path import basename
+from git import *
+
+REPOS = ['/home/portnov/www/projects']
+
+rp = None
+curr_path = None
+
+def open_repo(rid):
+  global curr_path,rp
+  path = REPOS[int(rid)]
+  if rp and curr_path==path:
+    return rp
+  curr_path = path
+  rp = Repo(path)
+  return rp
+
+def files(tree):
+  contents = tree.contents
+  return [(blob.id,blob.name) for blob in contents if blob.__class__.__name__=='Blob']
+
+def dirs(tree):
+  contents = tree.contents
+  return [(tree.id,tree.name) for tree in contents if tree.__class__.__name__=='Tree']
+
+def repos():
+  return [(i,basename(REPOS[i])) for i in range(len(REPOS))]
+
+def description(rid):
+  r = open_repo(rid)
+  return r.description
+
+def branches(rid):
+  r = open_repo(rid)
+  return [b.name for b in r.branches]
+
+def date(tp):
+  y,m,d,h,M,s,_,_,_ = tp
+  return "%d.%d.%d %d:%d:%d" % (d,m,y,h,M,s)
+
+def commits(rid,branch='master'):
+  r = open_repo(rid)
+  cs = r.commits(branch)[:20]
+  return [(c.id_abbrev,date(c.authored_date),c.author.name,c.message) for c in cs]
+
+def root_tree(rid,branch='master'):
+  r = open_repo(rid)
+  t = r.tree()
+  ds = dirs(t)
+  fs = files(t)
+  return ds,fs
+
+def tree(rid,tid):
+  r = open_repo(rid)
+  t = r.tree(tid)
+  return dirs(t),files(t)
+
+def blame(rid,file,branch='master'):
+  r = open_repo(rid)
+  bl = Blob.blame(r,branch,file)
+  return [(ci.id_abbrev,ci.author.name,date(ci.authored_date),ci.message[:40],''.join(l[:40])) for ci,l in bl]
+
+def diff_all(rid,cid1,cid2=None):
+  r = open_repo(rid)
+  c1 = r.commits(cid1)[0]
+  if cid2:
+    c2 = r.commits(cid2)[0]
+  else:
+    c2 = c1.parents[0]
+  ds = Commit.diff(r,c1,c2)
+  return [(d.a_path,d.diff) for d in ds]
+
+def diff_path(rid,path,cid1,cid2=None):
+  r = open_repo(rid)
+  c1 = r.commits(cid1)[0]
+  if cid2:
+    c2 = r.commits(cid2)[0]
+  else:
+    c2 = c1.parents[0]
+  ds = Commit.diff(r,c1,c2)
+  return [(d.a_path,d.diff) for d in ds if d.a_path==path]
+
diff --git a/pygit/views.py b/pygit/views.py
new file mode 100644
index 0000000..5070df5
--- /dev/null
+++ b/pygit/views.py
@@ -0,0 +1,58 @@
+# Create your views here.
+
+import sys
+from os.path import dirname,join
+
+import pygit
+
+sys.path.append(dirname(dirname(__file__)))
+from mgmt.views import render_it
+
+def all_repos(request):
+  return render_it('repos.html',
+      {'repos': pygit.repos()},
+      request)
+
+def one_repo(request,rid,branch='master'):
+  dsc = pygit.description(rid)
+  brs = pygit.branches(rid)
+  cs  = pygit.commits(rid,branch)
+  dirs,files = pygit.root_tree(rid,branch)
+  return render_it('repo.html',
+      {'description': dsc,
+       'rid': rid,
+       'current_branch': branch,
+       'branches': brs,
+       'commits': cs,
+       'dirs': dirs,
+       'files': files},
+      request)
+
+def tree(request,rid,branch,tid,path):
+  dirs,files = pygit.tree(rid,tid)
+  return render_it('git_tree.html',
+      {'rid': rid,
+       'branch': branch,
+       'path': path,
+       'dirs': dirs,
+       'files': files},
+      request)
+
+def blame(request,rid,branch,path):
+  bl = pygit.blame(rid,path,branch)
+  return render_it('git_blame.html',
+      {'rid': rid,
+       'path': path,
+       'blame': bl},
+      request)
+
+def diff(request,rid,cid,path=None):
+  if not path:
+    ds = pygit.diff_all(rid,cid)
+  else:
+    ds = pygit.diff_path(rid,path,cid)
+  return render_it('git_diff.html',
+    {'path': path,
+     'cid': cid,
+     'diffs': ds},
+    request)
diff --git a/templates/comments.html b/templates/comments.html
index 3ba70a4..3408519 100644
--- a/templates/comments.html
+++ b/templates/comments.html
@@ -3,6 +3,7 @@
   <div class='comments'>
     {% for comment in comments %}
       <div class='comment'>
+        <a name='comment-{{comment.id}}' />
         <p class='comment-title'>{{ comment.title }} от {{ comment.author }}, {{ comment.created }}</p>
         <p>{{ comment.text }}</p>
       </div>
diff --git a/templates/git_blame.html b/templates/git_blame.html
new file mode 100644
index 0000000..3d0f7ea
--- /dev/null
+++ b/templates/git_blame.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block title %}История файла {{path}}{% endblock %}
+
+{% block main %}
+<h2>История файла {{path}}</h2>
+
+<table>
+  {% for cid,author,dt,message,line in blame %}
+  <tr>
+    <td><a href='/git/diff/{{rid}}/{{cid}}/{{path}}/'>{{cid}}</a></td>
+    <td>{{author}}</td><td>{{dt}}</td><td>{{message}}</td><td>{{forloop.counter}}</td><td>{{line}}</td>
+  </tr>
+  {% endfor %}
+</table>
+
+{% endblock %}
diff --git a/templates/git_diff.html b/templates/git_diff.html
new file mode 100644
index 0000000..0963758
--- /dev/null
+++ b/templates/git_diff.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% block title %}Diff: #{{cid}}{% if path %} {{path}}{% endif %}{% endblock %}
+
+{% block main %}
+
+<h2>Diff: #{{cid}}{% if path %} {{path}}{% endif %}</h2>
+{% for file,diff in diffs %}
+  <h3>{{file}}</h3>
+  <code><pre>
+  {{diff}}
+  </pre></code>
+{% endfor %}
+
+{% endblock %}
diff --git a/templates/git_tree.html b/templates/git_tree.html
new file mode 100644
index 0000000..82a75f1
--- /dev/null
+++ b/templates/git_tree.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block title %}Список файлов{% endblock %}
+
+{% block main %}
+<h3>Файлы:</h3>
+
+<ul>
+  {% for id,name in dirs %}
+  <li><a href='/git/repo/{{rid}}/{{branch}}/tree/{{id}}/{{path}}/{{name}}/'>{{name}}</a></li>
+  {% endfor %}
+  {% for id,name in files %}
+  <li><a href='/git/blame/{{rid}}/{{branch}}/{{path}}/{{name}}/'>{{name}}</a></li>
+  {% endfor %}
+</ul>
+
+{% endblock %}
diff --git a/templates/repo.html b/templates/repo.html
new file mode 100644
index 0000000..5825391
--- /dev/null
+++ b/templates/repo.html
@@ -0,0 +1,37 @@
+{% extends "base.html" %}
+
+{% block title %}Репозиторий: {{description}}{% endblock %}
+
+{% block main %}
+
+<h2>Репозиторий: {{description}}</h2>
+
+<div class='links'>
+  Ветви:
+  {% for branch in branches %}
+  <a href='/git/repo/{{rid}}/{{branch}}/'>{{branch}}</a>
+  {% endfor %}
+</div>
+
+<h3>Последние коммиты:</h3>
+
+<table>
+  {% for id,dt,author,message in commits %}
+    <tr>
+      <td><a href='/git/diff/{{rid}}/{{id}}/'>{{id}}</a></td><td>{{dt}}</td><td>{{author}}</td><td>{{message}}</td>
+    </tr>
+  {% endfor %}
+</table>
+
+<h3>Файлы:</h3>
+
+<ul>
+  {% for id,name in dirs %}
+  <li><a href='/git/repo/{{rid}}/{{current_branch}}/tree/{{id}}/{{name}}/'>{{name}}</a></li>
+  {% endfor %}
+  {% for id,name in files %}
+  <li><a href='/git/blame/{{rid}}/{{current_branch}}/{{name}}/'>{{name}}</a></li>
+  {% endfor %}
+</ul>
+
+{% endblock %}
diff --git a/templates/repos.html b/templates/repos.html
new file mode 100644
index 0000000..c4e278f
--- /dev/null
+++ b/templates/repos.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{%block title%}Список репозиториев Git{% endblock %}
+
+{% block main %}
+
+<h2>Список репозиториев Git</h2>
+
+<ul>
+  {% for id,name in repos %}
+  <li><a href='/git/repo/{{id}}/'>{{name}}</a></li>
+  {% endfor %}
+</ul>
+
+{% endblock %}
diff --git a/urls.py b/urls.py
index 0f8f5a4..360867a 100644
--- a/urls.py
+++ b/urls.py
@@ -12,6 +12,13 @@ urlpatterns = patterns('',
     (r'^bugs/(\d+)/edit/$', "mgmt.views.edit_bug"),
     (r'^bugs/$', "mgmt.views.all_bugs"),
     (r'^create/doc/(\d+)/$', "mgmt.views.create_document"),
+    (r'^git/$', "pygit.views.all_repos"),
+    (r'^git/repo/(\d+)/$', 'pygit.views.one_repo'),
+    (r'^git/repo/(\d+)/(\w+)/$', 'pygit.views.one_repo'),
+    (r'^git/repo/(\d+)/(\w+)/tree/(\w+)/(.*)/$', 'pygit.views.tree'),
+    (r'^git/blame/(\d+)/(\w+)/(.*)/$','pygit.views.blame'),
+    (r'^git/diff/(\d+)/(\w+)/$', 'pygit.views.diff'),
+    (r'^git/diff/(\d+)/(\w+)/(.*)/$', 'pygit.views.diff'),

     (r'^login/', login),
     (r'^logout/', logout),
ViewGit