diff --git a/pylons_app/config/routing.py b/pylons_app/config/routing.py --- a/pylons_app/config/routing.py +++ b/pylons_app/config/routing.py @@ -39,6 +39,6 @@ def make_map(config): map.connect('tags_home', '/{repo_name}/tags', controller='tags') map.connect('graph_home', '/{repo_name}/graph/{revision}', controller='graph', revision='tip') map.connect('files_home', '/{repo_name}/files/{revision}/{f_path:.*}', controller='files', revision='tip', f_path='') - + map.connect('files_diff_home', '/{repo_name}/diff/{f_path:.*}', controller='files', action='diff', revision='tip', f_path='') return map diff --git a/pylons_app/controllers/files.py b/pylons_app/controllers/files.py --- a/pylons_app/controllers/files.py +++ b/pylons_app/controllers/files.py @@ -25,13 +25,29 @@ class FilesController(BaseController): c.file_history = self._get_history(repo, c.files_list, f_path) return render('files/files.html') - + def diff(self, repo_name, f_path): + hg_model = HgModel() + diff1 = request.GET.get('diff1') + diff2 = request.GET.get('diff2') + c.f_path = f_path + c.repo = hg_model.get_repo(c.repo_name) + c.changeset_1 = c.repo.get_changeset(diff1) + c.changeset_2 = c.repo.get_changeset(diff2) + + c.file_1 = c.changeset_1.get_node(f_path).content + c.file_2 = c.changeset_2.get_node(f_path).content + c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1._short) + c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2._short) + from difflib import unified_diff + d = unified_diff(c.file_1.splitlines(1), c.file_2.splitlines(1)) + c.diff = ''.join(d) + return render('files/file_diff.html') + def _get_history(self, repo, node, f_path): from vcs.nodes import NodeKind if not node.kind is NodeKind.FILE: return [] - changesets = list(node.history) - changesets.reverse() + changesets = node.history hist_l = [] for chs in changesets: n_desc = 'r%s:%s' % (chs.revision, chs._short) diff --git a/pylons_app/public/css/pygments_diff.css b/pylons_app/public/css/pygments_diff.css new file mode 100644 --- /dev/null +++ b/pylons_app/public/css/pygments_diff.css @@ -0,0 +1,87 @@ +div.codeblock { + overflow: auto; + padding: 0px; + border: 1px solid #ccc; + background: #f8f8f8; + font-size: 100%; + line-height: 100%; + /* new */ + line-height: 125%; +} + +.code-diff { + padding: 0px; + margin-top: 5px; + margin-bottom: 5px; + border-left: 2px solid #ccc; +} +.code-diff pre, .linenodiv pre { + padding: 5px; + margin: 0; +} +.linenos a { text-decoration: none; } + + +.code { display: block;} + +.code-diff .hll { background-color: #ffffcc } +.code-diff { background: #ffffff; } +.code-diff .c { color: #888888 } /* Comment */ +.code-diff .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.code-diff .k { color: #008800; font-weight: bold } /* Keyword */ +.code-diff .cm { color: #888888 } /* Comment.Multiline */ +.code-diff .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ +.code-diff .c1 { color: #888888 } /* Comment.Single */ +.code-diff .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ +.code-diff .gd { color: #000000; background-color: #ffdddd;width: 300px } /* Generic.Deleted */ +.code-diff .ge { font-style: italic } /* Generic.Emph */ +.code-diff .gr { color: #aa0000 } /* Generic.Error */ +.code-diff .gh { color: #303030 } /* Generic.Heading */ +.code-diff .gi { color: #000000; background-color: #ddffdd;width: 100% } /* Generic.Inserted */ +.code-diff .go { color: #888888 } /* Generic.Output */ +.code-diff .gp { color: #555555 } /* Generic.Prompt */ +.code-diff .gs { font-weight: bold } /* Generic.Strong */ +.code-diff .gu { color: #606060 } /* Generic.Subheading */ +.code-diff .gt { color: #aa0000 } /* Generic.Traceback */ +.code-diff .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ +.code-diff .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ +.code-diff .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ +.code-diff .kp { color: #008800 } /* Keyword.Pseudo */ +.code-diff .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ +.code-diff .kt { color: #888888; font-weight: bold } /* Keyword.Type */ +.code-diff .m { color: #0000DD; font-weight: bold } /* Literal.Number */ +.code-diff .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ +.code-diff .na { color: #336699 } /* Name.Attribute */ +.code-diff .nb { color: #003388 } /* Name.Builtin */ +.code-diff .nc { color: #bb0066; font-weight: bold } /* Name.Class */ +.code-diff .no { color: #003366; font-weight: bold } /* Name.Constant */ +.code-diff .nd { color: #555555 } /* Name.Decorator */ +.code-diff .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ +.code-diff .nf { color: #0066bb; font-weight: bold } /* Name.Function */ +.code-diff .nl { color: #336699; font-style: italic } /* Name.Label */ +.code-diff .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ +.code-diff .py { color: #336699; font-weight: bold } /* Name.Property */ +.code-diff .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ +.code-diff .nv { color: #336699 } /* Name.Variable */ +.code-diff .ow { color: #008800 } /* Operator.Word */ +.code-diff .w { color: #bbbbbb } /* Text.Whitespace */ +.code-diff .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ +.code-diff .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ +.code-diff .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ +.code-diff .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ +.code-diff .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ +.code-diff .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ +.code-diff .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ +.code-diff .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ +.code-diff .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ +.code-diff .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ +.code-diff .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ +.code-diff .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ +.code-diff .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ +.code-diff .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ +.code-diff .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ +.code-diff .bp { color: #003388 } /* Name.Builtin.Pseudo */ +.code-diff .vc { color: #336699 } /* Name.Variable.Class */ +.code-diff .vg { color: #dd7700 } /* Name.Variable.Global */ +.code-diff .vi { color: #3333bb } /* Name.Variable.Instance */ +.code-diff .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ diff --git a/pylons_app/templates/files/file_diff.html b/pylons_app/templates/files/file_diff.html new file mode 100644 --- /dev/null +++ b/pylons_app/templates/files/file_diff.html @@ -0,0 +1,35 @@ +<%inherit file="/base/base.html"/> + +<%def name="title()"> + ${_('Repository managment')} + +<%def name="breadcrumbs()"> + ${h.link_to(u'Home',h.url('/'))} + / + ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))} + / + ${_('files')} + +<%def name="page_nav()"> +
+ +
+ + ${self.menu('files')} + +<%def name="css()"> + + + +
+<%def name="main()"> + +
+
+ ${h.pygmentize(c.diff,linenos=True,anchorlinenos=True,cssclass="code-diff")} +
+
+ \ No newline at end of file diff --git a/pylons_app/templates/files/files_source.html b/pylons_app/templates/files/files_source.html --- a/pylons_app/templates/files/files_source.html +++ b/pylons_app/templates/files/files_source.html @@ -7,7 +7,7 @@
history / annotate / raw
${_('History')}
- ${h.form(h.url.current())} + ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='GET')} ${h.hidden('diff2',c.files_list.changeset._short)} ${h.select('diff1','',c.file_history)} ${h.submit('diff','diff')}