Changeset - ceaa65df5add
[Not reviewed]
default
0 6 0
domruf - 8 years ago 2017-10-25 22:57:31
dominikruf@gmail.com
diff: use fontello icon-diff-* names in generated html

This way we need less style code ... but generate slightly bigger html.

Also move the style code to kallithea-diff.less and remove the now obsolete
comment.
6 files changed with 96 insertions and 98 deletions:
0 comments (0 inline, 0 general)
kallithea/lib/diffs.py
Show inline comments
 
@@ -234,254 +234,254 @@ def get_gitdiff(filenode_old, filenode_n
 
    """
 
    # make sure we pass in default context
 
    context = context or 3
 
    submodules = filter(lambda o: isinstance(o, SubModuleNode),
 
                        [filenode_new, filenode_old])
 
    if submodules:
 
        return ''
 

	
 
    for filenode in (filenode_old, filenode_new):
 
        if not isinstance(filenode, FileNode):
 
            raise VCSError("Given object should be FileNode object, not %s"
 
                % filenode.__class__)
 

	
 
    repo = filenode_new.changeset.repository
 
    old_raw_id = getattr(filenode_old.changeset, 'raw_id', repo.EMPTY_CHANGESET)
 
    new_raw_id = getattr(filenode_new.changeset, 'raw_id', repo.EMPTY_CHANGESET)
 

	
 
    vcs_gitdiff = get_diff(repo, old_raw_id, new_raw_id, filenode_new.path,
 
                           ignore_whitespace, context)
 
    return vcs_gitdiff
 

	
 

	
 
def get_diff(scm_instance, rev1, rev2, path=None, ignore_whitespace=False, context=3):
 
    """
 
    A thin wrapper around vcs lib get_diff.
 
    """
 
    try:
 
        return scm_instance.get_diff(rev1, rev2, path=path,
 
                                     ignore_whitespace=ignore_whitespace, context=context)
 
    except MemoryError:
 
        h.flash('MemoryError: Diff is too big', category='error')
 
        return ''
 

	
 

	
 
NEW_FILENODE = 1
 
DEL_FILENODE = 2
 
MOD_FILENODE = 3
 
RENAMED_FILENODE = 4
 
COPIED_FILENODE = 5
 
CHMOD_FILENODE = 6
 
BIN_FILENODE = 7
 

	
 

	
 
class DiffProcessor(object):
 
    """
 
    Give it a unified or git diff and it returns a list of the files that were
 
    mentioned in the diff together with a dict of meta information that
 
    can be used to render it in a HTML template.
 
    """
 
    _diff_git_re = re.compile('^diff --git', re.MULTILINE)
 

	
 
    def __init__(self, diff, vcs='hg', diff_limit=None, inline_diff=True):
 
        """
 
        :param diff:   a text in diff format
 
        :param vcs: type of version control hg or git
 
        :param diff_limit: define the size of diff that is considered "big"
 
            based on that parameter cut off will be triggered, set to None
 
            to show full diff
 
        """
 
        if not isinstance(diff, basestring):
 
            raise Exception('Diff must be a basestring got %s instead' % type(diff))
 

	
 
        self._diff = diff
 
        self.adds = 0
 
        self.removes = 0
 
        self.diff_limit = diff_limit
 
        self.limited_diff = False
 
        self.vcs = vcs
 
        self.parsed = self._parse_gitdiff(inline_diff=inline_diff)
 

	
 
    def _parse_gitdiff(self, inline_diff):
 
        """Parse self._diff and return a list of dicts with meta info and chunks for each file.
 
        Might set limited_diff.
 
        Optionally, do an extra pass and to extra markup of one-liner changes.
 
        """
 
        _files = [] # list of dicts with meta info and chunks
 

	
 
        starts = [m.start() for m in self._diff_git_re.finditer(self._diff)]
 
        starts.append(len(self._diff))
 

	
 
        for start, end in zip(starts, starts[1:]):
 
            if self.diff_limit and end > self.diff_limit:
 
                self.limited_diff = True
 
                continue
 

	
 
            head, diff_lines = _get_header(self.vcs, buffer(self._diff, start, end - start))
 

	
 
            op = None
 
            stats = {
 
                'added': 0,
 
                'deleted': 0,
 
                'binary': False,
 
                'ops': {},
 
            }
 

	
 
            if head['deleted_file_mode']:
 
                op = 'D'
 
                op = 'removed'
 
                stats['binary'] = True
 
                stats['ops'][DEL_FILENODE] = 'deleted file'
 

	
 
            elif head['new_file_mode']:
 
                op = 'A'
 
                op = 'added'
 
                stats['binary'] = True
 
                stats['ops'][NEW_FILENODE] = 'new file %s' % head['new_file_mode']
 
            else:  # modify operation, can be cp, rename, chmod
 
                # CHMOD
 
                if head['new_mode'] and head['old_mode']:
 
                    op = 'M'
 
                    op = 'modified'
 
                    stats['binary'] = True
 
                    stats['ops'][CHMOD_FILENODE] = ('modified file chmod %s => %s'
 
                                        % (head['old_mode'], head['new_mode']))
 
                # RENAME
 
                if (head['rename_from'] and head['rename_to']
 
                      and head['rename_from'] != head['rename_to']):
 
                    op = 'R'
 
                    op = 'renamed'
 
                    stats['binary'] = True
 
                    stats['ops'][RENAMED_FILENODE] = ('file renamed from %s to %s'
 
                                    % (head['rename_from'], head['rename_to']))
 
                # COPY
 
                if head.get('copy_from') and head.get('copy_to'):
 
                    op = 'M'
 
                    op = 'modified'
 
                    stats['binary'] = True
 
                    stats['ops'][COPIED_FILENODE] = ('file copied from %s to %s'
 
                                        % (head['copy_from'], head['copy_to']))
 
                # FALL BACK: detect missed old style add or remove
 
                if op is None:
 
                    if not head['a_file'] and head['b_file']:
 
                        op = 'A'
 
                        op = 'added'
 
                        stats['binary'] = True
 
                        stats['ops'][NEW_FILENODE] = 'new file'
 

	
 
                    elif head['a_file'] and not head['b_file']:
 
                        op = 'D'
 
                        op = 'removed'
 
                        stats['binary'] = True
 
                        stats['ops'][DEL_FILENODE] = 'deleted file'
 

	
 
                # it's not ADD not DELETE
 
                if op is None:
 
                    op = 'M'
 
                    op = 'modified'
 
                    stats['binary'] = True
 
                    stats['ops'][MOD_FILENODE] = 'modified file'
 

	
 
            # a real non-binary diff
 
            if head['a_file'] or head['b_file']:
 
                chunks, added, deleted = _parse_lines(diff_lines)
 
                stats['binary'] = False
 
                stats['added'] = added
 
                stats['deleted'] = deleted
 
                # explicit mark that it's a modified file
 
                if op == 'M':
 
                if op == 'modified':
 
                    stats['ops'][MOD_FILENODE] = 'modified file'
 
            else:  # Git binary patch (or empty diff)
 
                # Git binary patch
 
                if head['bin_patch']:
 
                    stats['ops'][BIN_FILENODE] = 'binary diff not shown'
 
                chunks = []
 

	
 
            if op == 'D' and chunks:
 
            if op == 'removed' and chunks:
 
                # a way of seeing deleted content could perhaps be nice - but
 
                # not with the current UI
 
                chunks = []
 

	
 
            chunks.insert(0, [{
 
                'old_lineno': '',
 
                'new_lineno': '',
 
                'action':     'context',
 
                'line':       msg,
 
                } for _op, msg in stats['ops'].iteritems()
 
                  if _op not in [MOD_FILENODE]])
 

	
 
            _files.append({
 
                'old_filename':     head['a_path'],
 
                'filename':         head['b_path'],
 
                'old_revision':     head['a_blob_id'],
 
                'new_revision':     head['b_blob_id'],
 
                'chunks':           chunks,
 
                'operation':        op,
 
                'stats':            stats,
 
            })
 

	
 
        if not inline_diff:
 
            return _files
 

	
 
        # highlight inline changes when one del is followed by one add
 
        for diff_data in _files:
 
            for chunk in diff_data['chunks']:
 
                lineiter = iter(chunk)
 
                try:
 
                    peekline = lineiter.next()
 
                    while True:
 
                        # find a first del line
 
                        while peekline['action'] != 'del':
 
                            peekline = lineiter.next()
 
                        delline = peekline
 
                        peekline = lineiter.next()
 
                        # if not followed by add, eat all following del lines
 
                        if peekline['action'] != 'add':
 
                            while peekline['action'] == 'del':
 
                                peekline = lineiter.next()
 
                            continue
 
                        # found an add - make sure it is the only one
 
                        addline = peekline
 
                        try:
 
                            peekline = lineiter.next()
 
                        except StopIteration:
 
                            # add was last line - ok
 
                            _highlight_inline_diff(delline, addline)
 
                            raise
 
                        if peekline['action'] != 'add':
 
                            # there was only one add line - ok
 
                            _highlight_inline_diff(delline, addline)
 
                except StopIteration:
 
                    pass
 

	
 
        return _files
 

	
 
    def stat(self):
 
        """
 
        Returns tuple of added, and removed lines for this instance
 
        """
 
        return self.adds, self.removes
 

	
 

	
 
_escape_re = re.compile(r'(&)|(<)|(>)|(\t)|(\r)|(?<=.)( \n| $)')
 

	
 

	
 
def _escaper(string):
 
    """
 
    Do HTML escaping/markup
 
    """
 

	
 
    def substitute(m):
 
        groups = m.groups()
 
        if groups[0]:
 
            return '&amp;'
 
        if groups[1]:
 
            return '&lt;'
 
        if groups[2]:
 
            return '&gt;'
 
        if groups[3]:
 
            return '<u>\t</u>'
 
        if groups[4]:
 
            return '<u class="cr"></u>'
 
        if groups[5]:
 
            return ' <i></i>'
 
        assert False
 

	
 
    return _escape_re.sub(substitute, safe_unicode(string))
 

	
 

	
 
_git_header_re = re.compile(r"""
 
    ^diff[ ]--git[ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
 
    (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
 
       ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
kallithea/public/css/contextbar.css
Show inline comments
 
/**
 
 * Stylesheets for the context bar
 
 */
 
i[class^='icon-'] {
 
  background-repeat: no-repeat;
 
  background-position: center;
 
  display: inline-block;
 
  min-width: 16px;
 
  min-height: 16px;
 
  margin: -2px 0 -4px 0;
 
  /* background-color: red; /* for debugging */
 
}
 
/* Note: class 'icon-empty' or 'icon-gravatar' can be used to get icon styling without an actual glyph */
 
/* css classes for diff file status ... it'd be nice if css had a way to
 
   inherit from another class but alas, we must make sure this content is the
 
   same from the icon font file */
 
.icon-diff-M:before {
 
  font-family: 'kallithea';
 
  content: '\22a1';
 
  color: #d0b44c;
 
}
 
.icon-diff-D:before {
 
  font-family: 'kallithea';
 
  content: '\229f';
 
  color: #bd2c00;
 
}
 
.icon-diff-A:before {
 
  font-family: 'kallithea';
 
  content: '\229e';
 
  color: #6cc644;
 
}
 
.icon-diff-R:before {
 
  font-family: 'kallithea';
 
  content: '\e81f';
 
  color: #677a85;
 
}
 
nav.navbar #quick a,
 
#content #context-bar,
 
#content #context-bar a {
 
  color: #FFFFFF;
 
}
 
nav.navbar #quick a:hover,
 
#content #context-bar a:hover {
 
  text-decoration: none;
 
  background: inherit;
 
}
 
.navbar-text {
 
  margin-top: 0;
 
  margin-bottom: 0;
 
}
 
ul#quick ul.dropdown-menu > li {
 
  border-bottom: 1px solid rgba(0,0,0,0.1);
 
  border-top: 1px solid rgba(255,255,255,0.1);
 
}
 
ul#quick ul.dropdown-menu > li:first-child {
 
  border-top: none;
 
}
 
ul#quick ul.dropdown-menu > li:last-child {
 
  border-bottom: none;
 
}
 
nav.navbar #quick ul,
 
#context-pages ul {
 
  background: #577632;
 
}
 
.repo-switcher-dropdown.select2-drop.select2-drop-active .select2-results .select2-highlighted,
 
ul.dropdown-menu li a:focus,
 
nav.navbar #quick li:hover,
 
#quick_login > .pull-right .list-group-item:hover,
 
#context-pages li:hover,
 
nav.navbar #quick li.active,
 
nav.navbar #quick li a.menu_link:focus,
 
#context-pages li.active {
 
  background: linear-gradient(to bottom, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.1) 100%); /* W3C */
 
}
 
#quick_login > .pull-right .list-group-item {
 
  background-color: #577632;
 
  border: 0;
 
}
 
#content #context-pages .follow .show-following,
 
#content #context-pages .following .show-follow {
 
  display: none;
 
}
 
nav.navbar #quick li,
 
nav.navbar #quick_login .pull-right,
 
#content #context-pages li {
 
  border-right: 1px solid rgba(0,0,0,0.1);
 
  border-left: 1px solid rgba(255,255,255,0.1);
 
  padding: 0;
 
}
 
.nav .open > a {
 
  background: inherit !important;
 
}
 
nav.navbar #quick li:last-child,
 
#content #context-pages li:last-child {
 
  border-right: none;
 
}
 
nav.navbar #quick > li:first-child {
 
  border-left: none;
 
}
 
nav.navbar #quick > li:first-child > a {
 
  border-radius: 4px 0 0 4px;
 
}
 
nav.navbar #quick > li > a,
 
nav.navbar #quick .select2-container .select2-choice .select2-chosen,
 
#context-pages > ul > li > a,
 
#context-pages .select2-container .select2-choice .select2-chosen {
 
  padding: 0px 15px 1px 15px;
 
  line-height: 43px;
 
}
 
nav.navbar #quick a#quick_login_link {
 
  padding-left: 0px;
 
}
 
nav.navbar #quick a {
 
  overflow: hidden;
 
}
 
#context-pages a.dropdown-toggle:after {
 
  position: absolute;
 
  float: right;
 
  padding-left: 5px;
 
  padding-right: 5px;
 
}
kallithea/public/css/style.css
Show inline comments
 
@@ -1896,377 +1896,387 @@ span.pr-closed-tag {
 
#s2id_other_repo {
 
  min-width: 150px;
 
  margin: 5px;
 
}
 
#pr-summary .msg-div {
 
  margin: 5px 0;
 
}
 
#pr-summary > .pr-not-edit {
 
  min-height: 50px !important;
 
}
 
#pr-edit-btn {
 
  margin: 20px 0 0 !important;
 
  position: absolute;
 
}
 
/****
 
  PERMS
 
*****/
 
#perms .perms_section_head {
 
  padding: 10px 10px 10px 0px;
 
  font-size: 16px;
 
  font-weight: bold;
 
  text-transform: capitalize;
 
}
 
#perms .perms_section_head label {
 
  margin-left: 10px;
 
}
 
input.perm_filter {
 
  position: relative;
 
  top: 2px;
 
}
 
.perm-gravatar {
 
  vertical-align: middle;
 
  padding: 2px;
 
}
 
.perm-gravatar-ac {
 
  vertical-align: middle;
 
  padding: 2px;
 
  width: 14px;
 
  height: 14px;
 
}
 
.cs_files .progress {
 
  margin-bottom: 0;
 
}
 
.cs_files .changes {
 
  float: right;
 
  color: #577632;
 
}
 
.cs_files .changes .added {
 
  color: inherit;
 
  background-color: #BBFFBB;
 
  float: left;
 
  text-align: center;
 
  font-size: 9px;
 
  padding: 2px 0px 2px 0px;
 
}
 
.cs_files .changes .deleted {
 
  background-color: #FF8888;
 
  float: left;
 
  text-align: center;
 
  font-size: 9px;
 
  padding: 2px 0px 2px 0px;
 
}
 
/*new binary
 
NEW_FILENODE = 1
 
DEL_FILENODE = 2
 
MOD_FILENODE = 3
 
RENAMED_FILENODE = 4
 
CHMOD_FILENODE = 5
 
BIN_FILENODE = 6
 
*/
 
.cs_files .changes .bin {
 
  background-color: #BBFFBB;
 
  float: left;
 
  text-align: center;
 
  font-size: 9px;
 
  padding: 2px 0px 2px 0px;
 
}
 
.cs_files .changes .bin.bin1 {
 
  background-color: #BBFFBB;
 
}
 
/*deleted binary*/
 
.cs_files .changes .bin.bin2 {
 
  background-color: #FF8888;
 
}
 
/*mod binary*/
 
.cs_files .changes .bin.bin3 {
 
  background-color: #DDDDDD;
 
}
 
/*rename file*/
 
.cs_files .changes .bin.bin4 {
 
  background-color: #6D99FF;
 
}
 
/*chmod file*/
 
.cs_files .changes .bin.bin5 {
 
  background-color: #6D99FF;
 
}
 
.cs_files .cs_added,
 
.cs_files .cs_A {
 
.cs_files .cs_added {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.cs_files .cs_changed,
 
.cs_files .cs_M {
 
.cs_files .cs_modified {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.cs_files .cs_removed,
 
.cs_files .cs_D {
 
.cs_files .cs_removed {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.cs_files .cs_renamed,
 
.cs_files .cs_R {
 
.cs_files .cs_renamed {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.diff-collapse {
 
  text-align: center;
 
  margin-bottom: 15px;
 
}
 
table.code-difftable {
 
  border-collapse: collapse;
 
  border-radius: 0px !important;
 
  width: 100%;
 
}
 
table.code-difftable td {
 
  padding: 0 !important;
 
  background: none !important;
 
  border: 0 !important;
 
  vertical-align: baseline !important;
 
}
 
table.code-difftable .context {
 
  background: none repeat scroll 0 0 #DDE7EF;
 
  color: #999;
 
}
 
table.code-difftable .add {
 
  background: none repeat scroll 0 0 #DDFFDD;
 
}
 
table.code-difftable .add ins {
 
  background: none repeat scroll 0 0 #AAFFAA;
 
  text-decoration: none;
 
}
 
table.code-difftable .del {
 
  background: none repeat scroll 0 0 #FFDDDD;
 
}
 
table.code-difftable .del del {
 
  background: none repeat scroll 0 0 #FFAAAA;
 
  text-decoration: none;
 
}
 
table.code-difftable td.code pre u:before {
 
  content: "\21a6";
 
  display: inline-block;
 
  width: 0;
 
}
 
table.code-difftable td.code pre u.cr:before {
 
  content: "\21a4";
 
  display: inline-block;
 
  color: rgba(0, 0, 0, 0.5);
 
}
 
table.code-difftable td.code pre u {
 
  color: rgba(0, 0, 0, 0.15);
 
}
 
table.code-difftable td.code pre i {
 
  border-style: solid;
 
  border-width: 0 0 0 1px;
 
  color: rgba(0, 0, 0, 0.5);
 
}
 
/** LINE NUMBERS **/
 
table.code-difftable .lineno {
 
  padding-left: 2px;
 
  padding-right: 2px !important;
 
  width: 30px;
 
  -moz-user-select: none;
 
  -webkit-user-select: none;
 
  border-right: 1px solid #CCC !important;
 
  border-left: 0px solid #CCC !important;
 
  border-top: 0px solid #CCC !important;
 
  border-bottom: none !important;
 
  vertical-align: middle !important;
 
  text-align: center;
 
}
 
table.code-difftable .lineno.new {
 
  text-align: right;
 
}
 
table.code-difftable .lineno.old {
 
  text-align: right;
 
}
 
table.code-difftable .lineno a {
 
  color: #aaa !important;
 
  font: 11px Lucida Console, Consolas, Monaco, Inconsolata, Liberation Mono, monospace !important;
 
  padding-left: 6px;
 
  padding-right: 6px;
 
  display: block;
 
}
 
table.code-difftable .line:hover .lineno a {
 
  color: #333 !important;
 
}
 
table.code-difftable .lineno-inline {
 
  background: none repeat scroll 0 0 #FFF !important;
 
  padding-left: 2px;
 
  padding-right: 2px;
 
  text-align: right;
 
  width: 30px;
 
  -moz-user-select: none;
 
  -webkit-user-select: none;
 
}
 
/** CODE **/
 
table.code-difftable .code {
 
  display: block;
 
  width: 100%;
 
}
 
table.code-difftable .code pre {
 
  margin: 0 0 0 12px !important;
 
  padding: 0;
 
  min-height: 17px;
 
  line-height: 17px;
 
  white-space: pre-wrap;
 
  word-break: break-all;
 
}
 
table.code-difftable .del .code pre:before {
 
  content: "-";
 
  color: #800;
 
  float: left;
 
  left: -1em;
 
  position: relative;
 
  width: 0;
 
}
 
table.code-difftable .add .code pre:before {
 
  content: "+";
 
  color: #080;
 
  float: left;
 
  left: -1em;
 
  position: relative;
 
  width: 0;
 
}
 
table.code-difftable .unmod .code pre:before {
 
  content: " ";
 
  float: left;
 
  left: -1em;
 
  position: relative;
 
  width: 0;
 
}
 
.add-bubble {
 
  position: relative;
 
  display: none;
 
  float: left;
 
  width: 0px;
 
  height: 0px;
 
  left: -8px;
 
  box-sizing: border-box;
 
}
 
/* comment bubble, only visible when in a commentable diff */
 
.commentable-diff tr.line.add:hover td .add-bubble,
 
.commentable-diff tr.line.del:hover td .add-bubble,
 
.commentable-diff tr.line.unmod:hover td .add-bubble {
 
  display: block;
 
  z-index: 1;
 
}
 
.add-bubble div {
 
  background: #577632;
 
  width: 16px;
 
  height: 16px;
 
  cursor: pointer;
 
  padding: 0 2px 2px 0.5px;
 
  border: 1px solid #577632;
 
  border-radius: 3px;
 
  box-sizing: border-box;
 
}
 
.add-bubble div:before {
 
  font-size: 14px;
 
  color: #ffffff;
 
  font-family: "kallithea";
 
  content: '\1f5ea';
 
}
 
.add-bubble div:hover {
 
  transform: scale(1.2, 1.2);
 
}
 
/* file diff icons */
 
.icon-diff-modified:before {
 
  color: #d0b44c;
 
}
 
.icon-diff-removed:before {
 
  color: #bd2c00;
 
}
 
.icon-diff-added:before {
 
  color: #6cc644;
 
}
 
.icon-diff-renamed:before {
 
  color: #677a85;
 
}
 
/* show some context of link targets - but only works when the link target
 
   can be extended with any visual difference */
 
div.comment:target:before {
 
  display: block;
 
  height: 100px;
 
  margin: -100px 0 0;
 
  content: "";
 
}
 
div.comment:target > .panel {
 
  border: solid 2px #ee0 !important;
 
}
 
.lineno:target a {
 
  border: solid 2px #ee0 !important;
 
  margin: -2px;
 
}
 
.btn-image-diff-show,
 
.btn-image-diff-swap {
 
  margin: 5px;
 
}
 
.img-diff {
 
  max-width: 45%;
 
  height: auto;
 
  margin: 5px;
 
  /* http://lea.verou.me/demos/css3-patterns.html */
 
  background-image: linear-gradient(45deg, #888 25%, transparent 25%, transparent), linear-gradient(-45deg, #888 25%, transparent 25%, transparent), linear-gradient(45deg, transparent 75%, #888 75%), linear-gradient(-45deg, transparent 75%, #888 75%);
 
  background-size: 10px 10px;
 
  background-color: #999;
 
}
 
.img-preview {
 
  max-width: 100%;
 
  height: auto;
 
  margin: 5px;
 
}
 
div.comment-prev-next-links div.prev-comment,
 
div.comment-prev-next-links div.next-comment {
 
  display: inline-block;
 
  min-width: 150px;
 
  margin: 3px 6px;
 
}
 
#comments-general-comments div.comment-prev-next-links div.prev-comment,
 
#comments-general-comments div.comment-prev-next-links div.next-comment {
 
  margin-left: 0;
 
}
 
body table.dataTable thead .sorting {
 
  background-image: none;
 
}
 
body table.dataTable thead .sorting_asc {
 
  background-image: none;
 
}
 
body table.dataTable thead .sorting_desc {
 
  background-image: none;
 
}
 
body table.dataTable thead .sorting_asc_disabled {
 
  background-image: none;
 
}
 
body table.dataTable thead .sorting_desc_disabled {
 
  background-image: none;
 
}
 
body table.dataTable thead .sorting_asc::after {
 
  font-family: "kallithea";
 
  content: "\23f6";
 
}
 
body table.dataTable thead .sorting_desc::after {
 
  font-family: "kallithea";
 
  content: "\23f7";
 
}
 
.dataTables_wrapper .dataTables_left {
 
  float: left !important;
 
}
 
.dataTables_wrapper .dataTables_right {
 
  float: right;
 
}
 
.dataTables_wrapper .dataTables_right > div {
 
  padding-left: 30px;
 
}
 
.dataTables_wrapper .dataTables_info {
 
  clear: none;
 
  padding-top: 3px;
 
}
 
.dataTables_wrapper .dataTables_paginate {
 
  padding-top: 0;
 
}
 
.dataTables_wrapper .dataTables_paginate .paginate_button {
 
  padding: 3px 10px;
 
}
 
.dataTables_wrapper .dataTables_paginate > a.paginate_button {
 
  padding-top: 1px;
 
  border: 0 !important;
 
}
 
.dataTables_wrapper label {
 
  margin-bottom: 0;
 
  font-weight: inherit;
 
}
 
#content div.panel .changelog-panel > .changelog-heading,
 
#content div.panel .changelog-panel > ul.pagination {
 
  margin-left: 100px;
kallithea/public/less/kallithea-diff.less
Show inline comments
 
.cs_files .progress {
 
  margin-bottom: 0;
 
}
 
.cs_files .changes {
 
  float: right;
 
  color: #577632;
 
}
 
.cs_files .changes .added {
 
  color: inherit;
 
  background-color: #BBFFBB;
 
  float: left;
 
  text-align: center;
 
  font-size: 9px;
 
  padding: 2px 0px 2px 0px;
 
}
 
.cs_files .changes .deleted {
 
  background-color: #FF8888;
 
  float: left;
 
  text-align: center;
 
  font-size: 9px;
 
  padding: 2px 0px 2px 0px;
 
}
 
/*new binary
 
NEW_FILENODE = 1
 
DEL_FILENODE = 2
 
MOD_FILENODE = 3
 
RENAMED_FILENODE = 4
 
CHMOD_FILENODE = 5
 
BIN_FILENODE = 6
 
*/
 
.cs_files .changes .bin {
 
  background-color: #BBFFBB;
 
  float: left;
 
  text-align: center;
 
  font-size: 9px;
 
  padding: 2px 0px 2px 0px;
 
}
 
.cs_files .changes .bin.bin1 {
 
  background-color: #BBFFBB;
 
}
 
/*deleted binary*/
 
.cs_files .changes .bin.bin2 {
 
  background-color: #FF8888;
 
}
 
/*mod binary*/
 
.cs_files .changes .bin.bin3 {
 
  background-color: #DDDDDD;
 
}
 
/*rename file*/
 
.cs_files .changes .bin.bin4 {
 
  background-color: #6D99FF;
 
}
 
/*chmod file*/
 
.cs_files .changes .bin.bin5 {
 
  background-color: #6D99FF;
 
}
 
.cs_files .cs_added,
 
.cs_files .cs_A {
 
.cs_files .cs_added {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.cs_files .cs_changed,
 
.cs_files .cs_M {
 
.cs_files .cs_modified {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.cs_files .cs_removed,
 
.cs_files .cs_D {
 
.cs_files .cs_removed {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.cs_files .cs_renamed,
 
.cs_files .cs_R {
 
.cs_files .cs_renamed {
 
  height: 16px;
 
  margin-top: 7px;
 
  text-align: left;
 
}
 
.diff-collapse {
 
  text-align: center;
 
  margin-bottom: 15px;
 
}
 
table.code-difftable {
 
  border-collapse: collapse;
 
  border-radius: 0px !important;
 
  width: 100%;
 
}
 
table.code-difftable td {
 
  padding: 0 !important;
 
  background: none !important;
 
  border: 0 !important;
 
  vertical-align: baseline !important;
 
}
 
table.code-difftable .context {
 
  background: none repeat scroll 0 0 #DDE7EF;
 
  color: #999;
 
}
 
table.code-difftable .add {
 
  background: none repeat scroll 0 0 #DDFFDD;
 
}
 
table.code-difftable .add ins {
 
  background: none repeat scroll 0 0 #AAFFAA;
 
  text-decoration: none;
 
}
 
table.code-difftable .del {
 
  background: none repeat scroll 0 0 #FFDDDD;
 
}
 
table.code-difftable .del del {
 
  background: none repeat scroll 0 0 #FFAAAA;
 
  text-decoration: none;
 
}
 
table.code-difftable td.code pre u:before {
 
  content: "\21a6";
 
  display: inline-block;
 
  width: 0;
 
}
 
table.code-difftable td.code pre u.cr:before {
 
  content: "\21a4";
 
  display: inline-block;
 
  color: rgba(0, 0, 0, 0.5);
 
}
 
table.code-difftable td.code pre u {
 
  color: rgba(0, 0, 0, 0.15);
 
}
 
table.code-difftable td.code pre i {
 
  border-style: solid;
 
  border-width: 0 0 0 1px;
 
  color: rgba(0, 0, 0, 0.5);
 
}
 
/** LINE NUMBERS **/
 
table.code-difftable .lineno {
 
  padding-left: 2px;
 
  padding-right: 2px !important;
 
  width: 30px;
 
  -moz-user-select: none;
 
  -webkit-user-select: none;
 
  border-right: 1px solid #CCC !important;
 
  border-left: 0px solid #CCC !important;
 
  border-top: 0px solid #CCC !important;
 
  border-bottom: none !important;
 
  vertical-align: middle !important;
 
  text-align: center;
 
}
 
table.code-difftable .lineno.new {
 
  text-align: right;
 
}
 
table.code-difftable .lineno.old {
 
  text-align: right;
 
}
 
table.code-difftable .lineno a {
 
  color: #aaa !important;
 
  font: 11px Lucida Console, Consolas, Monaco, Inconsolata, Liberation Mono, monospace !important;
 
  padding-left: 6px;
 
  padding-right: 6px;
 
  display: block;
 
}
 
table.code-difftable .line:hover .lineno a {
 
  color: #333 !important;
 
}
 
table.code-difftable .lineno-inline {
 
  background: none repeat scroll 0 0 #FFF !important;
 
  padding-left: 2px;
 
  padding-right: 2px;
 
  text-align: right;
 
  width: 30px;
 
  -moz-user-select: none;
 
  -webkit-user-select: none;
 
}
 
/** CODE **/
 
table.code-difftable .code {
 
  display: block;
 
  width: 100%;
 
}
 
table.code-difftable .code pre {
 
  margin: 0 0 0 12px !important;
 
  padding: 0;
 
  min-height: 17px;
 
  line-height: 17px;
 
  white-space: pre-wrap;
 
  word-break: break-all;
 
}
 
table.code-difftable .del .code pre:before {
 
  content: "-";
 
  color: #800;
 
  float: left;
 
  left: -1em;
 
  position: relative;
 
  width: 0;
 
}
 
table.code-difftable .add .code pre:before {
 
  content: "+";
 
  color: #080;
 
  float: left;
 
  left: -1em;
 
  position: relative;
 
  width: 0;
 
}
 
table.code-difftable .unmod .code pre:before {
 
  content: " ";
 
  float: left;
 
  left: -1em;
 
  position: relative;
 
  width: 0;
 
}
 
.add-bubble {
 
  position: relative;
 
  display: none;
 
  float: left;
 
  width: 0px;
 
  height: 0px;
 
  left: -8px;
 
  box-sizing: border-box;
 
}
 
/* comment bubble, only visible when in a commentable diff */
 
.commentable-diff tr.line.add:hover td .add-bubble,
 
.commentable-diff tr.line.del:hover td .add-bubble,
 
.commentable-diff tr.line.unmod:hover td .add-bubble {
 
  display: block;
 
  z-index: 1;
 
}
 
.add-bubble div {
 
  background: #577632;
 
  width: 16px;
 
  height: 16px;
 
  cursor: pointer;
 
  padding: 0 2px 2px 0.5px;
 
  border: 1px solid #577632;
 
  border-radius: 3px;
 
  box-sizing: border-box;
 
}
 
.add-bubble div:before {
 
  font-size: 14px;
 
  color: #ffffff;
 
  font-family: "kallithea";
 
  content: '\1f5ea';
 
}
 
.add-bubble div:hover {
 
  transform: scale(1.2, 1.2);
 
}
 

	
 
/* file diff icons */
 
.icon-diff-modified:before {
 
  color: #d0b44c;
 
}
 
.icon-diff-removed:before {
 
  color: #bd2c00;
 
}
 
.icon-diff-added:before {
 
  color: #6cc644;
 
}
 
.icon-diff-renamed:before {
 
  color: #677a85;
 
}
kallithea/tests/functional/test_compare_local.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
from kallithea.tests.base import *
 

	
 
class TestCompareController(TestController):
 

	
 
    def test_compare_tag_hg(self):
 
        self.log_user()
 
        tag1 = 'v0.1.2'
 
        tag2 = 'v0.1.3'
 
        response = self.app.get(url('compare_url',
 
                                    repo_name=HG_REPO,
 
                                    org_ref_type="tag",
 
                                    org_ref_name=tag1,
 
                                    other_ref_type="tag",
 
                                    other_ref_name=tag2,
 
                                    ), status=200)
 
        response.mustcontain('%s@%s' % (HG_REPO, tag1))
 
        response.mustcontain('%s@%s' % (HG_REPO, tag2))
 

	
 
        ## outgoing changesets between tags
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/70d4cef8a37657ee4cf5aabb3bd9f68879769816">r115:70d4cef8a376</a>''' % HG_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/9749bfbfc0d2eba208d7947de266303b67c87cda">r116:9749bfbfc0d2</a>''' % HG_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/41fda979f02fda216374bf8edac4e83f69e7581c">r117:41fda979f02f</a>''' % HG_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/bb1a3ab98cc45cb934a77dcabf87a5a598b59e97">r118:bb1a3ab98cc4</a>''' % HG_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/36e0fc9d2808c5022a24f49d6658330383ed8666">r119:36e0fc9d2808</a>''' % HG_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">r120:17544fbfcd33</a>''' % HG_REPO)
 

	
 
        response.mustcontain('11 files changed with 94 insertions and 64 deletions')
 

	
 
        ## files diff
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-A"></i>
 
                          <i class="icon-diff-added"></i>
 
                          <a href="#C--1c5cf9e91c12">docs/api/utils/index.rst</a>
 
                      </span>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-A"></i>
 
                          <i class="icon-diff-added"></i>
 
                          <a href="#C--e3305437df55">test_and_report.sh</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--c8e92ef85cd1">.hgignore</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--6e08b694d687">.hgtags</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--2c14b00f3393">docs/api/index.rst</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--430ccbc82bdf">vcs/__init__.py</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--9c390eb52cd6">vcs/backends/hg.py</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--ebb592c595c0">vcs/utils/__init__.py</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--7abc741b5052">vcs/utils/annotate.py</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--2ef0ef106c56">vcs/utils/diffs.py</a>''')
 
        response.mustcontain(
 
                   '''<span class="node">
 
                          <i class="icon-diff-M"></i>
 
                          <i class="icon-diff-modified"></i>
 
                          <a href="#C--3150cb87d4b7">vcs/utils/lazy.py</a>''')
 

	
 
    def test_compare_tag_git(self):
 
        self.log_user()
 
        tag1 = 'v0.1.2'
 
        tag2 = 'v0.1.3'
 
        response = self.app.get(url('compare_url',
 
                                    repo_name=GIT_REPO,
 
                                    org_ref_type="tag",
 
                                    org_ref_name=tag1,
 
                                    other_ref_type="tag",
 
                                    other_ref_name=tag2,
 
                                    ), status=200)
 
        response.mustcontain('%s@%s' % (GIT_REPO, tag1))
 
        response.mustcontain('%s@%s' % (GIT_REPO, tag2))
 

	
 
        ## outgoing changesets between tags
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/794bbdd31545c199f74912709ea350dedcd189a2">r113:794bbdd31545</a>''' % GIT_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/e36d8c5025329bdd4212bd53d4ed8a70ff44985f">r115:e36d8c502532</a>''' % GIT_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/5c9ff4f6d7508db0e72b1d2991c357d0d8e07af2">r116:5c9ff4f6d750</a>''' % GIT_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/b7187fa2b8c1d773ec35e9dee12f01f74808c879">r117:b7187fa2b8c1</a>''' % GIT_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/5f3b74262014a8de2dc7dade1152de9fd0c8efef">r118:5f3b74262014</a>''' % GIT_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/17438a11f72b93f56d0e08e7d1fa79a378578a82">r119:17438a11f72b</a>''' % GIT_REPO)
 
        response.mustcontain('''<a class="changeset_hash" href="/%s/changeset/5a3a8fb005554692b16e21dee62bf02667d8dc3e">r120:5a3a8fb00555</a>''' % GIT_REPO)
 

	
 
        response.mustcontain('11 files changed with 94 insertions and 64 deletions')
 

	
 
        # files
 
        response.mustcontain('''<a href="#C--1c5cf9e91c12">docs/api/utils/index.rst</a>''')
 
        response.mustcontain('''<a href="#C--e3305437df55">test_and_report.sh</a>''')
 
        response.mustcontain('''<a href="#C--c8e92ef85cd1">.hgignore</a>''')
 
        response.mustcontain('''<a href="#C--6e08b694d687">.hgtags</a>''')
 
        response.mustcontain('''<a href="#C--2c14b00f3393">docs/api/index.rst</a>''')
 
        response.mustcontain('''<a href="#C--430ccbc82bdf">vcs/__init__.py</a>''')
 
        response.mustcontain('''<a href="#C--9c390eb52cd6">vcs/backends/hg.py</a>''')
 
        response.mustcontain('''<a href="#C--ebb592c595c0">vcs/utils/__init__.py</a>''')
 
        response.mustcontain('''<a href="#C--7abc741b5052">vcs/utils/annotate.py</a>''')
 
        response.mustcontain('''<a href="#C--2ef0ef106c56">vcs/utils/diffs.py</a>''')
 
        response.mustcontain('''<a href="#C--3150cb87d4b7">vcs/utils/lazy.py</a>''')
 

	
 
    def test_index_branch_hg(self):
 
        self.log_user()
 
        response = self.app.get(url('compare_url',
 
                                    repo_name=HG_REPO,
 
                                    org_ref_type="branch",
 
                                    org_ref_name='default',
 
                                    other_ref_type="branch",
 
                                    other_ref_name='default',
 
                                    ))
 

	
 
        response.mustcontain('%s@default' % (HG_REPO))
 
        response.mustcontain('%s@default' % (HG_REPO))
 
        # branch are equal
 
        response.mustcontain('<span class="empty_data">No files</span>')
 
        response.mustcontain('<span class="empty_data">No changesets</span>')
 

	
 
    def test_index_branch_git(self):
 
        self.log_user()
 
        response = self.app.get(url('compare_url',
 
                                    repo_name=GIT_REPO,
 
                                    org_ref_type="branch",
 
                                    org_ref_name='master',
 
                                    other_ref_type="branch",
 
                                    other_ref_name='master',
 
                                    ))
 

	
 
        response.mustcontain('%s@master' % (GIT_REPO))
 
        response.mustcontain('%s@master' % (GIT_REPO))
 
        # branch are equal
 
        response.mustcontain('<span class="empty_data">No files</span>')
 
        response.mustcontain('<span class="empty_data">No changesets</span>')
 

	
 
    def test_compare_revisions_hg(self):
 
        self.log_user()
 
        rev1 = 'b986218ba1c9'
 
        rev2 = '3d8f361e72ab'
 

	
 
        response = self.app.get(url('compare_url',
 
                                    repo_name=HG_REPO,
 
                                    org_ref_type="rev",
 
                                    org_ref_name=rev1,
 
                                    other_ref_type="rev",
 
                                    other_ref_name=rev2,
 
                                    ))
 
        response.mustcontain('%s@%s' % (HG_REPO, rev1))
 
        response.mustcontain('%s@%s' % (HG_REPO, rev2))
 

	
 
        ## outgoing changesets between those revisions
 
        response.mustcontain("""<a class="changeset_hash" href="/%s/changeset/3d8f361e72ab303da48d799ff1ac40d5ac37c67e">r1:%s</a>""" % (HG_REPO, rev2))
 

	
 
        response.mustcontain('1 file changed with 7 insertions and 0 deletions')
 
        ## files
 
        response.mustcontain("""<a href="#C--c8e92ef85cd1">.hgignore</a>""")
 

	
 
    def test_compare_revisions_git(self):
 
        self.log_user()
kallithea/tests/models/test_diff_parsers.py
Show inline comments
 
from kallithea.tests.base import *
 
from kallithea.lib.diffs import DiffProcessor, NEW_FILENODE, DEL_FILENODE, \
 
    MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
 
from kallithea.tests.fixture import Fixture
 

	
 
fixture = Fixture()
 

	
 

	
 
DIFF_FIXTURES = {
 
    'hg_diff_add_single_binary_file.diff': [
 
        ('US Warszawa.jpg', 'A',
 
        ('US Warszawa.jpg', 'added',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {NEW_FILENODE: 'new file 100755',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
    ],
 
    'hg_diff_mod_single_binary_file.diff': [
 
        ('US Warszawa.jpg', 'M',
 
        ('US Warszawa.jpg', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {MOD_FILENODE: 'modified file',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
    ],
 

	
 
    'hg_diff_mod_single_file_and_rename_and_chmod.diff': [
 
        ('README', 'R',
 
        ('README', 'renamed',
 
         {'added': 3,
 
          'deleted': 0,
 
          'binary': False,
 
          'ops': {RENAMED_FILENODE: 'file renamed from README.rst to README',
 
                  CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
 
    ],
 
    'hg_diff_mod_file_and_rename.diff': [
 
        ('README.rst', 'R',
 
        ('README.rst', 'renamed',
 
         {'added': 3,
 
          'deleted': 0,
 
          'binary': False,
 
          'ops': {RENAMED_FILENODE: 'file renamed from README to README.rst'}}),
 
    ],
 
    'hg_diff_del_single_binary_file.diff': [
 
        ('US Warszawa.jpg', 'D',
 
        ('US Warszawa.jpg', 'removed',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {DEL_FILENODE: 'deleted file',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
    ],
 
    'hg_diff_chmod_and_mod_single_binary_file.diff': [
 
        ('gravatar.png', 'M',
 
        ('gravatar.png', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
    ],
 
    'hg_diff_chmod.diff': [
 
        ('file', 'M',
 
        ('file', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
 
    ],
 
    'hg_diff_rename_file.diff': [
 
        ('file_renamed', 'R',
 
        ('file_renamed', 'renamed',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {RENAMED_FILENODE: 'file renamed from file to file_renamed'}}),
 
    ],
 
    'hg_diff_rename_and_chmod_file.diff': [
 
        ('README', 'R',
 
        ('README', 'renamed',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
 
                  RENAMED_FILENODE: 'file renamed from README.rst to README'}}),
 
    ],
 
    'hg_diff_binary_and_normal.diff': [
 
        ('img/baseline-10px.png', 'A',
 
        ('img/baseline-10px.png', 'added',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {NEW_FILENODE: 'new file 100644',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
        ('img/baseline-20px.png', 'D',
 
        ('img/baseline-20px.png', 'removed',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {DEL_FILENODE: 'deleted file',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
        ('index.html', 'M',
 
        ('index.html', 'modified',
 
         {'added': 3,
 
          'deleted': 2,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('js/global.js', 'D',
 
        ('js/global.js', 'removed',
 
         {'added': 0,
 
          'deleted': 75,
 
          'binary': False,
 
          'ops': {DEL_FILENODE: 'deleted file'}}),
 
        ('js/jquery/hashgrid.js', 'A',
 
        ('js/jquery/hashgrid.js', 'added',
 
         {'added': 340,
 
          'deleted': 0,
 
          'binary': False,
 
          'ops': {NEW_FILENODE: 'new file 100755'}}),
 
        ('less/docs.less', 'M',
 
        ('less/docs.less', 'modified',
 
         {'added': 34,
 
          'deleted': 0,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('less/scaffolding.less', 'M',
 
        ('less/scaffolding.less', 'modified',
 
         {'added': 1,
 
          'deleted': 3,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('readme.markdown', 'M',
 
        ('readme.markdown', 'modified',
 
         {'added': 1,
 
          'deleted': 10,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
    ],
 
    'git_diff_chmod.diff': [
 
        ('work-horus.xls', 'M',
 
        ('work-horus.xls', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}})
 
    ],
 
    'git_diff_rename_file.diff': [
 
        ('file.xls', 'R',
 
        ('file.xls', 'renamed',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {RENAMED_FILENODE: 'file renamed from work-horus.xls to file.xls'}}),
 
        ('files/var/www/favicon.ico/DEFAULT',
 
         'R',
 
         'renamed',
 
         {'added': 0,
 
          'binary': True,
 
          'deleted': 0,
 
          'ops': {4: 'file renamed from files/var/www/favicon.ico to files/var/www/favicon.ico/DEFAULT',
 
                  6: 'modified file chmod 100644 => 100755'}})
 
    ],
 
    'git_diff_mod_single_binary_file.diff': [
 
        ('US Warszawa.jpg', 'M',
 
        ('US Warszawa.jpg', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {MOD_FILENODE: 'modified file',
 
                  BIN_FILENODE: 'binary diff not shown'}})
 
    ],
 
    'git_diff_binary_and_normal.diff': [
 
        ('img/baseline-10px.png', 'A',
 
        ('img/baseline-10px.png', 'added',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {NEW_FILENODE: 'new file 100644',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
        ('img/baseline-20px.png', 'D',
 
        ('img/baseline-20px.png', 'removed',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {DEL_FILENODE: 'deleted file',
 
                  BIN_FILENODE: 'binary diff not shown'}}),
 
        ('index.html', 'M',
 
        ('index.html', 'modified',
 
         {'added': 3,
 
          'deleted': 2,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('js/global.js', 'D',
 
        ('js/global.js', 'removed',
 
         {'added': 0,
 
          'deleted': 75,
 
          'binary': False,
 
          'ops': {DEL_FILENODE: 'deleted file'}}),
 
        ('js/jquery/hashgrid.js', 'A',
 
        ('js/jquery/hashgrid.js', 'added',
 
         {'added': 340,
 
          'deleted': 0,
 
          'binary': False,
 
          'ops': {NEW_FILENODE: 'new file 100755'}}),
 
        ('less/docs.less', 'M',
 
        ('less/docs.less', 'modified',
 
         {'added': 34,
 
          'deleted': 0,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('less/scaffolding.less', 'M',
 
        ('less/scaffolding.less', 'modified',
 
         {'added': 1,
 
          'deleted': 3,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('readme.markdown', 'M',
 
        ('readme.markdown', 'modified',
 
         {'added': 1,
 
          'deleted': 10,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
    ],
 
    'diff_with_diff_data.diff': [
 
        ('vcs/backends/base.py', 'M',
 
        ('vcs/backends/base.py', 'modified',
 
         {'added': 18,
 
          'deleted': 2,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('vcs/backends/git/repository.py', 'M',
 
        ('vcs/backends/git/repository.py', 'modified',
 
         {'added': 46,
 
          'deleted': 15,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('vcs/backends/hg.py', 'M',
 
        ('vcs/backends/hg.py', 'modified',
 
         {'added': 22,
 
          'deleted': 3,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('vcs/tests/test_git.py', 'M',
 
        ('vcs/tests/test_git.py', 'modified',
 
         {'added': 5,
 
          'deleted': 5,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
        ('vcs/tests/test_repository.py', 'M',
 
        ('vcs/tests/test_repository.py', 'modified',
 
         {'added': 174,
 
          'deleted': 2,
 
          'binary': False,
 
          'ops': {MOD_FILENODE: 'modified file'}}),
 
    ],
 
    'git_diff_modify_binary_file.diff': [
 
        ('file.name', 'M',
 
        ('file.name', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {MOD_FILENODE: 'modified file',
 
                  BIN_FILENODE: 'binary diff not shown'}})
 
    ],
 
    'hg_diff_copy_file.diff': [
 
        ('file2', 'M',
 
        ('file2', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {COPIED_FILENODE: 'file copied from file1 to file2'}}),
 
    ],
 
    'hg_diff_copy_and_modify_file.diff': [
 
        ('file3', 'M',
 
        ('file3', 'modified',
 
         {'added': 1,
 
          'deleted': 0,
 
          'binary': False,
 
          'ops': {COPIED_FILENODE: 'file copied from file2 to file3',
 
                  MOD_FILENODE: 'modified file'}}),
 
    ],
 
    'hg_diff_copy_and_chmod_file.diff': [
 
        ('file4', 'M',
 
        ('file4', 'modified',
 
         {'added': 0,
 
          'deleted': 0,
 
          'binary': True,
 
          'ops': {COPIED_FILENODE: 'file copied from file3 to file4',
 
                  CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}}),
 
    ],
 
    'hg_diff_copy_chmod_and_edit_file.diff': [
 
        ('file5', 'M',
 
        ('file5', 'modified',
 
         {'added': 2,
 
          'deleted': 1,
 
          'binary': False,
 
          'ops': {COPIED_FILENODE: 'file copied from file4 to file5',
 
                  CHMOD_FILENODE: 'modified file chmod 100755 => 100644',
 
                  MOD_FILENODE: 'modified file'}}),
 
    ],
 
    'hg_diff_rename_space_cr.diff': [
 
        ('oh yes', 'R',
 
        ('oh yes', 'renamed',
 
         {'added': 3,
 
          'deleted': 2,
 
          'binary': False,
 
          'ops': {RENAMED_FILENODE: 'file renamed from oh no to oh yes'}}),
 
    ],
 
}
 

	
 

	
 
class TestDiffLib(TestController):
 

	
 
    @parametrize('diff_fixture', DIFF_FIXTURES)
 
    def test_diff(self, diff_fixture):
 
        raw_diff = fixture.load_resource(diff_fixture, strip=False)
 
        vcs = 'hg'
 
        if diff_fixture.startswith('git_'):
 
            vcs = 'git'
 
        diff_processor = DiffProcessor(raw_diff, vcs=vcs)
 
        data = [(x['filename'], x['operation'], x['stats']) for x in diff_processor.parsed]
 
        expected_data = DIFF_FIXTURES[diff_fixture]
 
        assert expected_data == data
 

	
 
    def test_diff_markup(self):
 
        raw_diff = fixture.load_resource('markuptest.diff', strip=False)
 
        diff_processor = DiffProcessor(raw_diff)
 
        chunks = diff_processor.parsed[0]['chunks']
 
        assert not chunks[0]
 
        #from pprint import pprint; pprint(chunks[1])
 
        l = ['\n']
 
        for d in chunks[1]:
 
            l.append('%(action)-7s %(new_lineno)3s %(old_lineno)3s %(line)r\n' % d)
 
        s = ''.join(l)
 
        print s
 
        assert s == r'''
 
context ... ... u'@@ -51,6 +51,13 @@\n'
 
unmod    51  51 u'<u>\t</u>begin();\n'
 
unmod    52  52 u'<u>\t</u>\n'
 
add      53     u'<u>\t</u>int foo;<u class="cr"></u>\n'
 
add      54     u'<u>\t</u>int bar; <u class="cr"></u>\n'
 
add      55     u'<u>\t</u>int baz;<u>\t</u><u class="cr"></u>\n'
 
add      56     u'<u>\t</u>int space; <i></i>'
 
add      57     u'<u>\t</u>int tab;<u>\t</u>\n'
 
add      58     u'<u>\t</u>\n'
 
unmod    59  53 u' <i></i>'
 
del          54 u'<u>\t</u>#define MAX_STEPS (48)\n'
 
add      60     u'<u>\t</u><u class="cr"></u>\n'
 
add      61     u'<u>\t</u>#define MAX_STEPS (64)<u class="cr"></u>\n'
 
unmod    62  55 u'\n'
 
del          56 u'<u>\t</u>#define MIN_STEPS (<del>48</del>)\n'
 
add      63     u'<u>\t</u>#define MIN_STEPS (<ins>42</ins>)\n'
 
'''
0 comments (0 inline, 0 general)