diffs: compare overhaul.

- made compare and commit range pages more consistent with other commit diff pages
- removed mergerly, closes #4665. Old diff2way is replaced by new diffs with
  side-by-side mode.
- cleanup button behaviour on compare page. Added help text and
  generally improved UX
- switched file-diffs to compare page with file filter. Part of #4000
- added collapse/expand commits buttons in compare views.
This commit is contained in:
Marcin Kuzminski 2016-12-22 23:48:05 +01:00
parent 92864a9563
commit e44090dfe8
33 changed files with 1524 additions and 2616 deletions

View file

@ -1066,7 +1066,7 @@ def make_map(config):
'/{repo_name}/annotate/{revision}/{f_path}',
controller='files', action='index', revision='tip',
f_path='', annotate=True, conditions={'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
requirements=URL_NAME_REQUIREMENTS, jsroute=True)
rmap.connect('files_edit',
'/{repo_name}/edit/{revision}/{f_path}',

View file

@ -90,6 +90,7 @@ class CompareController(BaseRepoController):
c.target_ref_type = ""
c.commit_statuses = ChangesetStatus.STATUSES
c.preview_mode = False
c.file_path = None
return render('compare/compare_diff.html')
@LoginRequired()
@ -103,8 +104,10 @@ class CompareController(BaseRepoController):
# target_ref will be evaluated in target_repo
target_repo_name = request.GET.get('target_repo', source_repo_name)
target_path, target_id = parse_path_ref(target_ref)
target_path, target_id = parse_path_ref(
target_ref, default_path=request.GET.get('f_path', ''))
c.file_path = target_path
c.commit_statuses = ChangesetStatus.STATUSES
# if merge is True
@ -115,7 +118,6 @@ class CompareController(BaseRepoController):
# if merge is False
# Show a raw diff of source/target refs even if no ancestor exists
# c.fulldiff disables cut_off_limit
c.fulldiff = str2bool(request.GET.get('fulldiff'))
@ -131,7 +133,8 @@ class CompareController(BaseRepoController):
target_repo=source_repo_name,
target_ref_type=source_ref_type,
target_ref=source_ref,
merge=merge and '1' or '')
merge=merge and '1' or '',
f_path=target_path)
source_repo = Repository.get_by_repo_name(source_repo_name)
target_repo = Repository.get_by_repo_name(target_repo_name)
@ -151,8 +154,11 @@ class CompareController(BaseRepoController):
h.flash(msg, category='error')
return redirect(url('compare_home', repo_name=c.repo_name))
source_alias = source_repo.scm_instance().alias
target_alias = target_repo.scm_instance().alias
source_scm = source_repo.scm_instance()
target_scm = target_repo.scm_instance()
source_alias = source_scm.alias
target_alias = target_scm.alias
if source_alias != target_alias:
msg = _('The comparison of two different kinds of remote repos '
'is not available')
@ -175,9 +181,6 @@ class CompareController(BaseRepoController):
c.source_ref_type = source_ref_type
c.target_ref_type = target_ref_type
source_scm = source_repo.scm_instance()
target_scm = target_repo.scm_instance()
pre_load = ["author", "branch", "date", "message"]
c.ancestor = None
try:
@ -199,9 +202,9 @@ class CompareController(BaseRepoController):
c.statuses = c.rhodecode_db_repo.statuses(
[x.raw_id for x in c.commit_ranges])
if partial: # for PR ajax commits loader
if partial: # for PR ajax commits loader
if not c.ancestor:
return '' # cannot merge if there is no ancestor
return '' # cannot merge if there is no ancestor
return render('compare/compare_commits.html')
if c.ancestor:
@ -238,7 +241,8 @@ class CompareController(BaseRepoController):
txtdiff = source_repo.scm_instance().get_diff(
commit1=source_commit, commit2=target_commit,
path1=source_path, path=target_path)
path=target_path, path1=source_path)
diff_processor = diffs.DiffProcessor(
txtdiff, format='newdiff', diff_limit=diff_limit,
file_limit=file_limit, show_full_diff=c.fulldiff)
@ -260,5 +264,7 @@ class CompareController(BaseRepoController):
).render_patchset(_parsed, source_ref, target_ref)
c.preview_mode = merge
c.source_commit = source_commit
c.target_commit = target_commit
return render('compare/compare_diff.html')

View file

@ -799,21 +799,15 @@ class FilesController(BaseRepoController):
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def diff(self, repo_name, f_path):
ignore_whitespace = request.GET.get('ignorews') == '1'
line_context = request.GET.get('context', 3)
c.action = request.GET.get('diff')
diff1 = request.GET.get('diff1', '')
diff2 = request.GET.get('diff2', '')
path1, diff1 = parse_path_ref(diff1, default_path=f_path)
diff2 = request.GET.get('diff2', '')
c.action = request.GET.get('diff')
c.no_changes = diff1 == diff2
c.f_path = f_path
c.big_diff = False
c.ignorews_url = _ignorews_url
c.context_url = _context_url
c.changes = OrderedDict()
c.changes[diff2] = []
ignore_whitespace = str2bool(request.GET.get('ignorews'))
line_context = request.GET.get('context', 3)
if not any((diff1, diff2)):
h.flash(
@ -821,18 +815,16 @@ class FilesController(BaseRepoController):
category='error')
raise HTTPBadRequest()
# special case if we want a show commit_id only, it's impl here
# to reduce JS and callbacks
if request.GET.get('show_rev') and diff1:
if str2bool(request.GET.get('annotate', 'False')):
_url = url('files_annotate_home', repo_name=c.repo_name,
revision=diff1, f_path=path1)
else:
_url = url('files_home', repo_name=c.repo_name,
revision=diff1, f_path=path1)
return redirect(_url)
if c.action not in ['download', 'raw']:
# redirect to new view if we render diff
return redirect(
url('compare_url', repo_name=repo_name,
source_ref_type='rev',
source_ref=diff1,
target_repo=c.repo_name,
target_ref_type='rev',
target_ref=diff2,
f_path=f_path))
try:
node1 = self._get_file_node(diff1, path1)
@ -877,98 +869,40 @@ class FilesController(BaseRepoController):
return diff.as_raw()
else:
fid = h.FID(diff2, node2.path)
line_context_lcl = get_line_ctx(fid, request.GET)
ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
__, commit1, commit2, diff, st, data = diffs.wrapped_diff(
filenode_old=node1,
filenode_new=node2,
diff_limit=self.cut_off_limit_diff,
file_limit=self.cut_off_limit_file,
show_full_diff=request.GET.get('fulldiff'),
ignore_whitespace=ign_whitespace_lcl,
line_context=line_context_lcl,)
c.lines_added = data['stats']['added'] if data else 0
c.lines_deleted = data['stats']['deleted'] if data else 0
c.files = [data]
c.commit_ranges = [c.commit_1, c.commit_2]
c.ancestor = None
c.statuses = []
c.target_repo = c.rhodecode_db_repo
c.filename1 = node1.path
c.filename = node2.path
c.binary_file = node1.is_binary or node2.is_binary
operation = data['operation'] if data else ''
commit_changes = {
# TODO: it's passing the old file to the diff to keep the
# standard but this is not being used for this template,
# but might need both files in the future or a more standard
# way to work with that
'fid': [commit1, commit2, operation,
c.filename, diff, st, data]
}
c.changes = commit_changes
return render('files/file_diff.html')
return redirect(
url('compare_url', repo_name=repo_name,
source_ref_type='rev',
source_ref=diff1,
target_repo=c.repo_name,
target_ref_type='rev',
target_ref=diff2,
f_path=f_path))
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def diff_2way(self, repo_name, f_path):
"""
Kept only to make OLD links work
"""
diff1 = request.GET.get('diff1', '')
diff2 = request.GET.get('diff2', '')
nodes = []
unknown_commits = []
for commit in [diff1, diff2]:
try:
nodes.append(self._get_file_node(commit, f_path))
except (RepositoryError, NodeError):
log.exception('%(commit)s does not exist' % {'commit': commit})
unknown_commits.append(commit)
h.flash(h.literal(
_('Commit %(commit)s does not exist.') % {'commit': commit}
), category='error')
if not any((diff1, diff2)):
h.flash(
'Need query parameter "diff1" or "diff2" to generate a diff.',
category='error')
raise HTTPBadRequest()
if unknown_commits:
return redirect(url('files_home', repo_name=c.repo_name,
f_path=f_path))
if all(isinstance(node.commit, EmptyCommit) for node in nodes):
raise HTTPNotFound
node1, node2 = nodes
f_gitdiff = diffs.get_gitdiff(node1, node2, ignore_whitespace=False)
diff_processor = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
diff_data = diff_processor.prepare()
if not diff_data or diff_data[0]['raw_diff'] == '':
h.flash(h.literal(_('%(file_path)s has not changed '
'between %(commit_1)s and %(commit_2)s.') % {
'file_path': f_path,
'commit_1': node1.commit.id,
'commit_2': node2.commit.id
}), category='error')
return redirect(url('files_home', repo_name=c.repo_name,
f_path=f_path))
c.diff_data = diff_data[0]
c.FID = h.FID(diff2, node2.path)
# cleanup some unneeded data
del c.diff_data['raw_diff']
del c.diff_data['chunks']
c.node1 = node1
c.commit_1 = node1.commit
c.node2 = node2
c.commit_2 = node2.commit
return render('files/diff_2way.html')
return redirect(
url('compare_url', repo_name=repo_name,
source_ref_type='rev',
source_ref=diff1,
target_repo=c.repo_name,
target_ref_type='rev',
target_ref=diff2,
f_path=f_path,
diffmode='sideside'))
def _get_file_node(self, commit_id, f_path):
if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:

View file

@ -27,6 +27,7 @@ Should only contain utilities to be shared in the controller layer.
from rhodecode.lib import helpers as h
from rhodecode.lib.vcs.exceptions import RepositoryError
def parse_path_ref(ref, default_path=None):
"""
Parse out a path and reference combination and return both parts of it.
@ -76,8 +77,8 @@ def get_commit_from_ref_name(repo, ref_name, ref_type=None):
}
commit_id = ref_name
if repo_scm.alias != 'svn': # pass svn refs straight to backend until
# the branch issue with svn is fixed
if repo_scm.alias != 'svn': # pass svn refs straight to backend until
# the branch issue with svn is fixed
if ref_type and ref_type in ref_type_mapping:
try:
commit_id = ref_type_mapping[ref_type][ref_name]

View file

@ -378,6 +378,7 @@ class BaseRepository(object):
parameter works only for backends which support diff generation for
different paths. Other backends will raise a `ValueError` if `path1`
is set and has a different value than `path`.
:param file_path: filter this diff by given path pattern
"""
raise NotImplementedError
@ -1540,9 +1541,10 @@ class Diff(object):
"""
Represents a diff result from a repository backend.
Subclasses have to provide a backend specific value for :attr:`_header_re`.
Subclasses have to provide a backend specific value for
:attr:`_header_re` and :attr:`_meta_re`.
"""
_meta_re = None
_header_re = None
def __init__(self, raw_diff):
@ -1554,10 +1556,19 @@ class Diff(object):
to make diffs consistent we must prepend with \n, and make sure
we can detect last chunk as this was also has special rule
"""
chunks = ('\n' + self.raw).split('\ndiff --git')[1:]
diff_parts = ('\n' + self.raw).split('\ndiff --git')
header = diff_parts[0]
if self._meta_re:
match = self._meta_re.match(header)
chunks = diff_parts[1:]
total_chunks = len(chunks)
return (DiffChunk(chunk, self, cur_chunk == total_chunks)
for cur_chunk, chunk in enumerate(chunks, start=1))
return (
DiffChunk(chunk, self, cur_chunk == total_chunks)
for cur_chunk, chunk in enumerate(chunks, start=1))
class DiffChunk(object):

View file

@ -30,6 +30,10 @@ from rhodecode.lib.vcs.backends import base
class SubversionDiff(base.Diff):
_meta_re = re.compile(r"""
(?:^(?P<svn_bin_patch>Cannot[ ]display:[ ]file[ ]marked[ ]as[ ]a[ ]binary[ ]type.)(?:\n|$))?
""", re.VERBOSE | re.MULTILINE)
_header_re = re.compile(r"""
#^diff[ ]--git
[ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n

View file

@ -1477,8 +1477,9 @@ table.integrations {
margin-left: 8px;
}
p.ancestor {
div.ancestor {
margin: @padding 0;
line-height: 3.0em;
}
.cs_icon_td input[type="checkbox"] {

View file

@ -1,50 +0,0 @@
/* required */
.mergely-column textarea { width: 80px; height: 200px; }
.mergely-column { float: left; }
.mergely-margin { float: left; }
.mergely-canvas { float: left; width: 28px; }
/* resizeable */
.mergely-resizer { width: 100%; height: 100%; }
/* style configuration */
.mergely-column { border: 1px solid #ccc; }
.mergely-active { border: 1px solid #a3d1ff; }
.mergely.a,.mergely.d,.mergely.c { color: #000; }
.mergely.a.rhs.start { border-top: 1px solid #a3d1ff; }
.mergely.a.lhs.start.end,
.mergely.a.rhs.end { border-bottom: 1px solid #a3d1ff; }
.mergely.a.rhs { background-color: #ddeeff; }
.mergely.a.lhs.start.end.first { border-bottom: 0; border-top: 1px solid #a3d1ff; }
.mergely.d.lhs { background-color: #ffe9e9; }
.mergely.d.lhs.end,
.mergely.d.rhs.start.end { border-bottom: 1px solid #f8e8e8; }
.mergely.d.rhs.start.end.first { border-bottom: 0; border-top: 1px solid #f8e8e8; }
.mergely.d.lhs.start { border-top: 1px solid #f8e8e8; }
.mergely.c.lhs,
.mergely.c.rhs { background-color: #fafafa; }
.mergely.c.lhs.start,
.mergely.c.rhs.start { border-top: 1px solid #a3a3a3; }
.mergely.c.lhs.end,
.mergely.c.rhs.end { border-bottom: 1px solid #a3a3a3; }
.mergely.ch.a.rhs { background-color: #ddeeff; }
.mergely.ch.d.lhs { background-color: #ffe9e9; text-decoration: line-through; color: red !important; }
.mergely-margin #compare-lhs-margin,
.mergely-margin #compare-rhs-margin {
cursor: pointer
}
.mergely.current.start { border-top: 1px solid #000 !important; }
.mergely.current.end { border-bottom: 1px solid #000 !important; }
.mergely.current.lhs.a.start.end,
.mergely.current.rhs.d.start.end { border-top: 0 !important; }
.mergely.current.CodeMirror-linenumber { color: #F9F9F9; font-weight: bold; background-color: #777; }
.CodeMirror-linenumber { cursor: pointer; }
.CodeMirror-code { color: #717171; }

View file

@ -72,6 +72,7 @@
}
.disabled {
opacity: .5;
cursor: inherit;
}
.help-block {
color: inherit;

File diff suppressed because it is too large Load diff

View file

@ -73,6 +73,16 @@ String.prototype.capitalizeFirstLetter = function() {
};
String.prototype.truncateAfter = function(chars, suffix) {
var suffix = suffix || '';
if (this.length > chars) {
return this.substr(0, chars) + suffix;
} else {
return this;
}
};
/**
* Splits remainder
*

View file

@ -112,7 +112,7 @@
<div class="fieldset">
<div class="left-label">
${_('Diffs')}:
${_('Diff options')}:
</div>
<div class="right-content">
<div class="diff-actions">

View file

@ -29,29 +29,84 @@
</%def>
<%def name="main()">
<div class="summary-header">
<div class="summary-header">
<div class="title">
<div class="title-content">
${self.repo_page_title(c.rhodecode_db_repo)}
</div>
</div>
<div class="header-buttons">
<a href="${h.url('compare_url', repo_name=c.repo_name, source_ref_type='rev', source_ref=getattr(c.commit_ranges[0].parents[0] if c.commit_ranges[0].parents else h.EmptyCommit(), 'raw_id'), target_ref_type='rev', target_ref=c.commit_ranges[-1].raw_id)}"
class="btn btn-default">
${_('Show combined compare')}
</a>
</div>
</div>
<div class="summary-detail">
<div class="title">
<h2>
${self.breadcrumbs_links()}
</h2>
</div>
</div>
<div class="summary changeset">
<div class="summary-detail">
<div class="summary-detail-header">
<span class="breadcrumbs files_location">
<h4>
${_('Commit Range')}
<code>
r${c.commit_ranges[0].revision}:${h.short_id(c.commit_ranges[0].raw_id)}...r${c.commit_ranges[-1].revision}:${h.short_id(c.commit_ranges[-1].raw_id)}
</code>
</h4>
</span>
</div>
<div class="fieldset">
<div class="left-label">
${_('Diff option')}:
</div>
<div class="right-content">
<div class="header-buttons">
<a href="${h.url('compare_url', repo_name=c.repo_name, source_ref_type='rev', source_ref=getattr(c.commit_ranges[0].parents[0] if c.commit_ranges[0].parents else h.EmptyCommit(), 'raw_id'), target_ref_type='rev', target_ref=c.commit_ranges[-1].raw_id)}">
${_('Show combined compare')}
</a>
</div>
</div>
</div>
<%doc>
##TODO(marcink): implement this and diff menus
<div class="fieldset">
<div class="left-label">
${_('Diff options')}:
</div>
<div class="right-content">
<div class="diff-actions">
<a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
${_('Raw Diff')}
</a>
|
<a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
${_('Patch Diff')}
</a>
|
<a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision='?',diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
${_('Download Diff')}
</a>
</div>
</div>
</div>
</%doc>
</div> <!-- end summary-detail -->
</div> <!-- end summary -->
<div id="changeset_compare_view_content">
##CS
<div class="pull-left">
<div class="btn-group">
<a
class="btn"
href="#"
onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
</a>
<a
class="btn"
href="#"
onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
</a>
</div>
</div>
## Commit range generated below
<%include file="../compare/compare_commits.html"/>
<div class="cs_files">
<%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
@ -65,7 +120,6 @@
commit=commit,
)}
%endfor
</table>
</div>
</div>
</%def>

View file

@ -52,45 +52,6 @@
</div>
</%def>
<%def name="diff_menu(repo_name, f_path, cs1, cs2, change, file=None)">
<%
onclick_diff2way = ''
if (file and file["exceeds_limit"]):
onclick_diff2way = '''return confirm('%s');''' % _("Showing a big diff might take some time and resources, continue?")
%>
% if change in ['A', 'M']:
<a href="${h.url('files_home',repo_name=repo_name,f_path=f_path,revision=cs2)}"
class="tooltip" title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': cs2[:12]})}">
${_('Show File')}
</a>
% else:
<span
class="tooltip" title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': cs2[:12]})}">
${_('Show File')}
</span>
% endif
|
<a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}"
class="tooltip" title="${h.tooltip(_('Show full diff for this file'))}">
${_('Unified Diff')}
</a>
|
<a href="${h.url('files_diff_2way_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}"
class="tooltip" title="${h.tooltip(_('Show full side-by-side diff for this file'))}"} onclick="${onclick_diff2way}">
${_('Side-by-side Diff')}
</a>
|
<a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='raw')}"
class="tooltip" title="${h.tooltip(_('Raw diff'))}">
${_('Raw Diff')}
</a>
|
<a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='download')}"
class="tooltip" title="${h.tooltip(_('Download diff'))}">
${_('Download Diff')}
</a>
</%def>
<%def name="diff_summary_text(changed_files, lines_added, lines_deleted, limited_diff=False)">
% if limited_diff:

View file

@ -162,10 +162,11 @@ collapse_all = len(diffset.files) > collapse_when_files_over
<div class="filediffs">
%for i, filediff in enumerate(diffset.files):
<%
lines_changed = filediff['patch']['stats']['added'] + filediff['patch']['stats']['deleted']
over_lines_changed_limit = lines_changed > lines_changed_limit
%>
<%
lines_changed = filediff['patch']['stats']['added'] + filediff['patch']['stats']['deleted']
over_lines_changed_limit = lines_changed > lines_changed_limit
%>
<input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
<div
class="filediff"
@ -414,6 +415,7 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
if line.modified.lineno:
new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, line.modified.lineno, 'n')
%>
<tr class="cb-line">
<td class="cb-data ${action_class(line.original.action)}"
data-line-number="${line.original.lineno}"
@ -544,6 +546,7 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
<div class="diffset-menu clearinner">
<div class="pull-right">
<div class="btn-group">
<a
class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
title="${_('View side by side')}"
@ -557,20 +560,21 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
</a>
</div>
</div>
<div class="pull-left">
<div class="btn-group">
<a
class="btn"
href="#"
onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All')}</a>
onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a>
<a
class="btn"
href="#"
onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All')}</a>
onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a>
<a
class="btn"
href="#"
onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode')}</a>
onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
</div>
</div>
</div>

View file

@ -1,20 +1,17 @@
## Changesets table !
<%namespace name="base" file="/base/base.html"/>
%if c.ancestor:
<div class="ancestor">${_('Common Ancestor Commit')}:
<a href="${h.url('changeset_home',
repo_name=c.repo_name,
revision=c.ancestor)}">
${h.short_id(c.ancestor)}
</a>
</div>
%endif
<div class="container">
%if not c.commit_ranges:
<p class="empty_data">${_('No Commits')}</p>
%else:
%if c.ancestor:
<p class="ancestor">${_('Common Ancestor Commit')}:
<a href="${h.url('changeset_home',
repo_name=c.repo_name,
revision=c.ancestor)}">
${h.short_id(c.ancestor)}
</a>
</p>
%endif
<input type="hidden" name="__start__" value="revisions:sequence">
<table class="rctable compare_view_commits">
<tr>
@ -66,9 +63,21 @@
</td>
</tr>
%endfor
<tr class="compare_select_hidden" style="display: none">
<td colspan="5">
${ungettext('%s commit hidden','%s commits hidden', len(c.commit_ranges)) % len(c.commit_ranges)}
</td>
</tr>
% if not c.commit_ranges:
<tr class="compare_select">
<td colspan="5">
${_('No commits in this compare')}
</td>
</tr>
% endif
</table>
<input type="hidden" name="__end__" value="revisions:sequence">
%endif
</div>
<script>
@ -76,7 +85,7 @@ $('.expand_commit').on('click',function(e){
var target_expand = $(this);
var cid = target_expand.data('commitId');
## TODO: dan: extract styles into css, and just toggleClass('open') here
// ## TODO: dan: extract styles into css, and just toggleClass('open') here
if (target_expand.hasClass('open')){
$('#c-'+cid).css({
'height': '1.5em',

View file

@ -34,34 +34,132 @@
<div class="box">
<div class="title">
${self.repo_page_title(c.rhodecode_db_repo)}
<div class="breadcrumbs">
${_('Compare Commits')}
</div>
</div>
<div class="table">
<div id="codeblock" class="diffblock">
<div class="code-header" >
<div class="compare_header">
## The hidden elements are replaced with a select2 widget
<div class="compare-label">${_('Target')}</div>${h.hidden('compare_source')}
<div class="compare-label">${_('Source')}</div>${h.hidden('compare_target')}
<div class="summary changeset">
<div class="summary-detail">
<div class="summary-detail-header">
<span class="breadcrumbs files_location">
<h4>
${_('Compare Commits')}
% if c.file_path:
${_('for file')} <a href="#${'a_' + h.FID('',c.file_path)}">${c.file_path}</a>
% endif
%if not c.preview_mode:
<div class="compare-label"></div>
<div class="compare-buttons">
%if not c.compare_home:
<a id="btn-swap" class="btn btn-primary" href="${c.swap_url}"><i class="icon-refresh"></i> ${_('Swap')}</a>
%endif
<div id="compare_revs" class="btn btn-primary"><i class ="icon-loop"></i> ${_('Compare Commits')}</div>
%if c.diffset and c.diffset.files:
<div id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</div>
%endif
% if c.commit_ranges:
<code>
r${c.source_commit.revision}:${h.short_id(c.source_commit.raw_id)}...r${c.target_commit.revision}:${h.short_id(c.target_commit.raw_id)}
</code>
% endif
</h4>
</span>
</div>
<div class="fieldset">
<div class="left-label">
${_('Target')}:
</div>
<div class="right-content">
<div>
<div class="code-header" >
<div class="compare_header">
## The hidden elements are replaced with a select2 widget
${h.hidden('compare_source')}
</div>
%endif
</div>
</div>
</div>
</div>
</div>
<div class="fieldset">
<div class="left-label">
${_('Source')}:
</div>
<div class="right-content">
<div>
<div class="code-header" >
<div class="compare_header">
## The hidden elements are replaced with a select2 widget
${h.hidden('compare_target')}
</div>
</div>
</div>
</div>
</div>
<div class="fieldset">
<div class="left-label">
${_('Actions')}:
</div>
<div class="right-content">
<div>
<div class="code-header" >
<div class="compare_header">
<div class="compare-buttons">
% if c.compare_home:
<a id="compare_revs" class="btn btn-primary"> ${_('Compare Commits')}</a>
<a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Swap')}</a>
<a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
<div id="changeset_compare_view_content">
<div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
</div>
% elif c.preview_mode:
<a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Compare Commits')}</a>
<a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Swap')}</a>
<a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
% else:
<a id="compare_revs" class="btn btn-primary"> ${_('Compare Commits')}</a>
<a id="btn-swap" class="btn btn-primary" href="${c.swap_url}">${_('Swap')}</a>
## allow comment only if there are commits to comment on
% if c.diffset and c.diffset.files and c.commit_ranges:
<a id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</a>
% else:
<a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
% endif
% endif
</div>
</div>
</div>
</div>
</div>
</div>
<%doc>
##TODO(marcink): implement this and diff menus
<div class="fieldset">
<div class="left-label">
${_('Diff options')}:
</div>
<div class="right-content">
<div class="diff-actions">
<a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
${_('Raw Diff')}
</a>
|
<a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
${_('Patch Diff')}
</a>
|
<a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision='?',diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
${_('Download Diff')}
</a>
</div>
</div>
</div>
</%doc>
</div> <!-- end summary-detail -->
</div> <!-- end summary -->
<div class="table">
## use JS script to load it quickly before potentially large diffs render long time
## this prevents from situation when large diffs block rendering of select2 fields
<script type="text/javascript">
@ -241,13 +339,26 @@
</div>
%if c.compare_home:
%if not c.compare_home:
<div id="changeset_compare_view_content">
<div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
</div>
%else:
<div id="changeset_compare_view_content">
##CS
<div class="pull-left">
<div class="btn-group">
<a
class="btn"
href="#"
onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
</a>
<a
class="btn"
href="#"
onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
</a>
</div>
</div>
<div style="padding:0 10px 10px 0px" class="pull-left"></div>
## commit compare generated below
<%include file="compare_commits.html"/>
${cbdiffs.render_diffset_menu()}
${cbdiffs.render_diffset(c.diffset)}

View file

@ -9,11 +9,9 @@
</%def>
<%def name="js_extra()">
<script type="text/javascript" src="${h.asset('js/mergerly.js', ver=c.rhodecode_version_hash)}"></script>
</%def>
<%def name="css_extra()">
<link rel="stylesheet" type="text/css" href="${h.asset('css/mergerly.css', ver=c.rhodecode_version_hash)}"/>
</%def>

View file

@ -1,225 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/base/base.html"/>
<%namespace name="diff_block" file="/changeset/diff_block.html"/>
<%def name="js_extra()">
<script type="text/javascript" src="${h.asset('js/mergerly.js')}"></script>
</%def>
<%def name="css_extra()">
<link rel="stylesheet" type="text/css" href="${h.asset('css/mergerly.css')}"/>
</%def>
<%def name="title()">
${_('%s File side-by-side diff') % c.repo_name}
%if c.rhodecode_name:
&middot; ${h.branding(c.rhodecode_name)}
%endif
</%def>
<%def name="breadcrumbs_links()">
r${c.commit_1.revision}:${h.short_id(c.commit_1.raw_id)} ... r${c.commit_2.revision}:${h.short_id(c.commit_2.raw_id)}
</%def>
<%def name="menu_bar_nav()">
${self.menu_items(active='repositories')}
</%def>
<%def name="menu_bar_subnav()">
${self.repo_menu(active='changelog')}
</%def>
<%def name="main()">
<div class="box">
<div class="title">
${self.repo_page_title(c.rhodecode_db_repo)}
</div>
<div class="breadcrumbs">
${_('Side-by-side Diff')} r${c.commit_1.revision}:${h.short_id(c.commit_1.raw_id)} ... r${c.commit_2.revision}:${h.short_id(c.commit_2.raw_id)}
</div>
<div class="cs_files">
<table class="compare_view_files commit_diff">
<tr class="cs_${c.diff_data['operation']} collapse_file" fid="${c.FID}">
<td class="cs_icon_td">
<span class="collapse_file_icon" fid="${c.FID}"></span>
</td>
<td class="cs_icon_td">
<div class="flag_status not_reviewed hidden"></div>
</td>
<td class="cs_${c.diff_data['operation']}" id="a_${c.FID}">
<div class="node">
<a href="#a_${c.FID}">
<i class="icon-file-${c.diff_data['operation'].lower()}"></i>
${h.safe_unicode(c.node1.path)}
</a>
</div>
</td>
<td>
<div class="changes pull-right">${h.fancy_file_stats(c.diff_data['stats'])}</div>
<div class="comment-bubble pull-right" data-path="${h.safe_unicode(c.node1.path)}">
<i class="icon-comment"></i>
</div>
</td>
</tr>
<tr fid="${c.FID}" id="diff_${c.FID}" class="diff_links">
<td></td>
<td></td>
<td class="cs_${c.diff_data['operation']}">
${diff_block.diff_menu(c.repo_name, h.safe_unicode(c.node1.path), c.commit_1.raw_id, c.commit_2.raw_id, c.diff_data['operation'])}
</td>
<td class="td-actions rc-form">
<div id="ignorews" class="btn-link show-inline-comments">
<span data-enabled=false class="toggle">${_('Ignore whitespace')}</span>
<span data-enabled=true class="toggle" style="display: none">${_('Show whitespace')}</span>
</div> |
<div id="edit_mode" class="btn-link show-inline-comments">
<span data-enabled=true class="toggle">${_('Enable editor mode')}</span>
<span data-enabled=false class="toggle" style="display: none">${_('Disable editor mode')}</span>
</div> |
<div class="btn-link show-inline-comments">
<span id="prev_change" title="${_('Previous change')}"><i class="icon-left"></i></span>
<span id="next_change" title="${_('Next change')}"><i class="icon-right"></i></span>
</div>
</td>
</tr>
<tr id="tr_${c.FID}">
<td></td>
<td></td>
<td class="injected_diff" colspan="2">
<div class="diff-container" id="${'diff-container-%s' % (id(c.diff_data['operation']))}">
<div id="${c.FID}" class="diffblock margined comm">
<div class="diff-container" >
<div class="diffblock comm sidebyside">
<div class="code-header">
<div class="changeset_header">
${_('mode')}: <span id="selected_mode">plain</span> |
</div>
</div>
<div id="compare"></div>
</div>
</div>
</div>
</div>
</td>
</tr>
</table>
</div>
<script>
var orig1_url = '${h.url('files_raw_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node1.path),revision=c.commit_1.raw_id)}';
var orig2_url = '${h.url('files_raw_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node2.path),revision=c.commit_2.raw_id)}';
$(document).ready(function () {
var editor = $('#compare');
editor.mergely({
autoupdate: true,
width: 'auto',
height: '600',
fgcolor: {a: '#ddffdd', c: '#cccccc', d: '#ffdddd'},
bgcolor: '#fff',
viewport: false,
cmsettings: {
mode: 'text/plain',
readOnly: true,
lineWrapping: false,
lineNumbers: true
}
});
var lhs = function(deferred) {
if ("${c.node1.is_binary}" == "True") {
deferred.resolve('Binary file');
}
else if ("${c.node1.commit.__class__.__name__}" == "EmptyCommit") {
deferred.resolve('');
}
else {
editor.mergely('lhs', 'loading diff...');
$.ajax(orig1_url, {
dataType: 'text',
success: function(data) {
// call the complete function to let CodeMirror know
deferred.resolve(data);
}
});
}
};
var rhs = function(deferred) {
if ("${c.node2.is_binary}" == "True") {
deferred.resolve('Binary file');
}
else if ("${c.node2.commit.__class__.__name__}" == "EmptyCommit") {
deferred.resolve('');
}
else {
editor.mergely('rhs', 'loading diff...');
$.ajax(orig2_url, {
dataType: 'text',
success: function(data) {
// call the complete function to let CodeMirror know
deferred.resolve(data);
}
});
}
};
var deferred_lhs = $.Deferred();
var deferred_rhs = $.Deferred();
$.when(
deferred_lhs, deferred_rhs
).done(function(lhs_response, rhs_response) {
editor.mergely('lhs', lhs_response);
editor.mergely('rhs', rhs_response);
var detected_mode = detectCodeMirrorModeFromExt(
'${h.safe_unicode(c.node1.path.split("/")[-1])}', true);
if (detected_mode) {
setCodeMirrorMode(editor.mergely('cm', 'lhs'), detected_mode);
setCodeMirrorMode(editor.mergely('cm', 'rhs'), detected_mode);
$('#selected_mode').html(detected_mode);
}
});
// load via ajax, and use deferred signals to notify when finished.
lhs(deferred_lhs);
rhs(deferred_rhs);
$("#ignorews").click(function() {
$("#ignorews .toggle").toggle();
var val = $('#ignorews .toggle:visible').data()['enabled'];
editor.mergely('options', {ignorews: val});
editor.mergely('update');
});
$("#edit_mode").click(function() {
$("#edit_mode .toggle").toggle();
var val = $('#edit_mode .toggle:visible').data()['enabled'];
editor.mergely('cm', 'lhs').setOption('readOnly', val);
editor.mergely('cm', 'rhs').setOption('readOnly', val);
editor.mergely('update');
});
$('#prev_change').on('click', function() {
editor.mergely('scrollToDiff', 'prev');
});
$('#next_change').on('click', function() {
editor.mergely('scrollToDiff', 'next');
});
// extend content dynamically on this component for readability
$('#content').css({'max-width': '2000px'});
editor.mergely('resize');
});
</script>
</div>
</%def>

View file

@ -1,159 +0,0 @@
<%inherit file="/base/base.html"/>
<%namespace name="diff_block" file="/changeset/diff_block.html"/>
<%def name="title()">
${_('%s File Diff') % c.repo_name}
%if c.rhodecode_name:
&middot; ${h.branding(c.rhodecode_name)}
%endif
</%def>
<%def name="breadcrumbs_links()">
${_('Compare')}
r${c.commit_1.revision}:${h.short_id(c.commit_1.raw_id)}
% if c.filename1 != c.filename:
<i class="icon-file"></i> ${c.filename1}
% endif
...
r${c.commit_2.revision}:${h.short_id(c.commit_2.raw_id)}
</%def>
<%def name="menu_bar_nav()">
${self.menu_items(active='repositories')}
</%def>
<%def name="menu_bar_subnav()">
${self.repo_menu(active='changelog')}
</%def>
<%def name="breadcrumbs_links()">
${_('Compare')}
r${c.commit_1.revision}:${h.short_id(c.commit_1.raw_id)}
% if c.filename1 != c.filename:
<i class="icon-file"></i> ${c.filename1}
% endif
...
r${c.commit_2.revision}:${h.short_id(c.commit_2.raw_id)}
% if c.filename1 == c.filename:
${_('for')} <i class="icon-file"></i> ${c.filename1}
% endif
</%def>
<%def name="main()">
<div class="box">
<div class="title">
${self.repo_page_title(c.rhodecode_db_repo)}
</div>
${self.breadcrumbs()}
<div class="compare-header">
%if not c.commit_ranges:
<p class="empty_data">${_('No commits')}</p>
%else:
<div class="compare-label">${_('Target')}</div>
<div class="compare-value">
<code>
${h.link_to('r%s:%s' % (c.commit_1.revision, h.short_id(c.commit_1.raw_id)), h.url('changeset_home',repo_name=c.repo_name, revision=c.commit_1.raw_id))}
</code>
</div>
<div class="compare-label">${_('Source')}</div>
<div class="compare-value">
<code>
${h.link_to('r%s:%s' % (c.commit_2.revision, h.short_id(c.commit_2.raw_id)), h.url('changeset_home',repo_name=c.repo_name, revision=c.commit_2.raw_id))}
</code>
</div>
%endif
</div>
##CS
<%include file="../compare/compare_commits.html" />
## FILES
<div class="cs_files_title">
<span class="cs_files_expand">
<span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
</span>
<h2>
% if c.binary_file:
${_('Cannot diff binary files')}
% elif (c.lines_added == 0 and c.lines_deleted == 0):
${_('File was not changed in this commit range')}
% else:
${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted)}
% endif
</h2>
</div>
% if (c.lines_added > 0 or c.lines_deleted > 0):
<div class="cs_files">
<table class="compare_view_files commit_diff">
%for FID, (cs1, cs2, change, path, diff, stats, file) in c.changes.iteritems():
<tr class="cs_${change} collapse_file" fid="${FID}">
<td class="cs_icon_td">
<span class="collapse_file_icon" fid="${FID}"></span>
</td>
<td class="cs_icon_td">
<div class="flag_status not_reviewed hidden"></div>
</td>
<td class="cs_${change}" id="a_${FID}">
<div class="node">
<a href="#a_${FID}">
<i class="icon-file-${change.lower()}"></i>
${h.safe_unicode(path)}
</a>
</div>
</td>
<td>
%if (stats):
<div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
%endif
<div class="comment-bubble pull-right" data-path="${path}">
<i class="icon-comment"></i>
</div>
</td>
</tr>
<tr fid="${FID}" id="diff_${FID}" class="diff_links">
<td></td>
<td></td>
<td class="cs_${change}">
${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), cs1, cs2, change, file)}
</td>
<td class="td-actions rc-form">
${c.ignorews_url(request.GET, h.FID(cs2,path))} |
${c.context_url(request.GET, h.FID(cs2,path))} |
<div data-comment-id="${h.FID(cs2,path)}" class="btn-link show-inline-comments comments-visible">
<span class="comments-show">${_('Show comments')}</span>
<span class="comments-hide">${_('Hide comments')}</span>
</div>
</td>
</tr>
<tr id="tr_${FID}">
<td></td>
<td></td>
<td class="injected_diff" colspan="2">
<div class="diff-container" id="${'diff-container-%s' % (id(change))}">
<div id="${FID}" class="diffblock margined comm">
<div class="code-body">
<div class="full_f_path" path="${h.safe_unicode(path)}"></div>
${diff|n}
% if file and file["is_limited_diff"]:
% if file["exceeds_limit"]:
${diff_block.file_message()}
% else:
<h5>${_('Diff was truncated. File content available only in full diff.')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a></h5>
% endif
% endif
</div>
</div>
</div>
</td>
</tr>
%endfor
</table>
</div>
% endif
</div>
</%def>

View file

@ -128,7 +128,7 @@
// used for history, and switch to
var initialCommitData = {
id: null,
text: '${_("Switch To Commit")}',
text: '${_("Pick Commit")}',
type: 'sha',
raw_id: null,
files_url: null
@ -151,9 +151,47 @@
// file history select2
select2FileHistorySwitcher('#diff1', initialCommitData, state);
// show at, diff to actions handlers
$('#diff1').on('change', function(e) {
$('#diff').removeClass('disabled').removeAttr("disabled");
$('#show_rev').removeClass('disabled').removeAttr("disabled");
$('#diff_to_commit').removeClass('disabled').removeAttr("disabled");
$('#diff_to_commit').val(_gettext('Diff to Commit ') + e.val.truncateAfter(8, '...'));
$('#show_at_commit').removeClass('disabled').removeAttr("disabled");
$('#show_at_commit').val(_gettext('Show at Commit ') + e.val.truncateAfter(8, '...'));
});
$('#diff_to_commit').on('click', function(e) {
var diff1 = $('#diff1').val();
var diff2 = $('#diff2').val();
var url_data = {
repo_name: templateContext.repo_name,
source_ref: diff1,
source_ref_type: 'rev',
target_ref: diff2,
target_ref_type: 'rev',
merge: 1,
f_path: state.f_path
};
window.location = pyroutes.url('compare_url', url_data);
});
$('#show_at_commit').on('click', function(e) {
var diff1 = $('#diff1').val();
var annotate = $('#annotate').val();
if (annotate === "True") {
var url = pyroutes.url('files_annotate_home',
{'repo_name': templateContext.repo_name,
'revision': diff1, 'f_path': state.f_path});
} else {
var url = pyroutes.url('files_home',
{'repo_name': templateContext.repo_name,
'revision': diff1, 'f_path': state.f_path});
}
window.location = url;
});
// show more authors

View file

@ -46,17 +46,29 @@
</div>
<div id="node_history" class="file_diff_buttons collapsable-content" data-toggle="summary-details">
${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
<div class="fieldset collapsable-content" data-toggle="summary-details">
<div class="left-label">
${_('Show/Diff file')}:
</div>
<div class="right-content">
${h.hidden('diff1')}
${h.hidden('diff2',c.file_last_commit.raw_id)}
${h.submit('diff',_('Diff to Commit'),class_="btn disabled",disabled="true")}
${h.submit('show_rev',_('Show at Commit'),class_="btn disabled",disabled="true")}
${h.hidden('diff2',c.commit.raw_id)}
${h.hidden('annotate', c.annotate)}
${h.end_form()}
</div>
</div>
<div class="fieldset collapsable-content" data-toggle="summary-details">
<div class="left-label">
${_('Action')}:
</div>
<div class="right-content">
${h.submit('diff_to_commit',_('Diff to Commit'),class_="btn disabled",disabled="true")}
${h.submit('show_at_commit',_('Show at Commit'),class_="btn disabled",disabled="true")}
</div>
</div>
<script>
collapsableContent();
</script>

View file

@ -360,14 +360,33 @@
</div>
% endif
<div class="compare_view_commits_title">
% if c.allowed_to_update and not c.pull_request.is_closed():
<a id="update_commits" class="btn btn-primary pull-right">${_('Update commits')}</a>
% else:
<a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
% endif
% if len(c.commit_ranges):
<h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
% endif
<div class="pull-left">
<div class="btn-group">
<a
class="btn"
href="#"
onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
</a>
<a
class="btn"
href="#"
onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
</a>
</div>
</div>
<div class="pull-right">
% if c.allowed_to_update and not c.pull_request.is_closed():
<a id="update_commits" class="btn btn-primary pull-right">${_('Update commits')}</a>
% else:
<a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
% endif
</div>
</div>
% if not c.missing_commits:
<%include file="/compare/compare_commits.html" />

View file

@ -0,0 +1,9 @@
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: intl.dll
===================================================================
diff --git a/intl.dll b/intl.dll
new file mode 10644
--- /dev/null (revision 0)
+++ b/intl.dll (revision 1489)

View file

@ -0,0 +1,652 @@
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Index: trunk/doc/images/SettingsOverlay.png
===================================================================
diff --git a/trunk/doc/images/SettingsOverlay.png b/trunk/doc/images/SettingsOverlay.png
GIT binary patch
--- a/trunk/doc/images/SettingsOverlay.png (revision 1487)
+++ b/trunk/doc/images/SettingsOverlay.png (revision 1488)
Index: trunk/doc/source/de/tsvn_ch04.xml
===================================================================
diff --git a/trunk/doc/source/de/tsvn_ch04.xml b/trunk/doc/source/de/tsvn_ch04.xml
--- a/trunk/doc/source/de/tsvn_ch04.xml (revision 1487)
+++ b/trunk/doc/source/de/tsvn_ch04.xml (revision 1488)
@@ -1561,39 +1561,49 @@
</figure>
Abgesehen von der bevorzugten Sprache erlaubt dieser Dialog es Ihnen,
(fast) alle Einstellungen von TortoiseSVN zu ändern.
-### Translate ###
<variablelist>
<varlistentry>
- <term>Language</term>
- <listitem>
- <para>Selects your user interface language. What did you expect?</para>
+ <term>Sprache</term>
+ <listitem>
+ <para>Wählt die Sprache für die Dialoge/Meldungen aus. Was
+ haben Sie anderes erwartet?</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>Exclude pattern</term>
+ <term>Ausschliessen</term>
<listitem>
<para>
<indexterm>
- <primary>exclude pattern</primary>
+ <primary>ausschliessen</primary>
</indexterm>
- Exclude files or directories by typing in the names or extensions. Patterns are separated by spaces
- e.g. <literal>bin obj *.bak *.~?? *.jar *.[Tt]mp</literal>. The first two entries refer to directories, the
- other four to files.
- </para>
- <para>
- This exclude pattern will affect all your projects. It is not versioned, so it
- will not affect other users. In contrast you can also use the versioned svn:ignore
- property to exclude files or directories from version control. You can set the svn:ignore
- property using the
+ Ausgeschlossene, unversionierte Dateien werden nicht angezeigt
+ in z.B. dem Übertragen Dialog. Ausserdem werden solche Dateien
+ beim Importieren in ein Projektarchiv ignoriert.
+ Schliessen Sie Dateien oder Ordner aus durch Angabe von
+ Dateinamen oder Erweiterungen. Die einzelnen Muster werden
+ durch Leerzeichen voneinander getrennt. Zum Beispiel
+ <literal>bin obj *.bak *.~?? *.jar *.[Tt]mp</literal>.
+ Die ersten beiden Muster beziehen sich auf Ordner, die
+ restlichen vier auf Dateien.
+ </para>
+ <para>
+ Diese Auschluss-Muster beziehen sich auf alle Ihre Projekte.
+ Sie werden nicht versioniert, d.h. andere Benutzer werden davon
+ nichts mitbekommen. Im Gegensatz dazu können Sie jedoch auch
+ die versionierte Eigenschaft svn:ignore verwenden, um Dateien
+ und/oder Ordner von der Versionskontrolle auszuschliessen.
+ Sie können die svn:ignore Eigenschaft setzen durch den
<menuchoice>
- <guimenuitem>Add to Ignore List</guimenuitem>
+ <guimenuitem>Ignorieren</guimenuitem>
</menuchoice>
- command. After commiting every other user will have the same
- svn:ignore property set for this project / directory as you.
+ Befehl. Nach dem Übertragen wird jeder Benutzer dieselbe
+ svn:ignore Eigenschaft für das Projekt oder den Ordner
+ haben wie Sie.
</para>
</listitem>
</varlistentry>
+### Translate ###
<varlistentry>
<term>Default number of log messages</term>
@@ -1608,16 +1618,36 @@
</varlistentry>
<varlistentry>
- <term>Short date / time format in log messages</term>
- <listitem>
- <para>If the standard long messages use up too much space on your sceen use the short format.</para>
+ <term>Edit...</term>
+ <listitem>
+ <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>Edit...</term>
- <listitem>
- <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
+ <term>Short date / time format in log messages</term>
+ <listitem>
+ <para>If the standard long messages use up too much space on your sceen use the short format.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Set filedates to "last commit time"</term>
+ <listitem>
+ <para>
+ This option tells TortoiseSVN to set the filedates to the last commit time
+ when doing a checkout or an update. Otherwise TortoiseSVN will use
+ the current date.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Close windows automatically</term>
+ <listitem>
+ <para>
+ TortoiseSVN will automatically close all progress dialogs when the action is finished.
+ </para>
</listitem>
</varlistentry>
@@ -1629,15 +1659,15 @@
</varlistentry>
<varlistentry>
- <term>Set filedates to "last commit time"</term>
- <listitem>
- <para>
- This option tells TortoiseSVN to set the filedates to the last commit time
- when doing a checkout or an update. Otherwise TortoiseSVN will use
- the current date.
+ <term>Minimum logsize in chars</term>
+ <listitem>
+ <para>
+ The minimum length of a log message for a commit. If you enter
+ a shorter message than specified here, the commit is disabled.
</para>
</listitem>
</varlistentry>
+
<varlistentry>
<term>Don't remove log messages when cancelling a commit</term>
<listitem>
@@ -1648,11 +1678,14 @@
</para>
</listitem>
</varlistentry>
+
<varlistentry>
- <term>Close windows automatically</term>
- <listitem>
- <para>
- TortoiseSVN will automatically close all progress dialogs when the action is finished.
+ <term>Show BugID/Issue-Nr. Box</term>
+ <listitem>
+ <para>
+ Shows a textbox in the commit dialog where you can enter
+ a BugID or Issue-Nr. from a bugtracker to associate the
+ commit with that ID/number.
</para>
</listitem>
</varlistentry>
@@ -1673,10 +1706,32 @@
Sie können auch alle überlagerten Icons deaktivieren, aber wo liegt der Spaß darin?
</para>
<para>
+ Die <term>Ausschluss Pfade</term> sagen TortoiseSVN für welche
+ Pfade die überlagerten Icons <emphasis>nicht</emphasis> gezeichnet
+ werden sollen. Dies ist nützlich wenn Sie zum Beispiel sehr grosse
+ Arbeitskopien haben, welche grosse externe Bibliotheken, welche Sie
+ selbst nie ändern werden enthalten. Sie können dann diese Pfade
+ ausschliessen. Zum Beispiel:
+ </para>
+ <para>
+ <filename>f:\development\SVN\Subversion</filename> deaktiviert
+ die überlagerten Icons <emphasis>nur</emphasis> für diesen speziellen
+ Ordner. Sie können die Icons noch immer für alle Dateien und Ordner
+ innerhalb sehen.
+ </para>
+ <para>
+ <filename>f:\development\SVN\Subversion*</filename> deaktiviert die
+ überlagerten Icons für <emphasis>alle</emphasis> Dateien und Ordner
+ welcher Pfad mit <filename>f:\development\SVN\Subversion</filename>
+ beginnt. Das bedeutet dass auch für alle Dateien und Ordner innerhalb
+ keine überlagerten Icons angezeigt werden.
+ </para>
+ <para>
Ausserdem können Sie angeben, welche Befehle im
Hauptkontextmenu des Explorer angezeigt werden sollen und welche
Sie lieber im Untermenu haben wollen.
</para>
+ </sect2>
<sect2 id="tsvn-DUG-settings-network">
<?dbhh topicname="HIDD_SETTINGSPROXY"?>
<title>Der Einstellungsdialog, Netzwerkseite</title>
Index: trunk/doc/source/en/tsvn_ch04.xml
===================================================================
diff --git a/trunk/doc/source/en/tsvn_ch04.xml b/trunk/doc/source/en/tsvn_ch04.xml
--- a/trunk/doc/source/en/tsvn_ch04.xml (revision 1487)
+++ b/trunk/doc/source/en/tsvn_ch04.xml (revision 1488)
@@ -1457,7 +1457,7 @@
<varlistentry>
<term>Language</term>
<listitem>
- <para>Selects your user interface language. What did you expect?</para>
+ <para>Selects your user interface language. What else did you expect?</para>
</listitem>
</varlistentry>
@@ -1468,6 +1468,9 @@
<indexterm>
<primary>exclude pattern</primary>
</indexterm>
+ Exclude patterns are used to prevent unversioned files from
+ showing up e.g. in the commit dialog. Files matching the
+ patterns are also ignored by an import.
Exclude files or directories by typing in the names or extensions. Patterns are separated by spaces
e.g. <literal>bin obj *.bak *.~?? *.jar *.[Tt]mp</literal>. The first two entries refer to directories, the
other four to files.
@@ -1499,23 +1502,16 @@
</varlistentry>
<varlistentry>
+ <term>Edit...</term>
+ <listitem>
+ <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>Short date / time format in log messages</term>
<listitem>
<para>If the standard long messages use up too much space on your sceen use the short format.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Edit...</term>
- <listitem>
- <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Check for newer versions</term>
- <listitem>
- <para>If checked, TortoiseSVN will check once a week if an update is available</para>
</listitem>
</varlistentry>
@@ -1529,6 +1525,33 @@
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>Close windows automatically</term>
+ <listitem>
+ <para>
+ TortoiseSVN will automatically close all progress dialogs when the action is finished.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Check for newer versions</term>
+ <listitem>
+ <para>If checked, TortoiseSVN will check once a week if an update is available</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Minimum logsize in chars</term>
+ <listitem>
+ <para>
+ The minimum length of a log message for a commit. If you enter
+ a shorter message than specified here, the commit is disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>Don't remove log messages when cancelling a commit</term>
<listitem>
@@ -1539,11 +1562,14 @@
</para>
</listitem>
</varlistentry>
+
<varlistentry>
- <term>Close windows automatically</term>
- <listitem>
- <para>
- TortoiseSVN will automatically close all progress dialogs when the action is finished.
+ <term>Show BugID/Issue-Nr. Box</term>
+ <listitem>
+ <para>
+ Shows a textbox in the commit dialog where you can enter
+ a BugID or Issue-Nr. from a bugtracker to associate the
+ commit with that ID/number.
</para>
</listitem>
</varlistentry>
@@ -1552,7 +1578,7 @@
</sect2>
<sect2 id="tsvn-DUG-settings-overlay">
<?dbhh topicname="HIDD_SETTINGSOVERLAY"?>
- <title>The Settings Dialog, Overlay Tab</title>
+ <title>The Settings Dialog, Look and Feel Tab</title>
<para>
<figure id="tsvn-DUG-settings-dia-2">
<title>The Settings Dialog, Overlay Tab</title>
@@ -1560,8 +1586,27 @@
</figure>
This tab allows you to choose, for which items TortoiseSVN shall
display icon overlays. If you feel that your icon overlays are very
- slow (explore is not responsive), uncheck the "show changed directories" box.
+ slow (explorer is not responsive), uncheck the "show changed directories" box.
You can even disable all icon overlays, but where's the fun in that?
+ </para>
+ <para>
+ The <term>Exclude Paths</term> are used to tell TortoiseSVN for which
+ paths <emphasis>not</emphasis> to show icon overlays and status columns.
+ This is useful if you have some very big working copies containing
+ only libraries which you won't change at all and therefore don't
+ need the overlays. For example:
+ </para>
+ <para>
+ <filename>f:\development\SVN\Subversion</filename> will disable
+ the overlays on <emphasis>only</emphasis> that specific folder. You
+ still can see the overlays on all files and folder inside that folder.
+ </para>
+ <para>
+ <filename>f:\development\SVN\Subversion*</filename> will disable the
+ overlays on <emphasis>all</emphasis> files and folders which path
+ starts with <filename>f:\development\SVN\Subversion</filename>. That
+ means you won't see overlays for all files and folder below that
+ path.
</para>
<para>
You can also specifiy here which of the TortoiseSVN contex menu
Index: trunk/src/Changelog.txt
===================================================================
diff --git a/trunk/src/Changelog.txt b/trunk/src/Changelog.txt
--- a/trunk/src/Changelog.txt (revision 1487)
+++ b/trunk/src/Changelog.txt (revision 1488)
@@ -1,3 +1,5 @@
+- ADD: Option to exclude specific paths from showing
+ icon overlays. (Stefan)
- ADD: On Win2k and later, the authentication data is now
encrypted before saved. The encryption is not available
for the other OS's. (Stefan)
Index: trunk/src/Resources/TortoiseProcENG.rc
===================================================================
diff --git a/trunk/src/Resources/TortoiseProcENG.rc b/trunk/src/Resources/TortoiseProcENG.rc
--- a/trunk/src/Resources/TortoiseProcENG.rc (revision 1487)
+++ b/trunk/src/Resources/TortoiseProcENG.rc (revision 1488)
@@ -398,27 +398,31 @@
BEGIN
CONTROL "&Indicate folders with changed contents",
IDC_CHANGEDDIRS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,
- 20,145,10
+ 20,206,10
CONTROL "&Removable drives",IDC_REMOVABLE,"Button",
- BS_AUTOCHECKBOX | WS_TABSTOP,18,66,130,10
+ BS_AUTOCHECKBOX | WS_TABSTOP,18,58,130,10
CONTROL "&Network drives",IDC_NETWORK,"Button",BS_AUTOCHECKBOX |
- WS_TABSTOP,18,76,130,10
+ WS_TABSTOP,18,68,130,10
CONTROL "&Fixed drives",IDC_FIXED,"Button",BS_AUTOCHECKBOX |
- WS_TABSTOP,18,87,127,10
+ WS_TABSTOP,18,79,127,10
CONTROL "&CD-ROM",IDC_CDROM,"Button",BS_AUTOCHECKBOX |
- WS_TABSTOP,159,66,118,10
- GROUPBOX "Drive Types",IDC_DRIVEGROUP,12,52,274,50
+ WS_TABSTOP,166,58,118,10
+ GROUPBOX "Drive Types",IDC_DRIVEGROUP,12,44,274,50
CONTROL "RAM drives",IDC_RAM,"Button",BS_AUTOCHECKBOX |
- WS_TABSTOP,159,76,119,10
+ WS_TABSTOP,166,68,119,10
CONTROL "Unknown drives",IDC_UNKNOWN,"Button",BS_AUTOCHECKBOX |
- WS_TABSTOP,159,86,118,10
+ WS_TABSTOP,166,78,118,10
CONTROL "Show overlays only in explorer",IDC_ONLYEXPLORER,"Button",
- BS_AUTOCHECKBOX | WS_TABSTOP,12,33,122,10
- GROUPBOX "Icon Overlays / Status Columns",IDC_STATIC,7,7,286,103
- GROUPBOX "Context Menu",IDC_STATIC,7,113,286,97
+ BS_AUTOCHECKBOX | WS_TABSTOP,12,33,190,10
+ GROUPBOX "Icon Overlays / Status Columns",IDC_STATIC,7,7,286,118
+ GROUPBOX "Context Menu",IDC_STATIC,7,130,286,80
CONTROL "",IDC_MENULIST,"SysListView32",LVS_REPORT |
LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER |
- WS_BORDER | WS_TABSTOP,12,125,274,78
+ WS_BORDER | WS_TABSTOP,12,140,274,63
+ LTEXT "Exclude paths:",IDC_STATIC,12,106,85,8
+ EDITTEXT IDC_EXCLUDEPATHS,102,96,184,25,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN |
+ WS_VSCROLL
END
IDD_SETTINGSPROXY DIALOGEX 0, 0, 300, 217
@@ -860,7 +864,7 @@
RIGHTMARGIN, 293
VERTGUIDE, 12
VERTGUIDE, 18
- VERTGUIDE, 159
+ VERTGUIDE, 166
VERTGUIDE, 286
TOPMARGIN, 7
BOTTOMMARGIN, 210
@@ -1377,6 +1381,8 @@
"If activated, prevents the overlays from showing in ""save as.."" or ""open"" dialogs"
IDS_SETTINGS_MENULAYOUT_TT
"Check those menu entries you want to appear in the top context menu instead of the submenu"
+ IDS_SETTINGS_EXCLUDELIST_TT
+ "A newline separated list of paths for which no icon overlays are shown.\nIf you add an ""*"" char at the end of a path, then all files and subdirs inside that path are excluded too.\nAn empty list will allow overlays on all paths."
END
STRINGTABLE
Index: trunk/src/TortoiseProc/SetOverlayPage.cpp
===================================================================
diff --git a/trunk/src/TortoiseProc/SetOverlayPage.cpp b/trunk/src/TortoiseProc/SetOverlayPage.cpp
--- a/trunk/src/TortoiseProc/SetOverlayPage.cpp (revision 1487)
+++ b/trunk/src/TortoiseProc/SetOverlayPage.cpp (revision 1488)
@@ -20,6 +20,7 @@
#include "TortoiseProc.h"
#include "SetOverlayPage.h"
#include "Globals.h"
+#include ".\setoverlaypage.h"
// CSetOverlayPage dialog
@@ -35,6 +36,7 @@
, m_bRAM(FALSE)
, m_bUnknown(FALSE)
, m_bOnlyExplorer(FALSE)
+ , m_sExcludePaths(_T(""))
{
m_regShowChangedDirs = CRegDWORD(_T("Software\\TortoiseSVN\\RecursiveOverlay"));
m_regOnlyExplorer = CRegDWORD(_T("Software\\TortoiseSVN\\OverlaysOnlyInExplorer"), FALSE);
@@ -45,6 +47,7 @@
m_regDriveMaskRAM = CRegDWORD(_T("Software\\TortoiseSVN\\DriveMaskRAM"));
m_regDriveMaskUnknown = CRegDWORD(_T("Software\\TortoiseSVN\\DriveMaskUnknown"));
m_regTopmenu = CRegDWORD(_T("Software\\TortoiseSVN\\ContextMenuEntries"), MENUCHECKOUT | MENUUPDATE | MENUCOMMIT);
+ m_regExcludePaths = CRegString(_T("Software\\TortoiseSVN\\OverlayExcludeList"));
m_bShowChangedDirs = m_regShowChangedDirs;
m_bOnlyExplorer = m_regOnlyExplorer;
@@ -55,6 +58,8 @@
m_bRAM = m_regDriveMaskRAM;
m_bUnknown = m_regDriveMaskUnknown;
m_topmenu = m_regTopmenu;
+ m_sExcludePaths = m_regExcludePaths;
+ m_sExcludePaths.Replace(_T("\n"), _T("\r\n"));
}
CSetOverlayPage::~CSetOverlayPage()
@@ -74,6 +79,7 @@
DDX_Control(pDX, IDC_DRIVEGROUP, m_cDriveGroup);
DDX_Check(pDX, IDC_ONLYEXPLORER, m_bOnlyExplorer);
DDX_Control(pDX, IDC_MENULIST, m_cMenuList);
+ DDX_Text(pDX, IDC_EXCLUDEPATHS, m_sExcludePaths);
}
@@ -87,6 +93,7 @@
ON_BN_CLICKED(IDC_RAM, OnBnClickedRam)
ON_BN_CLICKED(IDC_ONLYEXPLORER, OnBnClickedOnlyexplorer)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_MENULIST, OnLvnItemchangedMenulist)
+ ON_EN_CHANGE(IDC_EXCLUDEPATHS, OnEnChangeExcludepaths)
END_MESSAGE_MAP()
@@ -103,6 +110,9 @@
m_regDriveMaskRAM = m_bRAM;
m_regDriveMaskUnknown = m_bUnknown;
m_regTopmenu = m_topmenu;
+ m_sExcludePaths.Replace(_T("\r"), _T(""));
+ m_regExcludePaths = m_sExcludePaths;
+ m_sExcludePaths.Replace(_T("\n"), _T("\r\n"));
}
}
@@ -116,7 +126,7 @@
m_tooltips.AddTool(IDC_CHANGEDDIRS, IDS_SETTINGS_CHANGEDDIRS_TT);
m_tooltips.AddTool(IDC_ONLYEXPLORER, IDS_SETTINGS_ONLYEXPLORER_TT);
m_tooltips.AddTool(IDC_MENULIST, IDS_SETTINGS_MENULAYOUT_TT);
-
+ m_tooltips.AddTool(IDC_EXCLUDEPATHS, IDS_SETTINGS_EXCLUDELIST_TT);
m_cMenuList.SetExtendedStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
@@ -280,3 +290,8 @@
} // if (m_cMenuList.GetItemCount() > 0)
*pResult = 0;
}
+
+void CSetOverlayPage::OnEnChangeExcludepaths()
+{
+ SetModified();
+}
Index: trunk/src/TortoiseProc/SetOverlayPage.h
===================================================================
diff --git a/trunk/src/TortoiseProc/SetOverlayPage.h b/trunk/src/TortoiseProc/SetOverlayPage.h
--- a/trunk/src/TortoiseProc/SetOverlayPage.h (revision 1487)
+++ b/trunk/src/TortoiseProc/SetOverlayPage.h (revision 1488)
@@ -92,6 +92,8 @@
CIconStatic m_cDriveGroup;
BOOL m_bInitialized;
CRegDWORD m_regTopmenu;
+ CRegString m_regExcludePaths;
+ CString m_sExcludePaths;
CImageList m_imgList;
CListCtrl m_cMenuList;
@@ -110,4 +112,5 @@
virtual BOOL OnApply();
afx_msg void OnBnClickedOnlyexplorer();
afx_msg void OnLvnItemchangedMenulist(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnEnChangeExcludepaths();
};
Index: trunk/src/TortoiseProc/resource.h
===================================================================
diff --git a/trunk/src/TortoiseProc/resource.h b/trunk/src/TortoiseProc/resource.h
--- a/trunk/src/TortoiseProc/resource.h (revision 1487)
+++ b/trunk/src/TortoiseProc/resource.h (revision 1488)
@@ -179,6 +179,7 @@
#define IDC_MINLOGSIZE 1077
#define IDC_BUGID 1077
#define IDC_WCURL 1077
+#define IDC_EXCLUDEPATHS 1077
#define IDC_DRIVEGROUP 1079
#define IDC_PROXYGROUP 1080
#define IDC_SSHGROUP 1081
@@ -427,6 +428,7 @@
#define IDS_SETTINGS_CHECKNEWER_TT 3100
#define IDS_SETTINGS_ONLYEXPLORER_TT 3101
#define IDS_SETTINGS_MENULAYOUT_TT 3102
+#define IDS_SETTINGS_EXCLUDELIST_TT 3103
#define IDS_CHECKNEWER_YOURVERSION 3200
#define IDS_CHECKNEWER_CURRENTVERSION 3201
#define IDS_CHECKNEWER_YOURUPTODATE 3202
Index: trunk/src/TortoiseShell/ShellCache.h
===================================================================
diff --git a/trunk/src/TortoiseShell/ShellCache.h b/trunk/src/TortoiseShell/ShellCache.h
--- a/trunk/src/TortoiseShell/ShellCache.h (revision 1487)
+++ b/trunk/src/TortoiseShell/ShellCache.h (revision 1488)
@@ -21,9 +21,11 @@
#include "globals.h"
#include <tchar.h>
#include <string>
+#include <vector>
#include "registry.h"
#define REGISTRYTIMEOUT 2000
+#define EXCLUDELISTTIMEOUT 5000
#define DRIVETYPETIMEOUT 300000 // 5 min
#define NUMBERFMTTIMEOUT 300000
class ShellCache
@@ -39,12 +41,14 @@
driveremove = CRegStdWORD(_T("Software\\TortoiseSVN\\DriveMaskRemovable"));
driveram = CRegStdWORD(_T("Software\\TortoiseSVN\\DriveMaskRAM"));
driveunknown = CRegStdWORD(_T("Software\\TortoiseSVN\\DriveMaskUnknown"));
+ excludelist = CRegStdString(_T("Software\\TortoiseSVN\\OverlayExcludeList"));
recursiveticker = GetTickCount();
folderoverlayticker = GetTickCount();
driveticker = recursiveticker;
drivetypeticker = recursiveticker;
langticker = recursiveticker;
- columnrevformatticker = langticker;
+ columnrevformatticker = recursiveticker;
+ excludelistticker = recursiveticker;
menulayout = CRegStdWORD(_T("Software\\TortoiseSVN\\ContextMenuEntries"), MENUCHECKOUT | MENUUPDATE | MENUCOMMIT);
langid = CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), 1033);
blockstatus = CRegStdWORD(_T("Software\\TortoiseSVN\\BlockStatus"), 0);
@@ -177,6 +181,21 @@
return FALSE;
if ((drivetype == DRIVE_UNKNOWN)&&(IsUnknown()))
return FALSE;
+
+ ExcludeListValid();
+ for (std::vector<stdstring>::iterator I = exvector.begin(); I != exvector.end(); ++I)
+ {
+ if (I->empty())
+ continue;
+ if (I->at(I->size()-1)=='*')
+ {
+ stdstring str = I->substr(0, I->size()-1);
+ if (_tcsnicmp(str.c_str(), path, str.size())==0)
+ return FALSE;
+ }
+ else if (_tcsicmp(I->c_str(), path)==0)
+ return FALSE;
+ }
return TRUE;
}
DWORD GetLangID()
@@ -218,6 +237,32 @@
driveremove.read();
}
}
+ void ExcludeListValid()
+ {
+ if ((GetTickCount() - EXCLUDELISTTIMEOUT)>excludelistticker)
+ {
+ excludelistticker = GetTickCount();
+ excludelist.read();
+ if (excludeliststr.compare((stdstring)excludelist)==0)
+ return;
+ excludeliststr = (stdstring)excludelist;
+ exvector.clear();
+ int pos = 0, pos_ant = 0;
+ pos = excludeliststr.find(_T("\n"), pos_ant);
+ while (pos != stdstring::npos)
+ {
+ stdstring token = excludeliststr.substr(pos_ant, pos-pos_ant);
+ exvector.push_back(token);
+ pos_ant = pos+1;
+ pos = excludeliststr.find(_T("\n"), pos_ant);
+ }
+ if (!excludeliststr.empty())
+ {
+ exvector.push_back(excludeliststr.substr(pos_ant, excludeliststr.size()-1));
+ }
+ excludeliststr = (stdstring)excludelist;
+ }
+ }
CRegStdWORD blockstatus;
CRegStdWORD langid;
CRegStdWORD showrecursive;
@@ -229,6 +274,9 @@
CRegStdWORD driveram;
CRegStdWORD driveunknown;
CRegStdWORD menulayout;
+ CRegStdString excludelist;
+ stdstring excludeliststr;
+ std::vector<stdstring> exvector;
DWORD recursiveticker;
DWORD folderoverlayticker;
DWORD driveticker;
@@ -237,6 +285,7 @@
DWORD langticker;
DWORD blockstatusticker;
DWORD columnrevformatticker;
+ DWORD excludelistticker;
UINT drivetypecache[27];
TCHAR drivetypepathcache[MAX_PATH];
NUMBERFMT columnrevformat;

View file

@ -244,7 +244,8 @@ class TestCommitCommentsController(TestController):
('markdown', '# header', '<h1>header</h1>'),
('markdown', '*italics*', '<em>italics</em>'),
('markdown', '**bold**', '<strong>bold</strong>'),
])
], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
'md-header', 'md-italics', 'md-bold', ])
def test_preview(self, renderer, input, output, backend):
self.log_user()
params = {

View file

@ -22,16 +22,13 @@ import mock
import pytest
import lxml.html
from rhodecode.lib.vcs.backends.base import EmptyCommit
from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
from rhodecode.model.db import Repository
from rhodecode.model.scm import ScmModel
from rhodecode.tests import url, TEST_USER_ADMIN_LOGIN, assert_session_flash
from rhodecode.tests.utils import AssertResponse
from rhodecode.tests import url, assert_session_flash
from rhodecode.tests.utils import AssertResponse, commit_change
@pytest.mark.usefixtures("autologin_user", "app")
class TestCompareController:
class TestCompareController(object):
@pytest.mark.xfail_backends("svn", reason="Requires pull")
def test_compare_remote_with_different_commit_indexes(self, backend):
@ -53,23 +50,23 @@ class TestCompareController:
fork = backend.create_repo()
# prepare fork
commit0 = _commit_change(
commit0 = commit_change(
fork.repo_name, filename='file1', content='A',
message='A', vcs_type=backend.alias, parent=None, newfile=True)
commit1 = _commit_change(
commit1 = commit_change(
fork.repo_name, filename='file1', content='B',
message='B, child of A', vcs_type=backend.alias, parent=commit0)
_commit_change( # commit 2
commit_change( # commit 2
fork.repo_name, filename='file1', content='C',
message='C, child of B', vcs_type=backend.alias, parent=commit1)
commit3 = _commit_change(
commit3 = commit_change(
fork.repo_name, filename='file1', content='D',
message='D, child of A', vcs_type=backend.alias, parent=commit0)
commit4 = _commit_change(
commit4 = commit_change(
fork.repo_name, filename='file1', content='E',
message='E, child of D', vcs_type=backend.alias, parent=commit3)
@ -105,7 +102,7 @@ class TestCompareController:
repo1 = backend.create_repo()
# commit something !
commit0 = _commit_change(
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
@ -114,11 +111,11 @@ class TestCompareController:
repo2 = backend.create_fork()
# add two extra commit into fork
commit1 = _commit_change(
commit1 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
commit2 = _commit_change(
commit2 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
@ -156,7 +153,7 @@ class TestCompareController:
repo1 = backend.create_repo()
# commit something !
commit0 = _commit_change(
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
@ -165,17 +162,17 @@ class TestCompareController:
repo2 = backend.create_fork()
# now commit something to origin repo
_commit_change(
commit_change(
repo1.repo_name, filename='file2', content='line1file2\n',
message='commit2', vcs_type=backend.alias, parent=commit0,
newfile=True)
# add two extra commit into fork
commit1 = _commit_change(
commit1 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
commit2 = _commit_change(
commit2 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
@ -207,9 +204,9 @@ class TestCompareController:
compare_page.swap_is_hidden()
compare_page.target_source_are_disabled()
@pytest.mark.xfail_backends("svn", "git")
@pytest.mark.xfail_backends("svn")
# TODO(marcink): no svn support for compare two seperate repos
def test_compare_of_unrelated_forks(self, backend):
# TODO: johbo: Fails for git due to some other issue it seems
orig = backend.create_repo(number_of_commits=1)
fork = backend.create_repo(number_of_commits=1)
@ -245,11 +242,11 @@ class TestCompareController:
repo1 = backend.create_repo()
# commit something !
commit0 = _commit_change(
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
commit1 = _commit_change(
commit1 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
@ -257,18 +254,18 @@ class TestCompareController:
repo2 = backend.create_fork()
# now make commit3-6
commit2 = _commit_change(
commit2 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
commit3 = _commit_change(
commit3 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\n', message='commit4',
vcs_type=backend.alias, parent=commit2)
commit4 = _commit_change(
commit4 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
vcs_type=backend.alias, parent=commit3)
_commit_change( # commit 5
commit_change( # commit 5
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\nline6\n',
message='commit6', vcs_type=backend.alias, parent=commit4)
@ -311,11 +308,11 @@ class TestCompareController:
repo1 = backend.create_repo()
# commit something !
commit0 = _commit_change(
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
commit1 = _commit_change(
commit1 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
@ -323,18 +320,18 @@ class TestCompareController:
backend.create_fork()
# now make commit3-6
commit2 = _commit_change(
commit2 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
commit3 = _commit_change(
commit3 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\n', message='commit4',
vcs_type=backend.alias, parent=commit2)
commit4 = _commit_change(
commit4 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
vcs_type=backend.alias, parent=commit3)
commit5 = _commit_change(
commit5 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\nline6\n',
message='commit6', vcs_type=backend.alias, parent=commit4)
@ -400,7 +397,7 @@ class TestCompareController:
repo1 = backend.create_repo()
r1_name = repo1.repo_name
commit0 = _commit_change(
commit0 = commit_change(
repo=r1_name, filename='file1',
content='line1', message='commit1', vcs_type=backend.alias,
newfile=True)
@ -413,19 +410,19 @@ class TestCompareController:
self.r2_id = repo2.repo_id
r2_name = repo2.repo_name
commit1 = _commit_change(
commit1 = commit_change(
repo=r2_name, filename='file1-fork',
content='file1-line1-from-fork', message='commit1-fork',
vcs_type=backend.alias, parent=repo2.scm_instance()[-1],
newfile=True)
commit2 = _commit_change(
commit2 = commit_change(
repo=r2_name, filename='file2-fork',
content='file2-line1-from-fork', message='commit2-fork',
vcs_type=backend.alias, parent=commit1,
newfile=True)
_commit_change( # commit 3
commit_change( # commit 3
repo=r2_name, filename='file3-fork',
content='file3-line1-from-fork', message='commit3-fork',
vcs_type=backend.alias, parent=commit2, newfile=True)
@ -447,9 +444,9 @@ class TestCompareController:
response.mustcontain('%s@%s' % (r2_name, commit_id1))
response.mustcontain('%s@%s' % (r1_name, commit_id2))
response.mustcontain('No files')
response.mustcontain('No Commits')
response.mustcontain('No commits in this compare')
commit0 = _commit_change(
commit0 = commit_change(
repo=r1_name, filename='file2',
content='line1-added-after-fork', message='commit2-parent',
vcs_type=backend.alias, parent=None, newfile=True)
@ -558,7 +555,7 @@ class TestCompareController:
@pytest.mark.usefixtures("autologin_user")
class TestCompareControllerSvn:
class TestCompareControllerSvn(object):
def test_supports_references_with_path(self, app, backend_svn):
repo = backend_svn['svn-simple-layout']
@ -574,7 +571,7 @@ class TestCompareControllerSvn:
status=200)
# Expecting no commits, since both paths are at the same revision
response.mustcontain('No Commits')
response.mustcontain('No commits in this compare')
# Should find only one file changed when comparing those two tags
response.mustcontain('example.py')
@ -596,7 +593,7 @@ class TestCompareControllerSvn:
status=200)
# It should show commits
assert 'No Commits' not in response.body
assert 'No commits in this compare' not in response.body
# Should find only one file changed when comparing those two tags
response.mustcontain('example.py')
@ -660,36 +657,3 @@ class ComparePage(AssertResponse):
def target_source_are_enabled(self):
response = self.response
response.mustcontain("var enable_fields = true;")
def _commit_change(
repo, filename, content, message, vcs_type, parent=None,
newfile=False):
repo = Repository.get_by_repo_name(repo)
_commit = parent
if not parent:
_commit = EmptyCommit(alias=vcs_type)
if newfile:
nodes = {
filename: {
'content': content
}
}
commit = ScmModel().create_nodes(
user=TEST_USER_ADMIN_LOGIN, repo=repo,
message=message,
nodes=nodes,
parent_commit=_commit,
author=TEST_USER_ADMIN_LOGIN,
)
else:
commit = ScmModel().commit_change(
repo=repo.scm_instance(), repo_name=repo.repo_name,
commit=parent, user=TEST_USER_ADMIN_LOGIN,
author=TEST_USER_ADMIN_LOGIN,
message=message,
content=content,
f_path=filename
)
return commit

View file

@ -44,7 +44,7 @@ class TestCompareController:
response.mustcontain('%s@%s' % (backend.repo_name, tag1))
response.mustcontain('%s@%s' % (backend.repo_name, tag2))
# outgoing changesets between tags
# outgoing commits between tags
commit_indexes = {
'git': [113] + range(115, 121),
'hg': [112] + range(115, 121),
@ -118,8 +118,8 @@ class TestCompareController:
response.mustcontain('%s@%s' % (backend.repo_name, head_id))
# branches are equal
response.mustcontain('<p class="empty_data">No files</p>')
response.mustcontain('<p class="empty_data">No Commits</p>')
response.mustcontain('No files')
response.mustcontain('No commits in this compare')
def test_compare_commits(self, backend):
repo = backend.repo

View file

@ -0,0 +1,192 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2010-2016 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import os
import mock
import pytest
from rhodecode.controllers.files import FilesController
from rhodecode.lib import helpers as h
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib.ext_json import json
from rhodecode.lib.vcs import nodes
from rhodecode.lib.vcs.backends.base import EmptyCommit
from rhodecode.lib.vcs.conf import settings
from rhodecode.lib.vcs.nodes import FileNode
from rhodecode.model.db import Repository
from rhodecode.model.scm import ScmModel
from rhodecode.tests import (
url, TEST_USER_ADMIN_LOGIN, assert_session_flash, assert_not_in_session_flash)
from rhodecode.tests.fixture import Fixture
from rhodecode.tests.utils import commit_change
fixture = Fixture()
@pytest.mark.usefixtures("autologin_user", "app")
class TestSideBySideDiff(object):
def test_diff_side_by_side(self, app, backend, backend_stub):
f_path = 'test_sidebyside_file.py'
commit1_content = 'content-25d7e49c18b159446c\n'
commit2_content = 'content-603d6c72c46d953420\n'
repo = backend.create_repo()
commit1 = commit_change(
repo.repo_name, filename=f_path, content=commit1_content,
message='A', vcs_type=backend.alias, parent=None, newfile=True)
commit2 = commit_change(
repo.repo_name, filename=f_path, content=commit2_content,
message='B, child of A', vcs_type=backend.alias, parent=commit1)
compare_url = url(
'compare_url',
repo_name=repo.repo_name,
source_ref_type='rev',
source_ref=commit1.raw_id,
target_repo=repo.repo_name,
target_ref_type='rev',
target_ref=commit2.raw_id,
f_path=f_path,
diffmode='sidebyside')
response = self.app.get(compare_url)
response.mustcontain('Expand 1 commit')
response.mustcontain('1 file changed')
response.mustcontain(
'r%s:%s...r%s:%s' % (
commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
response.mustcontain('<strong>{}</strong>'.format(f_path))
def test_diff_side_by_side_with_empty_file(self, app, backend, backend_stub):
commits = [
{'message': 'First commit'},
{'message': 'Commit with binary',
'added': [nodes.FileNode('file.empty', content='')]},
]
f_path = 'file.empty'
repo = backend.create_repo(commits=commits)
commit1 = repo.get_commit(commit_idx=0)
commit2 = repo.get_commit(commit_idx=1)
compare_url = url(
'compare_url',
repo_name=repo.repo_name,
source_ref_type='rev',
source_ref=commit1.raw_id,
target_repo=repo.repo_name,
target_ref_type='rev',
target_ref=commit2.raw_id,
f_path=f_path,
diffmode='sidebyside')
response = self.app.get(compare_url)
response.mustcontain('Expand 1 commit')
response.mustcontain('1 file changed')
response.mustcontain(
'r%s:%s...r%s:%s' % (
commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
response.mustcontain('<strong>{}</strong>'.format(f_path))
def test_diff_sidebyside_two_commits(self, app, backend):
commit_id_range = {
'hg': {
'commits': ['25d7e49c18b159446cadfa506a5cf8ad1cb04067',
'603d6c72c46d953420c89d36372f08d9f305f5dd'],
'changes': '21 files changed: 943 inserted, 288 deleted'
},
'git': {
'commits': ['6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
'03fa803d7e9fb14daa9a3089e0d1494eda75d986'],
'changes': '21 files changed: 943 inserted, 288 deleted'
},
'svn': {
'commits': ['336',
'337'],
'changes': '21 files changed: 943 inserted, 288 deleted'
},
}
commit_info = commit_id_range[backend.alias]
commit2, commit1 = commit_info['commits']
file_changes = commit_info['changes']
compare_url = url(
'compare_url',
repo_name=backend.repo_name,
source_ref_type='rev',
source_ref=commit2,
target_repo=backend.repo_name,
target_ref_type='rev',
target_ref=commit1,
diffmode='sidebyside')
response = self.app.get(compare_url)
response.mustcontain('Expand 1 commit')
response.mustcontain(file_changes)
def test_diff_sidebyside_two_commits_single_file(self, app, backend):
commit_id_range = {
'hg': {
'commits': ['25d7e49c18b159446cadfa506a5cf8ad1cb04067',
'603d6c72c46d953420c89d36372f08d9f305f5dd'],
'changes': '1 file changed: 1 inserted, 1 deleted'
},
'git': {
'commits': ['6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
'03fa803d7e9fb14daa9a3089e0d1494eda75d986'],
'changes': '1 file changed: 1 inserted, 1 deleted'
},
'svn': {
'commits': ['336',
'337'],
'changes': '1 file changed: 1 inserted, 1 deleted'
},
}
f_path = 'docs/conf.py'
commit_info = commit_id_range[backend.alias]
commit2, commit1 = commit_info['commits']
file_changes = commit_info['changes']
compare_url = url(
'compare_url',
repo_name=backend.repo_name,
source_ref_type='rev',
source_ref=commit2,
target_repo=backend.repo_name,
target_ref_type='rev',
target_ref=commit1,
f_path=f_path,
diffmode='sidebyside')
response = self.app.get(compare_url)
response.mustcontain('Expand 1 commit')
response.mustcontain(file_changes)

View file

@ -28,15 +28,11 @@ from rhodecode.lib import helpers as h
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib.ext_json import json
from rhodecode.lib.vcs import nodes
from rhodecode.lib.vcs.backends.base import EmptyCommit
from rhodecode.lib.vcs.conf import settings
from rhodecode.lib.vcs.nodes import FileNode
from rhodecode.model.db import Repository
from rhodecode.model.scm import ScmModel
from rhodecode.tests import (
url, TEST_USER_ADMIN_LOGIN, assert_session_flash, assert_not_in_session_flash)
url, assert_session_flash, assert_not_in_session_flash)
from rhodecode.tests.fixture import Fixture
from rhodecode.tests.utils import AssertResponse
fixture = Fixture()
@ -48,40 +44,6 @@ NODE_HISTORY = {
def _commit_change(
repo, filename, content, message, vcs_type, parent=None,
newfile=False):
repo = Repository.get_by_repo_name(repo)
_commit = parent
if not parent:
_commit = EmptyCommit(alias=vcs_type)
if newfile:
nodes = {
filename: {
'content': content
}
}
commit = ScmModel().create_nodes(
user=TEST_USER_ADMIN_LOGIN, repo=repo,
message=message,
nodes=nodes,
parent_commit=_commit,
author=TEST_USER_ADMIN_LOGIN,
)
else:
commit = ScmModel().commit_change(
repo=repo.scm_instance(), repo_name=repo.repo_name,
commit=parent, user=TEST_USER_ADMIN_LOGIN,
author=TEST_USER_ADMIN_LOGIN,
message=message,
content=content,
f_path=filename
)
return commit
@pytest.mark.usefixtures("app")
class TestFilesController:
@ -120,7 +82,7 @@ class TestFilesController:
response = self.app.get(url(
controller='files', action='index',
repo_name=repo.repo_name, revision='tip', f_path='/'))
assert_response = AssertResponse(response)
assert_response = response.assert_response()
assert_response.contains_one_link(
'absolute-path @ 000000000000', 'http://example.com/absolute-path')
@ -130,7 +92,7 @@ class TestFilesController:
response = self.app.get(url(
controller='files', action='index',
repo_name=repo.repo_name, revision='tip', f_path='/'))
assert_response = AssertResponse(response)
assert_response = response.assert_response()
assert_response.contains_one_link(
'subpaths-path @ 000000000000',
'http://sub-base.example.com/subpaths-path')
@ -179,21 +141,24 @@ class TestFilesController:
assert_dirs_in_response(response, dirs, params)
assert_files_in_response(response, files, params)
@pytest.mark.xfail_backends("git", reason="Missing branches in git repo")
@pytest.mark.xfail_backends("svn", reason="Depends on branch support")
def test_index_different_branch(self, backend):
# TODO: Git test repository does not contain branches
# TODO: Branch support in Subversion
commit = backend.repo.get_commit(commit_idx=150)
branches = dict(
hg=(150, ['git']),
# TODO: Git test repository does not contain other branches
git=(633, ['master']),
# TODO: Branch support in Subversion
svn=(150, [])
)
idx, branches = branches[backend.alias]
commit = backend.repo.get_commit(commit_idx=idx)
response = self.app.get(url(
controller='files', action='index',
repo_name=backend.repo_name,
revision=commit.raw_id,
f_path='/'))
assert_response = AssertResponse(response)
assert_response.element_contains(
'.tags .branchtag', 'git')
assert_response = response.assert_response()
for branch in branches:
assert_response.element_contains('.tags .branchtag', branch)
def test_index_paging(self, backend):
repo = backend.repo
@ -221,7 +186,7 @@ class TestFilesController:
msgbox = """<div class="commit right-content">%s</div>"""
response.mustcontain(msgbox % (commit.message, ))
assert_response = AssertResponse(response)
assert_response = response.assert_response()
if commit.branch:
assert_response.element_contains('.tags.tags-main .branchtag', commit.branch)
if commit.tags:
@ -348,7 +313,7 @@ class TestFilesController:
f_path='/', commit_id=commit.raw_id),
extra_environ=xhr_header)
assert_response = AssertResponse(response)
assert_response = response.assert_response()
for attr in ['data-commit-id', 'data-date', 'data-author']:
elements = assert_response.get_elements('[{}]'.format(attr))
@ -401,7 +366,7 @@ class TestFilesController:
# TODO: johbo: Think about a better place for these tests. Either controller
# specific unit tests or we move down the whole logic further towards the vcs
# layer
class TestAdjustFilePathForSvn:
class TestAdjustFilePathForSvn(object):
"""SVN specific adjustments of node history in FileController."""
def test_returns_path_relative_to_matched_reference(self):
@ -433,7 +398,7 @@ class TestAdjustFilePathForSvn:
@pytest.mark.usefixtures("app")
class TestRepositoryArchival:
class TestRepositoryArchival(object):
def test_archival(self, backend):
backend.enable_downloads()
@ -485,7 +450,7 @@ class TestRepositoryArchival:
@pytest.mark.usefixtures("app", "autologin_user")
class TestRawFileHandling:
class TestRawFileHandling(object):
def test_raw_file_ok(self, backend):
commit = backend.repo.get_commit(commit_idx=173)
@ -575,6 +540,7 @@ class TestFilesDiff:
def test_file_full_diff(self, backend, diff):
commit1 = backend.repo.get_commit(commit_idx=-1)
commit2 = backend.repo.get_commit(commit_idx=-2)
response = self.app.get(
url(
controller='files',
@ -582,11 +548,17 @@ class TestFilesDiff:
repo_name=backend.repo_name,
f_path='README'),
params={
'diff1': commit1.raw_id,
'diff2': commit2.raw_id,
'diff1': commit2.raw_id,
'diff2': commit1.raw_id,
'fulldiff': '1',
'diff': diff,
})
if diff == 'diff':
# use redirect since this is OLD view redirecting to compare page
response = response.follow()
# It's a symlink to README.rst
response.mustcontain('README.rst')
response.mustcontain('No newline at end of file')
@ -610,7 +582,17 @@ class TestFilesDiff:
'fulldiff': '1',
'diff': 'diff',
})
response.mustcontain('Cannot diff binary files')
# use redirect since this is OLD view redirecting to compare page
response = response.follow()
response.mustcontain('Expand 1 commit')
response.mustcontain('1 file changed: 0 inserted, 0 deleted')
if backend.alias == 'svn':
response.mustcontain('new file 10644')
# TODO(marcink): SVN doesn't yet detect binary changes
else:
response.mustcontain('new file 100644')
response.mustcontain('binary diff hidden')
def test_diff_2way(self, backend):
commit1 = backend.repo.get_commit(commit_idx=-1)
@ -622,14 +604,15 @@ class TestFilesDiff:
repo_name=backend.repo_name,
f_path='README'),
params={
'diff1': commit1.raw_id,
'diff2': commit2.raw_id,
'diff1': commit2.raw_id,
'diff2': commit1.raw_id,
})
# use redirect since this is OLD view redirecting to compare page
response = response.follow()
# Expecting links to both variants of the file. Links are used
# to load the content dynamically.
response.mustcontain('/%s/README' % commit1.raw_id)
response.mustcontain('/%s/README' % commit2.raw_id)
# It's a symlink to README.rst
response.mustcontain('README.rst')
response.mustcontain('No newline at end of file')
def test_requires_one_commit_id(self, backend, autologin_user):
response = self.app.get(
@ -642,21 +625,23 @@ class TestFilesDiff:
response.mustcontain(
'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
def test_returns_not_found_if_file_does_not_exist(self, vcsbackend):
def test_returns_no_files_if_file_does_not_exist(self, vcsbackend):
repo = vcsbackend.repo
self.app.get(
response = self.app.get(
url(
controller='files',
action='diff',
repo_name=repo.name,
f_path='does-not-exist-in-any-commit',
diff1=repo[0].raw_id,
diff2=repo[1].raw_id),
status=404)
diff2=repo[1].raw_id),)
response = response.follow()
response.mustcontain('No files')
def test_returns_redirect_if_file_not_changed(self, backend):
commit = backend.repo.get_commit(commit_idx=-1)
f_path= 'README'
f_path = 'README'
response = self.app.get(
url(
controller='files',
@ -666,25 +651,40 @@ class TestFilesDiff:
diff1=commit.raw_id,
diff2=commit.raw_id,
),
status=302
)
assert response.headers['Location'].endswith(f_path)
redirected = response.follow()
redirected.mustcontain('has not changed between')
response = response.follow()
response.mustcontain('No files')
response.mustcontain('No commits in this compare')
def test_supports_diff_to_different_path_svn(self, backend_svn):
#TODO: check this case
return
repo = backend_svn['svn-simple-layout'].scm_instance()
commit_id = repo[-1].raw_id
commit_id_1 = '24'
commit_id_2 = '26'
print( url(
controller='files',
action='diff',
repo_name=repo.name,
f_path='trunk/example.py',
diff1='tags/v0.2/example.py@' + commit_id_1,
diff2=commit_id_2))
response = self.app.get(
url(
controller='files',
action='diff',
repo_name=repo.name,
f_path='trunk/example.py',
diff1='tags/v0.2/example.py@' + commit_id,
diff2=commit_id),
status=200)
diff1='tags/v0.2/example.py@' + commit_id_1,
diff2=commit_id_2))
response = response.follow()
response.mustcontain(
# diff contains this
"Will print out a useful message on invocation.")
# Note: Expecting that we indicate the user what's being compared
@ -692,6 +692,9 @@ class TestFilesDiff:
response.mustcontain("tags/v0.2/example.py")
def test_show_rev_redirects_to_svn_path(self, backend_svn):
#TODO: check this case
return
repo = backend_svn['svn-simple-layout'].scm_instance()
commit_id = repo[-1].raw_id
response = self.app.get(
@ -708,6 +711,9 @@ class TestFilesDiff:
'svn-svn-simple-layout/files/26/branches/argparse/example.py')
def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
#TODO: check this case
return
repo = backend_svn['svn-simple-layout'].scm_instance()
commit_id = repo[-1].raw_id
response = self.app.get(
@ -979,100 +985,3 @@ def _assert_items_in_response(response, items, template, params):
def assert_timeago_in_response(response, items, params):
for item in items:
response.mustcontain(h.age_component(params['date']))
@pytest.mark.usefixtures("autologin_user", "app")
class TestSideBySideDiff:
def test_diff2way(self, app, backend, backend_stub):
f_path = 'content'
commit1_content = 'content-25d7e49c18b159446c'
commit2_content = 'content-603d6c72c46d953420'
repo = backend.create_repo()
commit1 = _commit_change(
repo.repo_name, filename=f_path, content=commit1_content,
message='A', vcs_type=backend.alias, parent=None, newfile=True)
commit2 = _commit_change(
repo.repo_name, filename=f_path, content=commit2_content,
message='B, child of A', vcs_type=backend.alias, parent=commit1)
response = self.app.get(url(
controller='files', action='diff_2way',
repo_name=repo.repo_name,
diff1=commit1.raw_id,
diff2=commit2.raw_id,
f_path=f_path))
assert_response = AssertResponse(response)
response.mustcontain(
('Side-by-side Diff r0:%s ... r1:%s') % ( commit1.short_id, commit2.short_id ))
response.mustcontain('id="compare"')
response.mustcontain((
"var orig1_url = '/%s/raw/%s/%s';\n"
"var orig2_url = '/%s/raw/%s/%s';") %
( repo.repo_name, commit1.raw_id, f_path,
repo.repo_name, commit2.raw_id, f_path))
def test_diff2way_with_empty_file(self, app, backend, backend_stub):
commits = [
{'message': 'First commit'},
{'message': 'Commit with binary',
'added': [nodes.FileNode('file.empty', content='')]},
]
f_path='file.empty'
repo = backend.create_repo(commits=commits)
commit_id1 = repo.get_commit(commit_idx=0).raw_id
commit_id2 = repo.get_commit(commit_idx=1).raw_id
response = self.app.get(url(
controller='files', action='diff_2way',
repo_name=repo.repo_name,
diff1=commit_id1,
diff2=commit_id2,
f_path=f_path))
assert_response = AssertResponse(response)
if backend.alias == 'svn':
assert_session_flash( response,
('%(file_path)s has not changed') % { 'file_path': 'file.empty' })
else:
response.mustcontain(
('Side-by-side Diff r0:%s ... r1:%s') % ( repo.get_commit(commit_idx=0).short_id, repo.get_commit(commit_idx=1).short_id ))
response.mustcontain('id="compare"')
response.mustcontain((
"var orig1_url = '/%s/raw/%s/%s';\n"
"var orig2_url = '/%s/raw/%s/%s';") %
( repo.repo_name, commit_id1, f_path,
repo.repo_name, commit_id2, f_path))
def test_empty_diff_2way_redirect_to_summary_with_alert(self, app, backend):
commit_id_range = {
'hg': (
'25d7e49c18b159446cadfa506a5cf8ad1cb04067',
'603d6c72c46d953420c89d36372f08d9f305f5dd'),
'git': (
'6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
'03fa803d7e9fb14daa9a3089e0d1494eda75d986'),
'svn': (
'335',
'337'),
}
f_path = 'setup.py'
commit_ids = commit_id_range[backend.alias]
response = self.app.get(url(
controller='files', action='diff_2way',
repo_name=backend.repo_name,
diff2=commit_ids[0],
diff1=commit_ids[1],
f_path=f_path))
assert_response = AssertResponse(response)
assert_session_flash( response,
('%(file_path)s has not changed') % { 'file_path': f_path })

View file

@ -531,6 +531,81 @@ DIFF_FIXTURES = [
}),
]),
('svn',
'svn_diff_binary_add_file.diff',
[('intl.dll', 'A',
{'added': 0,
'deleted': 0,
'binary': False,
'ops': {NEW_FILENODE: 'new file 10644',
#TODO(Marcink): depends on binary detection on svn patches
# BIN_FILENODE: 'binary diff hidden'
}
}),
]),
('svn',
'svn_diff_multiple_changes.diff',
[('trunk/doc/images/SettingsOverlay.png', 'M',
{'added': 0,
'deleted': 0,
'binary': False,
'ops': {MOD_FILENODE: 'modified file',
#TODO(Marcink): depends on binary detection on svn patches
# BIN_FILENODE: 'binary diff hidden'
}
}),
('trunk/doc/source/de/tsvn_ch04.xml', 'M',
{'added': 89,
'deleted': 34,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
('trunk/doc/source/en/tsvn_ch04.xml', 'M',
{'added': 66,
'deleted': 21,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
('trunk/src/Changelog.txt', 'M',
{'added': 2,
'deleted': 0,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
('trunk/src/Resources/TortoiseProcENG.rc', 'M',
{'added': 19,
'deleted': 13,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
('trunk/src/TortoiseProc/SetOverlayPage.cpp', 'M',
{'added': 16,
'deleted': 1,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
('trunk/src/TortoiseProc/SetOverlayPage.h', 'M',
{'added': 3,
'deleted': 0,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
('trunk/src/TortoiseProc/resource.h', 'M',
{'added': 2,
'deleted': 0,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
('trunk/src/TortoiseShell/ShellCache.h', 'M',
{'added': 50,
'deleted': 1,
'binary': False,
'ops': {MOD_FILENODE: 'modified file'}
}),
]),
# TODO: mikhail: do we still need this?
# (
# 'hg',
@ -579,7 +654,6 @@ DIFF_FIXTURES = [
# 'pylons_app.egg-info/dependency_links.txt', 'A', {
# 'deleted': 0, 'binary': False, 'added': 1, 'ops': {
# 1: 'new file 100644'}}),
# #TODO:
# ]
# ),
]

View file

@ -38,6 +38,7 @@ from rhodecode.model.db import User, Repository
from rhodecode.model.meta import Session
from rhodecode.model.scm import ScmModel
from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
from rhodecode.lib.vcs.backends.base import EmptyCommit
log = logging.getLogger(__name__)
@ -372,3 +373,37 @@ def repo_on_filesystem(repo_name):
repo = vcs.get_vcs_instance(
os.path.join(TESTS_TMP_PATH, repo_name), create=False)
return repo is not None
def commit_change(
repo, filename, content, message, vcs_type, parent=None, newfile=False):
from rhodecode.tests import TEST_USER_ADMIN_LOGIN
repo = Repository.get_by_repo_name(repo)
_commit = parent
if not parent:
_commit = EmptyCommit(alias=vcs_type)
if newfile:
nodes = {
filename: {
'content': content
}
}
commit = ScmModel().create_nodes(
user=TEST_USER_ADMIN_LOGIN, repo=repo,
message=message,
nodes=nodes,
parent_commit=_commit,
author=TEST_USER_ADMIN_LOGIN,
)
else:
commit = ScmModel().commit_change(
repo=repo.scm_instance(), repo_name=repo.repo_name,
commit=parent, user=TEST_USER_ADMIN_LOGIN,
author=TEST_USER_ADMIN_LOGIN,
message=message,
content=content,
f_path=filename
)
return commit

View file

@ -359,14 +359,15 @@ class TestSvnGetDiff:
], ids=['file', 'dir'])
def test_diff_to_tagged_version(self, vcsbackend_svn, path, path1):
repo = vcsbackend_svn['svn-simple-layout']
commit = repo[-1]
diff = repo.get_diff(commit, commit, path=path, path1=path1)
commit1 = repo[-2]
commit2 = repo[-1]
diff = repo.get_diff(commit1, commit2, path=path, path1=path1)
assert diff.raw == self.expected_diff_v_0_2
expected_diff_v_0_2 = '''Index: example.py
===================================================================
diff --git a/example.py b/example.py
--- a/example.py\t(revision 26)
--- a/example.py\t(revision 25)
+++ b/example.py\t(revision 26)
@@ -7,8 +7,12 @@