[viewvc-dev] [PATCH] Show property diffs in ViewVC
Alexey Neyman <[email protected]> Fri, 30 Sep 2011 10:39:46 -0700
| Newsgroups | gmane.comp.version-control.cvs.viewcvs.devel |
|---|---|
| Message-ID | <201109301039.47124.stilor__300.199891398911$1317406555$gmane$org@att.net> |
Hi all,
Attached is a patch that allows ViewVC to show property changes in
files/directories. I have not formally described it in template authoring
guide yet, pending approval of the approach taken.
Some highlights of the patch:
- Property diff is displayed as a separate view, 'view=pdiff'
- Most of the diff.ezt template is moved to an include/diff_display.ezt, so
that it could be used by both views (diff/pdiff)
- The arrays of changes in diff.ezt prefixed with diff (e.g. "sidebyside" ->
"diff.sidebyside"), to allow iterating over multiple displayable diffs. This
is needed by pdiff view to display multiple properties.
- The format of the property diff is controlled by a new config option,
prop_diff_format, with the same allowed values as 'diff_format'.
prop_diff_format defaults to 'f' - given that properties are typically not
very long, I think it makes sense to display the whole value.
- New view is linked from the log.ezt (if root type is "svn"), from revision
view ("property changed" is now a link) and from file header (which allows to
switch between content and property diff)
- In diff.ezt, sidebyside and unified "sub-templates" now properly display if
the .type attribute is set to "binary-diff" or "no-changes". "binary-diff" can
now be generated by the property diff (if property value cannot be coerced
into utf8); idiff.sidebyside and idiff.unified now return "no-changes" if,
naturally, there are no changes.
- In viewvc.py, some code common to both the view_diff and the new view_pdiff
functions has been moved into separate routines (look for is_undisplayable,
get_diff_type, diff_side_item)
- try/except block removed from view_diff - this exception is generated and
handled by setup_diff now.
- In viewvc-install, a minor bug is fixed which resulted in traceback if
[Enter] was pressed in response to "overwrite/do not overwrite" question.
In a hindsight, I think the pdiff view may have been made a part of the
regular diff view (with "props changed" link pointing to an anchor in the diff
view). Please advise if you think it would be a better way.
The changes in diff.ezt template arguments could have been avoided if it were
possible to alias "variables" in the templates. For example, this fails:
[define changes][diff.changes][end]
as the diff.changes is an array and ezt.py throws a traceback, failing to
convert it to a printable form. I thought about something like:
[alias changes diff.chages]
which would make "diff.changes" be used wherever "changes" variable is
encountered. Again, please advise if such new template command would be
useful.
Regards,
Alexey.
Regards,
Alexey.
------------------------------------------------------
http://viewvc.tigris.org/ds/viewMessage.do?dsForumId=4251&dsMessageId=2848329
To unsubscribe from this discussion, e-mail: [[email protected]].
prop-diff.diff
(text/x-patch, 50.9 KB)
Index: conf/viewvc.conf.dist
===================================================================
--- conf/viewvc.conf.dist (revision 2598)
+++ conf/viewvc.conf.dist (working copy)
@@ -338,8 +338,8 @@
## in this comma-delited list will not be served (or, will return an
## error on attempted access).
##
-## Valid items for this list include: "annotate", "co", "diff", "markup",
-## "roots", "tar".
+## Valid items for this list include: "annotate", "co", "diff", "pdiff",
+## "markup", "roots", "tar".
##
## ----------+---------------------------------------------------------
## VIEW | DESCRIPTION
@@ -360,6 +360,10 @@
## | two revisions of a versioned file in a variety of
## | different user-selectable formats.
## ----------+---------------------------------------------------------
+## pdiff | The 'pdiff' view displays line-based differences between
+## | the properties of a file/directory in two different
+## | revisions.
+## ----------+---------------------------------------------------------
## markup | The 'markup' view shows the contents of a single
## | revision of a versioned file, with syntax highlighting
## | where possible and enabled. It can also optionally
@@ -375,7 +379,7 @@
## | directory and its contents (recursively).
## ----------+---------------------------------------------------------
##
-#allowed_views = annotate, diff, markup, roots
+#allowed_views = annotate, diff, pdiff, markup, roots
## authorizer: The name of the ViewVC authorizer plugin to use when
## authorizing access to repository contents. This value must be the
@@ -516,6 +520,16 @@
##
#diff_format = h
+## prop_diff_format: Default property diff format
+## h Human readable
+## u Unified diff
+## c Context diff
+## s Side by side
+## l Long human readable (more context)
+## f Full human readable (entire file)
+##
+#prop_diff_format = f
+
## hr_breakable: Diff view line breaks
## 1 lines break at spaces
## 0 no line breaking
Index: templates/log.ezt
===================================================================
--- templates/log.ezt (revision 2598)
+++ templates/log.ezt (working copy)
@@ -105,42 +105,52 @@
[is entries.state "dead"]
<br /><strong><em>FILE REMOVED</em></strong>
[else]
- [is pathtype "file"]
- [if-any entries.prev]
- <br />Diff to <a href="[entries.diff_to_prev_href]">previous [entries.prev]</a>
- [if-any human_readable]
- [else]
- (<a href="[entries.diff_to_prev_href]&diff_format=h">colored</a>)
+ [is pathtype "file"]
+ [if-any entries.prev]
+ <br />Diff to <a href="[entries.diff_to_prev_href]">previous [entries.prev]</a>
+ [if-any human_readable]
+ [else]
+ (<a href="[entries.diff_to_prev_href]&diff_format=h">colored</a>)
+ [end]
[end]
- [end]
- [is roottype "cvs"]
- [if-any entries.branch_point]
- , to <a href="[entries.diff_to_branch_href]">branch point [entries.branch_point]</a>
- [if-any human_readable]
- [else]
- (<a href="[entries.diff_to_branch_href]&diff_format=h">colored</a>)
- [end]
+ [is roottype "cvs"]
+ [if-any entries.branch_point]
+ , to <a href="[entries.diff_to_branch_href]">branch point [entries.branch_point]</a>
+ [if-any human_readable]
+ [else]
+ (<a href="[entries.diff_to_branch_href]&diff_format=h">colored</a>)
+ [end]
+ [end]
+
+ [if-any entries.next_main]
+ , to <a href="[entries.diff_to_main_href]">next main [entries.next_main]</a>
+ [if-any human_readable]
+ [else]
+ (<a href="[entries.diff_to_main_href]&diff_format=h">colored</a>)
+ [end]
+ [end]
[end]
-
- [if-any entries.next_main]
- , to <a href="[entries.diff_to_main_href]">next main [entries.next_main]</a>
- [if-any human_readable]
- [else]
- (<a href="[entries.diff_to_main_href]&diff_format=h">colored</a>)
- [end]
+
+ [if-any entries.diff_to_sel_href]
+ [if-any entries.prev], [else]<br />Diff[end]
+ to <a href="[entries.diff_to_sel_href]">selected [rev_selected]</a>
+ [if-any human_readable]
+ [else]
+ (<a href="[entries.diff_to_sel_href]&diff_format=h">colored</a>)
+ [end]
[end]
[end]
+ [is roottype "svn"]
+ [if-any entries.prev]
+ <br />Property diff to <a href="[entries.pdiff_to_prev_href]">previous [entries.prev]</a>
+ [end]
- [if-any entries.diff_to_sel_href]
- [if-any entries.prev], [else]<br />Diff[end]
- to <a href="[entries.diff_to_sel_href]">selected [rev_selected]</a>
- [if-any human_readable]
- [else]
- (<a href="[entries.diff_to_sel_href]&diff_format=h">colored</a>)
+ [if-any entries.pdiff_to_sel_href]
+ [if-any entries.prev], [else]<br />Property diff[end]
+ to <a href="[entries.pdiff_to_sel_href]">selected [rev_selected]</a>
[end]
[end]
- [end]
[end]
<pre class="vc_log">[entries.log]</pre>
Index: templates/include/file_header.ezt
===================================================================
--- templates/include/file_header.ezt (revision 2598)
+++ templates/include/file_header.ezt (working copy)
@@ -7,9 +7,15 @@
[if-any graph_href]
| <a href="[graph_href]"><img src="[docroot]/images/cvsgraph_16x16.png" class="vc_icon" alt="View Revision Graph" /> Revision Graph</a>
[end]
-[is view "diff"]
+[if-any patch_href]
| <a href="[patch_href]"><img src="[docroot]/images/diff.png" class="vc_icon" alt="View Patch" /> Patch</a>
[end]
+[if-any diff_href]
+ | <a href="[diff_href]"><img src="[docroot]/images/diff.png" class="vc_icon" alt="View Diff" /> Diff</a>
+[end]
+[if-any pdiff_href]
+ | <a href="[pdiff_href]"><img src="[docroot]/images/diff.png" class="vc_icon" alt="View Property Diff" /> Property Diff</a>
+[end]
[else]
<a href="[view_href]"><img src="[docroot]/images/dir.png" class="vc_icon" alt="View Directory Listing" /> Directory Listing</a>
[end]
Index: templates/include/diff_display.ezt
===================================================================
--- templates/include/diff_display.ezt (revision 2594)
+++ templates/include/diff_display.ezt (working copy)
@@ -1,21 +1,11 @@
-[# setup page definitions]
- [define page_title]Diff of /[where][end]
- [define help_href][docroot]/help_rootview.html[end]
-[# end]
-
-[include "include/header.ezt" "diff"]
-[include "include/file_header.ezt"]
-
-<h3 style="text-align:center;"></h3>
-
-[if-any raw_diff]
- <pre class="vc_raw_diff">[raw_diff]</pre>
+[if-any diff.raw_diff]
+ <pre class="vc_raw_diff">[diff.raw_diff]</pre>
[end]
[define left_view_href][if-any left.prefer_markup][left.view_href][else][if-any left.download_href][left.download_href][end][end][end]
[define right_view_href][if-any right.prefer_markup][right.view_href][else][if-any right.download_href][right.download_href][end][end][end]
-[if-any changes]
+[if-any diff.changes]
<table cellspacing="0" cellpadding="0">
<tr class="vc_diff_header">
<th style="width:6%;"></th>
@@ -33,54 +23,54 @@
</th>
</tr>
- [for changes]
- [is changes.type "header"]
- <tr class="vc_diff_chunk_header" id="h[changes.line_info_right]">
+ [for diff.changes]
+ [is diff.changes.type "header"]
+ <tr class="vc_diff_chunk_header" id="h[diff.changes.line_info_right]">
<td style="width:6%;"><strong>#</strong></td>
<td style="width:47%;">
- <strong>Line [changes.line_info_left]</strong>
- <span class="vc_diff_chunk_extra">[changes.line_info_extra]</span>
+ <strong>Line [diff.changes.line_info_left]</strong>
+ <span class="vc_diff_chunk_extra">[diff.changes.line_info_extra]</span>
</td>
<td style="width:47%;">
- <strong>Line [changes.line_info_right]</strong>
- <span class="vc_diff_chunk_extra">[changes.line_info_extra]</span>
+ <strong>Line [diff.changes.line_info_right]</strong>
+ <span class="vc_diff_chunk_extra">[diff.changes.line_info_extra]</span>
</td>
</tr>
[else]
- [is changes.type "add"]
+ [is diff.changes.type "add"]
<tr>
- <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
+ <td class="vc_diff_line_number" id="l[diff.changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[diff.changes.line_number]">[diff.changes.line_number]</a>[else][diff.changes.line_number][end]</td>
<td class="vc_diff_empty"> </td>
- <td class="vc_diff_add"> [changes.right]</td>
+ <td class="vc_diff_add"> [diff.changes.right]</td>
</tr>
[else]
- [is changes.type "remove"]
+ [is diff.changes.type "remove"]
<tr>
<td></td>
- <td class="vc_diff_remove"> [changes.left]</td>
+ <td class="vc_diff_remove"> [diff.changes.left]</td>
<td class="vc_diff_empty"> </td>
</tr>
[else]
- [is changes.type "change"]
+ [is diff.changes.type "change"]
<tr>
- [if-any changes.have_right]
- <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
+ [if-any diff.changes.have_right]
+ <td class="vc_diff_line_number" id="l[diff.changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[diff.changes.line_number]">[diff.changes.line_number]</a>[else][diff.changes.line_number][end]</td>
[else]
<td></td>
[end]
- [if-any changes.have_left]
- <td class="vc_diff_change"> [changes.left]</td>
+ [if-any diff.changes.have_left]
+ <td class="vc_diff_change"> [diff.changes.left]</td>
[else]
<td class="vc_diff_change_empty"> </td>
[end]
- [if-any changes.have_right]
- <td class="vc_diff_change"> [changes.right]</td>
+ [if-any diff.changes.have_right]
+ <td class="vc_diff_change"> [diff.changes.right]</td>
[else]
<td class="vc_diff_change_empty"> </td>
[end]
</tr>
[else]
- [is changes.type "no-changes"]
+ [is diff.changes.type "no-changes"]
<tr>
<td colspan="3"> </td>
</tr>
@@ -89,16 +79,16 @@
<strong>- No changes -</strong><br /> </td>
</tr>
[else]
- [is changes.type "binary-diff"]
+ [is diff.changes.type "binary-diff"]
<tr>
<td colspan="3"> </td>
</tr>
<tr class="vc_diff_empty">
<td colspan="3" style="text-align:center;"><br />
- <strong>- Binary file revisions differ -</strong><br /> </td>
+ <strong>- Binary content differs -</strong><br /> </td>
</tr>
[else]
- [is changes.type "error"]
+ [is diff.changes.type "error"]
<tr>
<td colspan="3"> </td>
</tr>
@@ -112,9 +102,9 @@
</tr>
[else]
<tr>
- <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
- <td class="vc_diff_nochange"> [changes.left]</td>
- <td class="vc_diff_nochange"> [changes.right]</td>
+ <td class="vc_diff_line_number" id="l[diff.changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[diff.changes.line_number]">[diff.changes.line_number]</a>[else][diff.changes.line_number][end]</td>
+ <td class="vc_diff_nochange"> [diff.changes.left]</td>
+ <td class="vc_diff_nochange"> [diff.changes.right]</td>
</tr>
[end]
[end]
@@ -127,10 +117,10 @@
</table>
[end]
-[if-any sidebyside]
+[if-any diff.sidebyside]
<table class="vc_idiff">
- <colgroup><col /><col class="content" /></colgroup>
- <colgroup><col /><col class="content" /></colgroup>
+ <colgroup><col style="width: 1%"/><col class="content"/></colgroup>
+ <colgroup><col style="width: 1%"/><col class="content"/></colgroup>
<thead>
<tr>
<th colspan="2">
@@ -144,25 +134,44 @@
</tr>
</thead>
<tbody>
- [for sidebyside]
- [if-any sidebyside.gap]
- <tr>
- <th>…</th><th></th>
- <th>…</th><th></th>
- </tr>
- [end]
- <tr>
- [for sidebyside.columns]
- <th>[sidebyside.columns.line_number]</th><td[if-any sidebyside.columns.line_number][else] class="vc_idiff_empty"[end]>[for sidebyside.columns.segments][if-any sidebyside.columns.segments.type]<span class="vc_idiff_[sidebyside.columns.segments.type]">[sidebyside.columns.segments.text]</span>[else][sidebyside.columns.segments.text][end][end]</td>
- [end]
- </tr>
+ [for diff.sidebyside]
+ [is diff.sidebyside.type "no-changes"]
+ <tr class="vc_diff_empty">
+ <td colspan="4" style="text-align:center;"><br />
+ <strong>- No changes -</strong><br /> </td>
+ </tr>
+ [else]
+ [is diff.sidebyside.type "binary-diff"]
+ <tr class="vc_diff_empty">
+ <td colspan="4" style="text-align:center;"><br />
+ <strong>- Binary content differs -</strong><br /> </td>
+ </tr>
+ [else]
+ [if-any diff.sidebyside.gap]
+ <tr>
+ <th>…</th><th></th>
+ <th>…</th><th></th>
+ </tr>
+ [end]
+ <tr>
+ [for diff.sidebyside.columns]
+ <th>[diff.sidebyside.columns.line_number]</th><td[if-any diff.sidebyside.columns.line_number][else] class="vc_idiff_empty"[end]>[for diff.sidebyside.columns.segments][if-any diff.sidebyside.columns.segments.type]<span class="vc_idiff_[diff.sidebyside.columns.segments.type]">[diff.sidebyside.columns.segments.text]</span>[else][diff.sidebyside.columns.segments.text][end][end]</td>
+ [end]
+ </tr>
+ [end]
+ [end]
[end]
</tbody>
</table>
[end]
-[if-any unified]
+[if-any diff.unified]
<table class="vc_idiff">
+ <colgroup>
+ <col style="width: 1%"/>
+ <col style="width: 1%"/>
+ <col class="content"/>
+ </colgroup>
<thead>
<tr>
<th>r[left.rev]</th>
@@ -171,70 +180,34 @@
</tr>
</thead>
<tbody>
- [for unified]
- [if-any unified.gap]
- <tr>
- <th>…</th>
- <th>…</th>
- <th></th>
- </tr>
- [end]
- <tr>
- <th>[unified.left_number]</th>
- <th>[unified.right_number]</th>
- <td[if-any unified.type] class="vc_idiff_[unified.type]"[end]>[for unified.segments][if-any unified.segments.type]<span class="vc_idiff_[unified.segments.type]">[unified.segments.text]</span>[else][unified.segments.text][end][end]</td>
- </tr>
+ [for diff.unified]
+ [is diff.unified.type "no-changes"]
+ <tr class="vc_diff_empty">
+ <td colspan="3" style="text-align:center;"><br />
+ <strong>- No changes -</strong><br /> </td>
+ </tr>
+ [else]
+ [is diff.unified.type "binary-diff"]
+ <tr class="vc_diff_empty">
+ <td colspan="3" style="text-align:center;"><br />
+ <strong>- Binary content differs -</strong><br /> </td>
+ </tr>
+ [else]
+ [if-any diff.unified.gap]
+ <tr>
+ <th>…</th>
+ <th>…</th>
+ <th></th>
+ </tr>
+ [end]
+ <tr>
+ <th>[diff.unified.left_number]</th>
+ <th>[diff.unified.right_number]</th>
+ <td[if-any diff.unified.type] class="vc_idiff_[diff.unified.type]"[end]>[for diff.unified.segments][if-any diff.unified.segments.type]<span class="vc_idiff_[diff.unified.segments.type]">[diff.unified.segments.text]</span>[else][diff.unified.segments.text][end][end]</td>
+ </tr>
+ [end]
+ [end]
[end]
</tbody>
</table>
[end]
-
-<hr style="margin-top:1em;" />
-
-<table cellpadding="10" class="auto">
- <tr>
- <td>
- <form method="get" action="[diff_format_action]">
- <div>
- [for diff_format_hidden_values]<input type="hidden" name="[diff_format_hidden_values.name]" value="[diff_format_hidden_values.value]"/>[end]
- <select name="diff_format" onchange="submit()">
- <option value="h" [is diff_format "h"]selected="selected"[end]>Colored Diff</option>
- <option value="l" [is diff_format "l"]selected="selected"[end]>Long Colored Diff</option>
- <option value="f" [is diff_format "f"]selected="selected"[end]>Full Colored Diff</option>
- <option value="u" [is diff_format "u"]selected="selected"[end]>Unidiff</option>
- <option value="c" [is diff_format "c"]selected="selected"[end]>Context Diff</option>
- <option value="s" [is diff_format "s"]selected="selected"[end]>Side by Side</option>
- </select>
- <input type="submit" value="Show" />
- </div>
- </form>
- </td>
- <td>
-[if-any raw_diff]
-
-[else]
- <table style="border:solid gray 1px;" class="auto">
- <tr>
- <td>Legend:<br />
- <table cellspacing="0" cellpadding="1">
- <tr>
- <td style="text-align:center;" class="vc_diff_remove">Removed from v.[left.rev]</td>
- <td class="vc_diff_empty"> </td>
- </tr>
- <tr>
- <td style="text-align:center;" colspan="2" class="vc_diff_change">changed lines</td>
- </tr>
- <tr>
- <td class="vc_diff_empty"> </td>
- <td style="text-align:center;" class="vc_diff_add">Added in v.[right.rev]</td>
- </tr>
- </table>
- </td>
- </tr>
- </table>
-[end]
- </td>
- </tr>
-</table>
-
-[include "include/footer.ezt"]
Index: templates/pdiff.ezt
===================================================================
--- templates/pdiff.ezt (revision 0)
+++ templates/pdiff.ezt (revision 0)
@@ -0,0 +1,66 @@
+[# setup page definitions]
+ [define page_title]Property diff of /[where][end]
+ [define help_href][docroot]/help_rootview.html[end]
+[# end]
+
+[include "include/header.ezt" "diff"]
+[include "include/file_header.ezt"]
+
+[if-any diff]
+ [for diff]
+ <h3>[diff.name] <em>([if-any diff.is_added]added[else][if-any diff.is_removed]removed[else]changed[end][end])</em></h3>
+ [include "include/diff_display.ezt"]
+ [end]
+
+<hr style="margin-top:1em;" />
+
+<table cellpadding="10" class="auto">
+ <tr>
+ <td>
+ <form method="get" action="[diff_format_action]">
+ <div>
+ [for diff_format_hidden_values]<input type="hidden" name="[diff_format_hidden_values.name]" value="[diff_format_hidden_values.value]"/>[end]
+ <select name="diff_format" onchange="submit()">
+ <option value="h" [is diff_format "h"]selected="selected"[end]>Colored Diff</option>
+ <option value="l" [is diff_format "l"]selected="selected"[end]>Long Colored Diff</option>
+ <option value="f" [is diff_format "f"]selected="selected"[end]>Full Colored Diff</option>
+ <option value="u" [is diff_format "u"]selected="selected"[end]>Unidiff</option>
+ <option value="c" [is diff_format "c"]selected="selected"[end]>Context Diff</option>
+ <option value="s" [is diff_format "s"]selected="selected"[end]>Side by Side</option>
+ </select>
+ <input type="submit" value="Show" />
+ </div>
+ </form>
+ </td>
+ <td>
+[if-any hide_legend]
+
+[else]
+ <table style="border:solid gray 1px;" class="auto">
+ <tr>
+ <td>Legend:<br />
+ <table cellspacing="0" cellpadding="1">
+ <tr>
+ <td style="text-align:center;" class="vc_diff_remove">Removed from v.[left.rev]</td>
+ <td class="vc_diff_empty"> </td>
+ </tr>
+ <tr>
+ <td style="text-align:center;" colspan="2" class="vc_diff_change">changed lines</td>
+ </tr>
+ <tr>
+ <td class="vc_diff_empty"> </td>
+ <td style="text-align:center;" class="vc_diff_add">Added in v.[right.rev]</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+[end]
+ </td>
+ </tr>
+</table>
+[else]
+<h3><em>( no property changes )</em></h3>
+[end]
+
+[include "include/footer.ezt"]
Index: templates/revision.ezt
===================================================================
--- templates/revision.ezt (revision 2598)
+++ templates/revision.ezt (working copy)
@@ -61,7 +61,7 @@
</td>
<td>[if-any changes.log_href]<a href="[changes.log_href]" title="View Log">[end][changes.action][if-any changes.log_href]</a>[end]
[if-any changes.text_mods], [if-any changes.diff_href]<a href="[changes.diff_href]" title="View Diff">[end]text changed[if-any changes.diff_href]</a>[end][end]
- [if-any changes.prop_mods], props changed[end]
+ [if-any changes.prop_mods], [if-any changes.pdiff_href]<a href="[changes.pdiff_href]" title="View Property Diff">[end]props changed[if-any changes.pdiff_href]</a>[end][end]
</td>
</tr>
[end]
Index: templates/diff.ezt
===================================================================
--- templates/diff.ezt (revision 2598)
+++ templates/diff.ezt (working copy)
@@ -5,190 +5,8 @@
[include "include/header.ezt" "diff"]
[include "include/file_header.ezt"]
+[include "include/diff_display.ezt"]
-<h3 style="text-align:center;"></h3>
-
-[if-any raw_diff]
- <pre class="vc_raw_diff">[raw_diff]</pre>
-[end]
-
-[define left_view_href][if-any left.prefer_markup][left.view_href][else][if-any left.download_href][left.download_href][end][end][end]
-[define right_view_href][if-any right.prefer_markup][right.view_href][else][if-any right.download_href][right.download_href][end][end][end]
-
-[if-any changes]
-<table cellspacing="0" cellpadding="0">
- <tr class="vc_diff_header">
- <th style="width:6%;"></th>
- <th style="width:47%; vertical-align:top;">
- [is left.path right.path][else][left.path][end]
- revision [if-any left_view_href]<a href="[left_view_href]">[end][left.rev][if-any left_view_href]</a>[end][if-any left.author] by <em>[left.author]</em>[end],
- [left.date]
- [if-any left.tag]<br />Tag: [left.tag][end]
- </th>
- <th style="width:47%; vertical-align:top;">
- [is left.path right.path][else][right.path][end]
- revision [if-any right_view_href]<a href="[right_view_href]">[end][right.rev][if-any right_view_href]</a>[end][if-any right.author] by <em>[right.author]</em>[end],
- [right.date]
- [if-any right.tag]<br />Tag: [right.tag][end]
- </th>
- </tr>
-
- [for changes]
- [is changes.type "header"]
- <tr class="vc_diff_chunk_header" id="h[changes.line_info_right]">
- <td style="width:6%;"><strong>#</strong></td>
- <td style="width:47%;">
- <strong>Line [changes.line_info_left]</strong>
- <span class="vc_diff_chunk_extra">[changes.line_info_extra]</span>
- </td>
- <td style="width:47%;">
- <strong>Line [changes.line_info_right]</strong>
- <span class="vc_diff_chunk_extra">[changes.line_info_extra]</span>
- </td>
- </tr>
- [else]
- [is changes.type "add"]
- <tr>
- <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
- <td class="vc_diff_empty"> </td>
- <td class="vc_diff_add"> [changes.right]</td>
- </tr>
- [else]
- [is changes.type "remove"]
- <tr>
- <td></td>
- <td class="vc_diff_remove"> [changes.left]</td>
- <td class="vc_diff_empty"> </td>
- </tr>
- [else]
- [is changes.type "change"]
- <tr>
- [if-any changes.have_right]
- <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
- [else]
- <td></td>
- [end]
- [if-any changes.have_left]
- <td class="vc_diff_change"> [changes.left]</td>
- [else]
- <td class="vc_diff_change_empty"> </td>
- [end]
- [if-any changes.have_right]
- <td class="vc_diff_change"> [changes.right]</td>
- [else]
- <td class="vc_diff_change_empty"> </td>
- [end]
- </tr>
- [else]
- [is changes.type "no-changes"]
- <tr>
- <td colspan="3"> </td>
- </tr>
- <tr class="vc_diff_empty">
- <td colspan="3" style="text-align:center;"><br />
- <strong>- No changes -</strong><br /> </td>
- </tr>
- [else]
- [is changes.type "binary-diff"]
- <tr>
- <td colspan="3"> </td>
- </tr>
- <tr class="vc_diff_empty">
- <td colspan="3" style="text-align:center;"><br />
- <strong>- Binary file revisions differ -</strong><br /> </td>
- </tr>
- [else]
- [is changes.type "error"]
- <tr>
- <td colspan="3"> </td>
- </tr>
- <tr class="vc_diff_empty">
- <td colspan="3" style="text-align:center;"> <br />
- <strong>- ViewVC depends on rcsdiff and GNU diff to create
- this page. ViewVC cannot find GNU diff. Even if you
- have GNU diff installed, the rcsdiff program must be
- configured and compiled with the GNU diff location.
- -</strong> <br /> </td>
- </tr>
- [else]
- <tr>
- <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
- <td class="vc_diff_nochange"> [changes.left]</td>
- <td class="vc_diff_nochange"> [changes.right]</td>
- </tr>
- [end]
- [end]
- [end]
- [end]
- [end]
- [end]
- [end]
- [end]
-</table>
-[end]
-
-[if-any sidebyside]
- <table class="vc_idiff">
- <colgroup><col /><col class="content" /></colgroup>
- <colgroup><col /><col class="content" /></colgroup>
- <thead>
- <tr>
- <th colspan="2">
- [is left.path right.path][else][left.path][end]
- Revision [left.rev]
- </th>
- <th colspan="2">
- [is left.path right.path][else][right.path][end]
- Revision [right.rev]
- </th>
- </tr>
- </thead>
- <tbody>
- [for sidebyside]
- [if-any sidebyside.gap]
- <tr>
- <th>…</th><th></th>
- <th>…</th><th></th>
- </tr>
- [end]
- <tr>
- [for sidebyside.columns]
- <th>[sidebyside.columns.line_number]</th><td[if-any sidebyside.columns.line_number][else] class="vc_idiff_empty"[end]>[for sidebyside.columns.segments][if-any sidebyside.columns.segments.type]<span class="vc_idiff_[sidebyside.columns.segments.type]">[sidebyside.columns.segments.text]</span>[else][sidebyside.columns.segments.text][end][end]</td>
- [end]
- </tr>
- [end]
- </tbody>
- </table>
-[end]
-
-[if-any unified]
- <table class="vc_idiff">
- <thead>
- <tr>
- <th>r[left.rev]</th>
- <th>r[right.rev]</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- [for unified]
- [if-any unified.gap]
- <tr>
- <th>…</th>
- <th>…</th>
- <th></th>
- </tr>
- [end]
- <tr>
- <th>[unified.left_number]</th>
- <th>[unified.right_number]</th>
- <td[if-any unified.type] class="vc_idiff_[unified.type]"[end]>[for unified.segments][if-any unified.segments.type]<span class="vc_idiff_[unified.segments.type]">[unified.segments.text]</span>[else][unified.segments.text][end][end]</td>
- </tr>
- [end]
- </tbody>
- </table>
-[end]
-
<hr style="margin-top:1em;" />
<table cellpadding="10" class="auto">
@@ -210,7 +28,7 @@
</form>
</td>
<td>
-[if-any raw_diff]
+[if-any diff.raw_diff]
[else]
<table style="border:solid gray 1px;" class="auto">
Index: lib/viewvc.py
===================================================================
--- lib/viewvc.py (revision 2598)
+++ lib/viewvc.py (working copy)
@@ -82,6 +82,7 @@
CHUNK_SIZE = 8192
# for rcsdiff processing of header
+_RCSDIFF_NO_CHANGES = "no-changes"
_RCSDIFF_IS_BINARY = 'binary-diff'
_RCSDIFF_ERROR = 'error'
@@ -533,12 +534,6 @@
if view_func is view_annotate and params.get('annotate') is not None:
view_func = None
- # no need to explicitly specify diff view when
- # there's r1 and r2 parameters
- if (view_func is view_diff and params.get('r1') is not None
- and params.get('r2') is not None):
- view_func = None
-
# no need to explicitly specify checkout view when it's the default
# view or when checkout_magic is enabled
if view_func is view_checkout:
@@ -1387,6 +1382,9 @@
'download_href' : None,
'download_text_href' : None,
'graph_href': None,
+ 'patch_href': None,
+ 'diff_href': None,
+ 'pdiff_href': None,
'kv' : request.kv,
'lockinfo' : None,
'log_href' : None,
@@ -1689,25 +1687,27 @@
def make_comma_sep_list_string(items):
return ', '.join(map(lambda x: x.name, items))
+def is_undisplayable(val):
+ try:
+ unicode(val)
+ return ezt.boolean(0)
+ except:
+ return ezt.boolean(1)
+
def get_itemprops(request, path_parts, rev):
itemprops = request.repos.itemprops(path_parts, rev)
propnames = itemprops.keys()
propnames.sort()
props = []
for name in propnames:
- value = format_log(request, itemprops[name])
- undisplayable = ezt.boolean(0)
# skip non-utf8 property names
- try:
- unicode(name, 'utf8')
- except:
+ if is_undisplayable(name):
continue
+ value = format_log(request, itemprops[name])
# note non-utf8 property values
- try:
- unicode(value, 'utf8')
- except:
+ undisplayable = is_undisplayable(value)
+ if undisplayable:
value = None
- undisplayable = ezt.boolean(1)
props.append(_item(name=name, value=value, undisplayable=undisplayable))
return props
@@ -2442,6 +2442,8 @@
entry.diff_to_prev_href = None
entry.diff_to_branch_href = None
entry.diff_to_main_href = None
+ entry.pdiff_to_sel_href = None
+ entry.pdiff_to_prev_href = None
if request.roottype == 'cvs':
prev = rev.prev or rev.parent
@@ -2530,6 +2532,12 @@
'r2': entry.rev,
'diff_format': None},
escape=1)
+ entry.pdiff_to_prev_href = \
+ request.get_url(view_func=view_pdiff,
+ params={'r1': entry.prev,
+ 'r2': entry.rev,
+ 'diff_format': None},
+ escape=1)
if selected_rev and \
selected_rev != str(entry.rev) and \
selected_rev != str(entry.prev) and \
@@ -2541,6 +2549,12 @@
'r2': entry.rev,
'diff_format': None},
escape=1)
+ entry.pdiff_to_sel_href = \
+ request.get_url(view_func=view_pdiff,
+ params={'r1': selected_rev,
+ 'r2': entry.rev,
+ 'diff_format': None},
+ escape=1)
if entry.next_main:
entry.diff_to_main_href = \
@@ -2975,7 +2989,7 @@
if not line:
if self.state == 'no-changes':
self.state = 'done'
- return _item(type='no-changes')
+ return _item(type=_RCSDIFF_NO_CHANGES)
# see if there are lines to flush
if self.left_col or self.right_col:
@@ -3242,35 +3256,11 @@
copy_stream(fp, server_fp)
fp.close()
-
-def view_diff(request):
- if 'diff' not in request.cfg.options.allowed_views:
- raise debug.ViewVCException('Diff generation is disabled',
- '403 Forbidden')
-
- cfg = request.cfg
- query_dict = request.query_dict
- p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
-
- # since templates are in use and subversion allows changes to the dates,
- # we can't provide a strong etag
- if check_freshness(request, None, '%s-%s' % (rev1, rev2), weak=1):
- return
-
- # TODO: Is the slice necessary, or is limit enough?
- log_entry1 = request.repos.itemlog(p1, rev1, vclib.SORTBY_REV, 0, 1, {})[-1]
- log_entry2 = request.repos.itemlog(p2, rev2, vclib.SORTBY_REV, 0, 1, {})[-1]
-
- ago1 = log_entry1.date is not None \
- and html_time(request, log_entry1.date, 1) or None
- ago2 = log_entry2.date is not None \
- and html_time(request, log_entry2.date, 2) or None
-
+def get_diff_type(format):
diff_type = None
diff_options = {}
human_readable = 0
- format = query_dict.get('diff_format', cfg.options.diff_format)
if format == 'c':
diff_type = vclib.CONTEXT
elif format == 's':
@@ -3291,45 +3281,97 @@
else:
raise debug.ViewVCException('Diff format %s not understood'
% format, '400 Bad Request')
+ return (diff_type, diff_options, human_readable)
+
+def diff_side_item(request, path_comp, rev, sym, content_links=1):
+ # TODO: Is the slice necessary, or is limit enough?
+ log_entry = request.repos.itemlog(path_comp, rev, vclib.SORTBY_REV, 0, 1, {})[-1]
+ ago = log_entry.date is not None \
+ and html_time(request, log_entry.date, 1) or None
+ path_joined = _path_join(path_comp)
+ i = _item(date=make_time_string(log_entry.date, request.cfg),
+ author=log_entry.author,
+ log=format_log(request, log_entry.log),
+ size=log_entry.size,
+ ago=ago,
+ path=path_joined,
+ rev=rev,
+ tag=sym)
+ if content_links:
+ fvi = get_file_view_info(request, path_joined, rev)
+ i.view_href = fvi.view_href
+ i.download_href = fvi.download_href
+ i.download_text_href = fvi.download_text_href
+ i.annotate_href = fvi.annotate_href
+ i.revision_href = fvi.revision_href
+ i.prefer_markup = fvi.prefer_markup
+ else:
+ i.view_href = None
+ i.download_href = None
+ i.download_text_href = None
+ i.annotate_href = None
+ i.revision_href = None
+ i.prefer_markup = ezt.boolean(0)
+ return log_entry, i
+
+# Return whether idiff routines should be used. That module
+# only supports unified and human-readable diff formats. Moreover,
+# unless intraline diffs are not requested - fall back to
+# using GNU diff.
+def use_idiff(cfg, diff_type, human_readable):
+ return (cfg.options.hr_intraline and idiff
+ and ((human_readable and idiff.sidebyside)
+ or (not human_readable and diff_type == vclib.UNIFIED)))
+
+def view_diff(request):
+ if 'diff' not in request.cfg.options.allowed_views:
+ raise debug.ViewVCException('Diff generation is disabled',
+ '403 Forbidden')
+
+ cfg = request.cfg
+ query_dict = request.query_dict
+ p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
+ path_left = _path_join(p1)
+ path_right = _path_join(p2)
+
+ # since templates are in use and subversion allows changes to the dates,
+ # we can't provide a strong etag
+ if check_freshness(request, None, '%s-%s' % (rev1, rev2), weak=1):
+ return
+
+ diff_format = query_dict.get('diff_format', cfg.options.diff_format)
+ diff_type, diff_options, human_readable = get_diff_type(diff_format)
+
if human_readable:
diff_options['funout'] = cfg.options.hr_funout
diff_options['ignore_white'] = cfg.options.hr_ignore_white
diff_options['ignore_keyword_subst'] = cfg.options.hr_ignore_keyword_subst
- try:
- fp = sidebyside = unified = None
- if (cfg.options.hr_intraline and idiff
- and ((human_readable and idiff.sidebyside)
- or (not human_readable and diff_type == vclib.UNIFIED))):
- f1 = request.repos.openfile(p1, rev1, {})[0]
- try:
- lines_left = f1.readlines()
- finally:
- f1.close()
- f2 = request.repos.openfile(p2, rev2, {})[0]
- try:
- lines_right = f2.readlines()
- finally:
- f2.close()
+ changes = fp = sidebyside = unified = None
+ date1 = date2 = raw_diff_fp = None
- if human_readable:
- sidebyside = idiff.sidebyside(lines_left, lines_right,
- diff_options.get("context", 5))
- else:
- unified = idiff.unified(lines_left, lines_right,
- diff_options.get("context", 2))
- else:
- fp = request.repos.rawdiff(p1, rev1, p2, rev2, diff_type, diff_options)
- except vclib.InvalidRevision:
- raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
- 'to diff', '400 Bad Request')
- path_left = _path_join(p1)
- path_right = _path_join(p2)
+ if use_idiff(cfg, diff_type, human_readable):
+ f1 = request.repos.openfile(p1, rev1, {})[0]
+ try:
+ lines_left = f1.readlines()
+ finally:
+ f1.close()
- date1 = date2 = raw_diff_fp = None
- changes = []
- if fp:
+ f2 = request.repos.openfile(p2, rev2, {})[0]
+ try:
+ lines_right = f2.readlines()
+ finally:
+ f2.close()
+
+ if human_readable:
+ sidebyside = idiff.sidebyside(lines_left, lines_right,
+ diff_options.get("context", 5))
+ else:
+ unified = idiff.unified(lines_left, lines_right,
+ diff_options.get("context", 2))
+ else:
+ fp = request.repos.rawdiff(p1, rev1, p2, rev2, diff_type, diff_options)
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
path_left, path_right,
rev1, rev2, sym1, sym2)
@@ -3346,57 +3388,173 @@
diff_format_action, diff_format_hidden_values = \
request.get_form(params=no_format_params)
- fvi = get_file_view_info(request, path_left, rev1)
- left = _item(date=make_time_string(log_entry1.date, cfg),
- author=log_entry1.author,
- log=format_log(request, log_entry1.log),
- size=log_entry1.size,
- ago=ago1,
- path=path_left,
- rev=rev1,
- tag=sym1,
- view_href=fvi.view_href,
- download_href=fvi.download_href,
- download_text_href=fvi.download_text_href,
- annotate_href=fvi.annotate_href,
- revision_href=fvi.revision_href,
- prefer_markup=fvi.prefer_markup)
-
- fvi = get_file_view_info(request, path_right, rev2)
- right = _item(date=make_time_string(log_entry2.date, cfg),
- author=log_entry2.author,
- log=format_log(request, log_entry2.log),
- size=log_entry2.size,
- ago=ago2,
- path=path_right,
- rev=rev2,
- tag=sym2,
- view_href=fvi.view_href,
- download_href=fvi.download_href,
- download_text_href=fvi.download_text_href,
- annotate_href=fvi.annotate_href,
- revision_href=fvi.revision_href,
- prefer_markup=fvi.prefer_markup)
+ log_left, left = diff_side_item(request, p1, rev1, sym1)
+ log_right, right = diff_side_item(request, p2, rev2, sym2)
data = common_template_data(request)
data.merge(ezt.TemplateData({
'left' : left,
'right' : right,
- 'raw_diff' : raw_diff_fp,
- 'changes' : changes,
- 'sidebyside': sidebyside,
- 'unified': unified,
- 'diff_format' : request.query_dict.get('diff_format',
- cfg.options.diff_format),
+ 'diff' : _item(raw_diff=raw_diff_fp,
+ changes=changes,
+ sidebyside=sidebyside,
+ unified=unified,
+ undisplayable=ezt.boolean(0)),
'patch_href' : request.get_url(view_func=view_patch,
params=no_format_params,
escape=1),
+ 'pdiff_href' : request.get_url(view_func=view_pdiff,
+ params=no_format_params,
+ escape=1),
+ 'diff_format' : diff_format,
'diff_format_action' : diff_format_action,
'diff_format_hidden_values' : diff_format_hidden_values,
}))
generate_page(request, "diff", data)
+def view_pdiff(request):
+ if 'pdiff' not in request.cfg.options.allowed_views:
+ raise debug.ViewVCException('Property diff generation is disabled',
+ '403 Forbidden')
+ cfg = request.cfg
+ pathtype = request.pathtype
+ query_dict = request.query_dict
+ p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
+ # since templates are in use and subversion allows changes to the dates,
+ # we can't provide a strong etag
+ if check_freshness(request, None, '%s-%s' % (rev1, rev2), weak=1):
+ return
+
+ diff_format = query_dict.get('diff_format', cfg.options.diff_format)
+ diff_type, diff_options, human_readable = get_diff_type(diff_format)
+
+ if human_readable:
+ diff_options['ignore_white'] = cfg.options.hr_ignore_white
+
+ path_left = _path_join(p1)
+ path_right = _path_join(p2)
+
+ log_left, left = diff_side_item(request, p1, rev1, sym1, 0)
+ log_right, right = diff_side_item(request, p2, rev2, sym2, 0)
+
+ props_left = request.repos.itemprops(p1, rev1)
+ props_right = request.repos.itemprops(p2, rev2)
+ diff = []
+ hide_legend = ezt.boolean(0)
+
+ def uniq(lst):
+ h = {}
+ for e in lst:
+ h[e] = 1
+ return sorted(h.keys())
+
+ for p in uniq(props_left.keys() + props_right.keys()):
+ # Skip non-utf8 property names
+ if is_undisplayable(p):
+ continue
+ # Get the values and see if there's a change
+ val_left = props_left.get(p)
+ val_right = props_right.get(p)
+ is_added = ezt.boolean(0)
+ is_removed = ezt.boolean(0)
+ if val_left is None:
+ is_added = ezt.boolean(1)
+ elif val_right is None:
+ is_removed = ezt.boolean(1)
+ elif val_left == val_right:
+ continue
+ # Determine if old/new value is displayable and if it is, split to lines
+ undisplayable = 0
+ raw_diff_fp = changes = sidebyside = unified = None
+ if use_idiff(cfg, diff_format, human_readable):
+ lines_left = []
+ lines_right = []
+ if val_left is not None:
+ if is_undisplayable(val_left):
+ undisplayable = 1
+ else:
+ lines_left = val_left.splitlines()
+ if val_right is not None:
+ if is_undisplayable(val_right):
+ undisplayable = 1
+ else:
+ lines_right = val_right.splitlines()
+ if human_readable:
+ if undisplayable:
+ sidebyside = [ _item(type=_RCSDIFF_IS_BINARY) ]
+ else:
+ sidebyside = idiff.sidebyside(lines_left, lines_right,
+ diff_options.get("context", 5))
+ else:
+ if undisplayable:
+ unified = [ _item(type=_RCSDIFF_IS_BINARY) ]
+ else:
+ unified = idiff.unified(lines_left, lines_right,
+ diff_options.get("context", 2))
+ else:
+ def prop_temp(val):
+ fname = tempfile.mktemp()
+ fp = open(fname, "wb")
+ if val:
+ fp.write(val)
+ fp.close()
+ return fname
+
+ def prop_path(f,n):
+ return "%s:property(%s)" % (f, n)
+
+ temp1 = prop_temp(val_left)
+ temp2 = prop_temp(val_right)
+ ppath1 = prop_path(path_left, p)
+ ppath2 = prop_path(path_right, p)
+ info1 = (ppath1, log_left.date, rev1)
+ info2 = (ppath2, log_right.date, rev2)
+ diff_args = vclib._diff_args(diff_type, diff_options)
+ fp = vclib._diff_fp(temp1, temp2, info1, info2,
+ cfg.utilities.diff or 'diff', diff_opts=diff_args)
+ date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
+ ppath1, ppath2, rev1, rev2, sym1, sym2)
+ if human_readable:
+ if flag is not None:
+ changes = [ _item(type=flag) ]
+ else:
+ changes = DiffSource(fp, cfg)
+ else:
+ hide_legend = ezt.boolean(1)
+ raw_diff_fp = MarkupPipeWrapper(fp, request.server.escape(headers), None, 1)
+
+ diff.append(_item(name=p,
+ is_added=is_added,
+ is_removed=is_removed,
+ sidebyside=sidebyside,
+ unified=unified,
+ raw_diff=raw_diff_fp,
+ changes=changes))
+
+ no_format_params = request.query_dict.copy()
+ no_format_params['diff_format'] = None
+ diff_format_action, diff_format_hidden_values = \
+ request.get_form(params=no_format_params)
+
+ data = common_template_data(request)
+ data.merge(ezt.TemplateData({
+ 'left' : left,
+ 'right' : right,
+ 'diff' : diff,
+ 'patch_href' : request.get_url(view_func=view_patch,
+ params=no_format_params,
+ escape=1),
+ 'diff_href' : request.get_url(view_func=view_diff,
+ params=no_format_params,
+ escape=1),
+ 'diff_format' : diff_format,
+ 'diff_format_action' : diff_format_action,
+ 'diff_format_hidden_values' : diff_format_hidden_values,
+ 'hide_legend' : hide_legend
+ }))
+ generate_page(request, "pdiff", data)
+
def generate_tarball_header(out, name, size=0, mode=None, mtime=0,
uid=0, gid=0, typefrag=None, linkname='',
uname='viewvc', gname='viewvc',
@@ -3613,19 +3771,14 @@
propnames.sort()
props = []
for name in propnames:
- value = format_log(request, revprops[name])
- undisplayable = ezt.boolean(0)
# skip non-utf8 property names
- try:
- unicode(name, 'utf8')
- except:
+ if is_undisplayable(name):
continue
+ value = format_log(request, revprops[name])
# note non-utf8 property values
- try:
- unicode(value, 'utf8')
- except:
+ undisplayable = is_undisplayable(value)
+ if undisplayable:
value = None
- undisplayable = ezt.boolean(1)
props.append(_item(name=name, value=value, undisplayable=undisplayable))
# Sort the changes list by path.
@@ -3702,6 +3855,15 @@
'r2' : str(change.base_rev),
},
escape=1)
+ if change.props_changed:
+ change.pdiff_href = request.get_url(view_func=view_pdiff,
+ where=path,
+ pathtype=change.pathtype,
+ params={'pathrev' : str(rev),
+ 'r1' : str(rev),
+ 'r2' : str(change.base_rev),
+ },
+ escape=1)
# use same variable names as the log template
@@ -4365,6 +4527,7 @@
'log': view_log,
'markup': view_markup,
'patch': view_patch,
+ 'pdiff': view_pdiff,
'query': view_query,
'queryform': view_queryform,
'revision': view_revision,
Index: lib/config.py
===================================================================
--- lib/config.py (revision 2598)
+++ lib/config.py (working copy)
@@ -393,7 +393,7 @@
self.options.root_as_url_component = 1
self.options.checkout_magic = 0
- self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
+ self.options.allowed_views = ['annotate', 'diff', 'pdiff', 'markup', 'roots']
self.options.authorizer = None
self.options.mangle_email_addresses = 0
self.options.default_file_view = "log"
@@ -408,6 +408,7 @@
self.options.hide_errorful_entries = 0
self.options.log_sort = 'date'
self.options.diff_format = 'h'
+ self.options.prop_diff_format = 'f'
self.options.hide_cvsroot = 1
self.options.hr_breakable = 1
self.options.hr_funout = 1
Index: lib/idiff.py
===================================================================
--- lib/idiff.py (revision 2598)
+++ lib/idiff.py (working copy)
@@ -22,6 +22,8 @@
import ezt
import sapi
+_RCSDIFF_NO_CHANGES = "no-changes"
+
def sidebyside(fromlines, tolines, context):
"""Generate side by side diff"""
@@ -29,6 +31,7 @@
line_strip = lambda line: line.rstrip("\n")
fromlines = map(line_strip, fromlines)
tolines = map(line_strip, tolines)
+ had_changes = 0
gap = False
for fromdata, todata, flag in difflib._mdiff(fromlines, tolines, context):
@@ -37,8 +40,11 @@
else:
from_item = _mdiff_split(flag, fromdata)
to_item = _mdiff_split(flag, todata)
- yield _item(gap=ezt.boolean(gap), columns=(from_item, to_item))
+ had_changes = 1
+ yield _item(gap=ezt.boolean(gap), columns=(from_item, to_item), type="intraline")
gap = False
+ if not had_changes:
+ yield _item(type=_RCSDIFF_NO_CHANGES)
_re_mdiff = re.compile("\0([+-^])(.*?)\1")
@@ -71,19 +77,26 @@
diff = difflib.Differ().compare(fromlines, tolines)
lastrow = None
+ had_changes = 0
for row in _trim_context(diff, context):
if row[0].startswith("? "):
+ had_changes = 1
yield _differ_split(lastrow, row[0])
lastrow = None
else:
if lastrow:
+ had_changes = 1
yield _differ_split(lastrow, None)
lastrow = row
if lastrow:
+ had_changes = 1
yield _differ_split(lastrow, None)
+ if not had_changes:
+ yield _item(type=_RCSDIFF_NO_CHANGES)
+
def _trim_context(lines, context_size):
"""Trim context lines that don't surround changes from Differ results
Index: viewvc-install
===================================================================
--- viewvc-install (revision 2598)
+++ viewvc-install (working copy)
@@ -207,6 +207,8 @@
temp = raw_input("Do you want to [O]verwrite, [D]o "
"not overwrite, or [V]iew "
"differences? ")
+ if len(temp) == 0:
+ continue
temp = temp[0].lower()
if temp == "v" and ext not in BINARY_FILE_EXTS:
print """