1
# -*- coding: UTF-8 -*-
2
1
"""Difference window.
4
3
This module contains the code to manage the diff window which shows
5
4
the changes made between two revisions on a branch.
8
__copyright__ = "Copyright © 2005 Canonical Ltd."
7
__copyright__ = "Copyright 2005 Canonical Ltd."
9
8
__author__ = "Scott James Remnant <scott@ubuntu.com>"
12
11
from cStringIO import StringIO
13
from gi.repository import Gtk
14
from gi.repository import Pango
20
from xml.etree.ElementTree import Element, SubElement, tostring
22
from elementtree.ElementTree import Element, SubElement, tostring
25
from gi.repository import GtkSource
24
26
have_gtksourceview = True
25
27
except ImportError:
26
28
have_gtksourceview = False
30
from gi.repository import GConf
30
32
except ImportError:
35
42
from bzrlib.diff import show_diff_trees
36
from bzrlib.errors import NoSuchFile
43
from bzrlib.patches import parse_patches
37
44
from bzrlib.trace import warning
40
class DiffWindow(gtk.Window):
43
This object represents and manages a single window containing the
44
differences between two revisions on a branch.
45
from bzrlib.plugins.gtk.dialog import (
50
from bzrlib.plugins.gtk.i18n import _i18n
51
from bzrlib.plugins.gtk.window import Window
54
def fallback_guess_language(slm, content_type):
55
for lang_id in slm.get_language_ids():
56
lang = slm.get_language(lang_id)
57
if "text/x-patch" in lang.get_mime_types():
62
class SelectCancelled(Exception):
67
class DiffFileView(Gtk.ScrolledWindow):
68
"""Window for displaying diffs from a diff file"""
47
70
def __init__(self):
48
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
49
self.set_border_width(0)
50
self.set_title("bzrk diff")
52
# Use two thirds of the screen by default
53
screen = self.get_screen()
54
monitor = screen.get_monitor_geometry(0)
55
width = int(monitor.width * 0.66)
56
height = int(monitor.height * 0.66)
57
self.set_default_size(width, height)
71
super(DiffFileView, self).__init__()
61
75
def construct(self):
62
"""Construct the window contents."""
63
# The window consists of a pane containing: the
64
# hierarchical list of files on the left, and the diff
65
# for the currently selected file on the right.
76
self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
77
self.set_shadow_type(Gtk.ShadowType.IN)
79
if have_gtksourceview:
80
self.buffer = GtkSource.Buffer()
81
lang_manager = GtkSource.LanguageManager.get_default()
82
language = lang_manager.guess_language(None, "text/x-patch")
84
self.apply_gedit_colors(self.buffer)
85
self.apply_colordiff_colors(self.buffer)
86
self.buffer.set_language(language)
87
self.buffer.set_highlight_syntax(True)
89
self.sourceview = GtkSource.View(buffer=self.buffer)
91
self.buffer = Gtk.TextBuffer()
92
self.sourceview = Gtk.TextView(self.buffer)
94
self.sourceview.set_editable(False)
95
self.sourceview.modify_font(Pango.FontDescription("Monospace"))
96
self.add(self.sourceview)
97
self.sourceview.show()
100
def apply_gedit_colors(buf):
101
"""Set style to that specified in gedit configuration.
103
This method needs the gconf module.
105
:param buf: a GtkSource.Buffer object.
107
GEDIT_SCHEME_PATH = '/apps/gedit-2/preferences/editor/colors/scheme'
108
GEDIT_USER_STYLES_PATH = os.path.expanduser('~/.gnome2/gedit/styles')
110
client = GConf.Client.get_default()
111
style_scheme_name = client.get_string(GEDIT_SCHEME_PATH)
112
if style_scheme_name is not None:
113
style_scheme_mgr = GtkSource.StyleSchemeManager()
114
style_scheme_mgr.append_search_path(GEDIT_USER_STYLES_PATH)
116
style_scheme = style_scheme_mgr.get_scheme(style_scheme_name)
118
if style_scheme is not None:
119
buf.set_style_scheme(style_scheme)
122
def apply_colordiff_colors(klass, buf):
123
"""Set style colors for lang using the colordiff configuration file.
125
Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
127
:param buf: a "Diff" GtkSource.Buffer object.
129
scheme_manager = GtkSource.StyleSchemeManager()
130
style_scheme = scheme_manager.get_scheme('colordiff')
132
# if style scheme not found, we'll generate it from colordiffrc
133
# TODO: reload if colordiffrc has changed.
134
if style_scheme is None:
137
for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
138
f = os.path.expanduser(f)
139
if os.path.exists(f):
143
warning('could not open file %s: %s' % (f, str(e)))
145
colors.update(klass.parse_colordiffrc(f))
149
# ~/.colordiffrc does not exist
153
# map GtkSourceView2 scheme styles to colordiff names
154
# since GSV is richer, accept new names for extra bits,
155
# defaulting to old names if they're not present
156
'diff:added-line': ['newtext'],
157
'diff:removed-line': ['oldtext'],
158
'diff:location': ['location', 'diffstuff'],
159
'diff:file': ['file', 'diffstuff'],
160
'diff:special-case': ['specialcase', 'diffstuff'],
163
converted_colors = {}
164
for name, values in mapping.items():
167
color = colors.get(value, None)
168
if color is not None:
172
converted_colors[name] = color
174
# some xml magic to produce needed style scheme description
175
e_style_scheme = Element('style-scheme')
176
e_style_scheme.set('id', 'colordiff')
177
e_style_scheme.set('_name', 'ColorDiff')
178
e_style_scheme.set('version', '1.0')
179
for name, color in converted_colors.items():
180
style = SubElement(e_style_scheme, 'style')
181
style.set('name', name)
182
style.set('foreground', '#%s' % color)
184
scheme_xml = tostring(e_style_scheme, 'UTF-8')
185
if not os.path.exists(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles')):
186
os.makedirs(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles'))
187
file(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles/colordiff.xml'), 'w').write(scheme_xml)
189
scheme_manager.force_rescan()
190
style_scheme = scheme_manager.get_scheme('colordiff')
192
buf.set_style_scheme(style_scheme)
195
def parse_colordiffrc(fileobj):
196
"""Parse fileobj as a colordiff configuration file.
198
:return: A dict with the key -> value pairs.
202
if re.match(r'^\s*#', line):
206
key, val = line.split('=', 1)
207
colors[key.strip()] = val.strip()
210
def set_trees(self, rev_tree, parent_tree):
211
self.rev_tree = rev_tree
212
self.parent_tree = parent_tree
213
# self._build_delta()
215
# def _build_delta(self):
216
# self.parent_tree.lock_read()
217
# self.rev_tree.lock_read()
219
# self.delta = iter_changes_to_status(self.parent_tree, self.rev_tree)
220
# self.path_to_status = {}
221
# self.path_to_diff = {}
222
# source_inv = self.parent_tree.inventory
223
# target_inv = self.rev_tree.inventory
224
# for (file_id, real_path, change_type, display_path) in self.delta:
225
# self.path_to_status[real_path] = u'=== %s %s' % (change_type, display_path)
226
# if change_type in ('modified', 'renamed and modified'):
227
# source_ie = source_inv[file_id]
228
# target_ie = target_inv[file_id]
230
# source_ie.diff(internal_diff, *old path, *old_tree,
231
# *new_path, target_ie, self.rev_tree,
233
# self.path_to_diff[real_path] =
236
# self.rev_tree.unlock()
237
# self.parent_tree.unlock()
239
def show_diff(self, specific_files):
241
if specific_files is None:
242
self.buffer.set_text(self._diffs[None])
244
for specific_file in specific_files:
245
sections.append(self._diffs[specific_file])
246
self.buffer.set_text(''.join(sections))
249
class DiffView(DiffFileView):
250
"""This is the soft and chewy filling for a DiffWindow."""
253
super(DiffView, self).__init__()
255
self.parent_tree = None
257
def show_diff(self, specific_files):
258
"""Show the diff for the specified files"""
260
show_diff_trees(self.parent_tree, self.rev_tree, s, specific_files,
261
old_label='', new_label='',
262
# path_encoding=sys.getdefaultencoding()
263
# The default is utf-8, but we interpret the file
264
# contents as getdefaultencoding(), so we should
265
# probably try to make the paths in the same encoding.
267
# str.decode(encoding, 'replace') doesn't do anything. Because if a
268
# character is not valid in 'encoding' there is nothing to replace, the
269
# 'replace' is for 'str.encode()'
271
decoded = s.getvalue().decode(sys.getdefaultencoding())
272
except UnicodeDecodeError:
274
decoded = s.getvalue().decode('UTF-8')
275
except UnicodeDecodeError:
276
decoded = s.getvalue().decode('iso-8859-1')
277
# This always works, because every byte has a valid
278
# mapping from iso-8859-1 to Unicode
279
# TextBuffer must contain pure UTF-8 data
280
self.buffer.set_text(decoded.encode('UTF-8'))
283
class DiffWidget(Gtk.HPaned):
288
super(DiffWidget, self).__init__()
70
290
# The file hierarchy: a scrollable treeview
71
scrollwin = gtk.ScrolledWindow()
72
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
73
scrollwin.set_shadow_type(gtk.SHADOW_IN)
291
scrollwin = Gtk.ScrolledWindow()
292
scrollwin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
293
scrollwin.set_shadow_type(Gtk.ShadowType.IN)
294
self.pack1(scrollwin)
77
self.model = gtk.TreeStore(str, str)
78
self.treeview = gtk.TreeView(self.model)
297
self.model = Gtk.TreeStore(str, str)
298
self.treeview = Gtk.TreeView(model=self.model)
79
299
self.treeview.set_headers_visible(False)
80
300
self.treeview.set_search_column(1)
81
301
self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
82
302
scrollwin.add(self.treeview)
83
303
self.treeview.show()
85
cell = gtk.CellRendererText()
305
cell = Gtk.CellRendererText()
86
306
cell.set_property("width-chars", 20)
87
column = gtk.TreeViewColumn()
88
column.pack_start(cell, expand=True)
307
column = Gtk.TreeViewColumn()
308
column.pack_start(cell, True)
89
309
column.add_attribute(cell, "text", 0)
90
310
self.treeview.append_column(column)
312
def set_diff_text(self, lines):
313
"""Set the current diff from a list of lines
315
:param lines: The diff to show, in unified diff format
92
317
# The diffs of the selected file: a scrollable source or
94
scrollwin = gtk.ScrolledWindow()
95
scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
96
scrollwin.set_shadow_type(gtk.SHADOW_IN)
100
if have_gtksourceview:
101
self.buffer = gtksourceview.SourceBuffer()
102
slm = gtksourceview.SourceLanguagesManager()
103
gsl = slm.get_language_from_mime_type("text/x-patch")
105
self.apply_gedit_colors(gsl)
106
self.apply_colordiff_colors(gsl)
107
self.buffer.set_language(gsl)
108
self.buffer.set_highlight(True)
110
sourceview = gtksourceview.SourceView(self.buffer)
112
self.buffer = gtk.TextBuffer()
113
sourceview = gtk.TextView(self.buffer)
115
sourceview.set_editable(False)
116
sourceview.modify_font(pango.FontDescription("Monospace"))
117
scrollwin.add(sourceview)
120
def set_diff(self, description, rev_tree, parent_tree):
320
def set_diff_text_sections(self, sections):
321
if getattr(self, 'diff_view', None) is None:
322
self.diff_view = DiffFileView()
323
self.pack2(self.diff_view)
324
self.diff_view.show()
325
for oldname, newname, patch in sections:
326
self.diff_view._diffs[newname] = str(patch)
329
self.model.append(None, [oldname, newname])
330
self.diff_view.show_diff(None)
332
def set_diff(self, rev_tree, parent_tree):
121
333
"""Set the differences showed by this window.
123
335
Compares the two trees and populates the window with the
338
if getattr(self, 'diff_view', None) is None:
339
self.diff_view = DiffView()
340
self.pack2(self.diff_view)
341
self.diff_view.show()
342
self.diff_view.set_trees(rev_tree, parent_tree)
126
343
self.rev_tree = rev_tree
127
344
self.parent_tree = parent_tree
174
392
if specific_files == [ None ]:
176
394
elif specific_files == [ "" ]:
180
show_diff_trees(self.parent_tree, self.rev_tree, s, specific_files)
181
self.buffer.set_text(s.getvalue().decode(sys.getdefaultencoding(), 'replace'))
184
def apply_gedit_colors(lang):
185
"""Set style for lang to that specified in gedit configuration.
187
This method needs the gconf module.
189
:param lang: a gtksourceview.SourceLanguage object.
191
GEDIT_SYNTAX_PATH = '/apps/gedit-2/preferences/syntax_highlighting'
192
GEDIT_LANG_PATH = GEDIT_SYNTAX_PATH + '/' + lang.get_id()
194
client = gconf.client_get_default()
195
client.add_dir(GEDIT_LANG_PATH, gconf.CLIENT_PRELOAD_NONE)
197
for tag in lang.get_tags():
198
tag_id = tag.get_id()
199
gconf_key = GEDIT_LANG_PATH + '/' + tag_id
200
style_string = client.get_string(gconf_key)
202
if style_string is None:
205
# function to get a bool from a string that's either '0' or '1'
206
string_bool = lambda x: bool(int(x))
208
# style_string is a string like "2/#FFCCAA/#000000/0/1/0/0"
209
# values are: mask, fg, bg, italic, bold, underline, strike
210
# this packs them into (str_value, attr_name, conv_func) tuples
211
items = zip(style_string.split('/'), ['mask', 'foreground',
212
'background', 'italic', 'bold', 'underline', 'strikethrough' ],
213
[ int, gtk.gdk.color_parse, gtk.gdk.color_parse, string_bool,
214
string_bool, string_bool, string_bool ]
217
style = gtksourceview.SourceTagStyle()
219
# XXX The mask attribute controls whether the present values of
220
# foreground and background color should in fact be used. Ideally
221
# (and that's what gedit does), one could set all three attributes,
222
# and let the TagStyle object figure out which colors to use.
223
# However, in the GtkSourceview python bindings, the mask attribute
224
# is read-only, and it's derived instead from the colors being
225
# set or not. This means that we have to sometimes refrain from
226
# setting fg or bg colors, depending on the value of the mask.
227
# This code could go away if mask were writable.
228
mask = int(items[0][0])
229
if not (mask & 1): # GTK_SOURCE_TAG_STYLE_USE_BACKGROUND
231
if not (mask & 2): # GTK_SOURCE_TAG_STYLE_USE_FOREGROUND
233
items[0:1] = [] # skip the mask unconditionally
235
for value, attr, func in items:
239
warning('gconf key %s contains an invalid value: %s'
242
setattr(style, attr, value)
244
lang.set_tag_style(tag_id, style)
247
def apply_colordiff_colors(lang):
248
"""Set style colors for lang using the colordiff configuration file.
250
Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
252
:param lang: a "Diff" gtksourceview.SourceLanguage object.
256
for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
257
f = os.path.expanduser(f)
258
if os.path.exists(f):
262
warning('could not open file %s: %s' % (f, str(e)))
264
colors.update(DiffWindow.parse_colordiffrc(f))
268
# ~/.colordiffrc does not exist
395
specific_files = None
397
self.diff_view.show_diff(specific_files)
399
def _on_wraplines_toggled(self, widget=None, wrap=False):
400
"""Callback for when the wrap lines checkbutton is toggled"""
401
if wrap or widget.get_active():
402
self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.WORD)
404
self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.NONE)
406
class DiffWindow(Window):
409
This object represents and manages a single window containing the
410
differences between two revisions on a branch.
413
def __init__(self, parent=None, operations=None):
414
super(DiffWindow, self).__init__(parent=parent)
415
self.set_border_width(0)
416
self.set_title("bzrk diff")
418
# Use two thirds of the screen by default
419
screen = self.get_screen()
420
monitor = screen.get_monitor_geometry(0)
421
width = int(monitor.width * 0.66)
422
height = int(monitor.height * 0.66)
423
self.set_default_size(width, height)
424
self.construct(operations)
426
def construct(self, operations):
427
"""Construct the window contents."""
428
self.vbox = Gtk.VBox()
431
self.diff = DiffWidget()
432
self.vbox.pack_end(self.diff, True, True, 0)
434
# Build after DiffWidget to connect signals
435
menubar = self._get_menu_bar()
436
self.vbox.pack_start(menubar, False, False, 0)
437
hbox = self._get_button_bar(operations)
439
self.vbox.pack_start(hbox, False, True, 0)
442
def _get_menu_bar(self):
443
menubar = Gtk.MenuBar()
445
mb_view = Gtk.MenuItem.new_with_mnemonic(_i18n("_View"))
446
mb_view_menu = Gtk.Menu()
447
mb_view_wrapsource = Gtk.CheckMenuItem.new_with_mnemonic(
448
_i18n("Wrap _Long Lines"))
449
mb_view_wrapsource.connect('activate', self.diff._on_wraplines_toggled)
450
mb_view_wrapsource.show()
451
mb_view_menu.append(mb_view_wrapsource)
453
mb_view.set_submenu(mb_view_menu)
455
menubar.append(mb_view)
459
def _get_button_bar(self, operations):
460
"""Return a button bar to use.
462
:return: None, meaning that no button bar will be used.
464
if operations is None:
466
hbox = Gtk.HButtonBox()
467
hbox.set_layout(Gtk.ButtonBoxStyle.START)
468
for title, method in operations:
469
merge_button = Gtk.Button(title)
471
merge_button.set_relief(Gtk.ReliefStyle.NONE)
472
merge_button.connect("clicked", method)
473
hbox.pack_start(merge_button, expand=False, fill=True)
477
def _get_merge_target(self):
478
d = Gtk.FileChooserDialog('Merge branch', self,
479
Gtk.FileChooserAction.SELECT_FOLDER,
480
buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
482
Gtk.ResponseType.CANCEL,))
485
if result != Gtk.ResponseType.OK:
486
raise SelectCancelled()
487
return d.get_current_folder_uri()
491
def _merge_successful(self):
492
# No conflicts found.
493
info_dialog(_i18n('Merge successful'),
494
_i18n('All changes applied successfully.'))
496
def _conflicts(self):
497
warning_dialog(_i18n('Conflicts encountered'),
498
_i18n('Please resolve the conflicts manually'
499
' before committing.'))
501
def _handle_error(self, e):
502
error_dialog('Error', str(e))
504
def _get_save_path(self, basename):
505
d = Gtk.FileChooserDialog('Save As', self,
506
Gtk.FileChooserAction.SAVE,
507
buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
509
Gtk.ResponseType.CANCEL,))
510
d.set_current_name(basename)
513
if result != Gtk.ResponseType.OK:
514
raise SelectCancelled()
515
return urlutils.local_path_from_url(d.get_uri())
519
def set_diff(self, description, rev_tree, parent_tree):
520
"""Set the differences showed by this window.
522
Compares the two trees and populates the window with the
525
self.diff.set_diff(rev_tree, parent_tree)
526
self.set_title(description + " - bzrk diff")
528
def set_file(self, file_path):
529
self.diff.set_file(file_path)
532
class DiffController(object):
534
def __init__(self, path, patch, window=None, allow_dirty=False):
537
self.allow_dirty = allow_dirty
539
window = DiffWindow(operations=self._provide_operations())
540
self.initialize_window(window)
543
def initialize_window(self, window):
544
window.diff.set_diff_text_sections(self.get_diff_sections())
545
window.set_title(self.path + " - diff")
547
def get_diff_sections(self):
548
yield "Complete Diff", None, ''.join(self.patch)
549
# allow_dirty was added to parse_patches in bzrlib 2.2b1
550
if 'allow_dirty' in inspect.getargspec(parse_patches).args:
551
patches = parse_patches(self.patch, allow_dirty=self.allow_dirty)
553
patches = parse_patches(self.patch)
554
for patch in patches:
555
oldname = patch.oldname.split('\t')[0]
556
newname = patch.newname.split('\t')[0]
557
yield oldname, newname, str(patch)
559
def perform_save(self, window):
561
save_path = self.window._get_save_path(osutils.basename(self.path))
562
except SelectCancelled:
272
# map GtkSourceView tags to colordiff names
273
# since GSV is richer, accept new names for extra bits,
274
# defaulting to old names if they're not present
275
'Added@32@line': ['newtext'],
276
'Removed@32@line': ['oldtext'],
277
'Location': ['location', 'diffstuff'],
278
'Diff@32@file': ['file', 'diffstuff'],
279
'Special@32@case': ['specialcase', 'diffstuff'],
282
for tag in lang.get_tags():
283
tag_id = tag.get_id()
284
keys = mapping.get(tag_id, [])
288
color = colors.get(key, None)
289
if color is not None:
295
style = gtksourceview.SourceTagStyle()
297
style.foreground = gtk.gdk.color_parse(color)
299
warning('not a valid color: %s' % color)
301
lang.set_tag_style(tag_id, style)
304
def parse_colordiffrc(fileobj):
305
"""Parse fileobj as a colordiff configuration file.
307
:return: A dict with the key -> value pairs.
311
if re.match(r'^\s*#', line):
315
key, val = line.split('=', 1)
316
colors[key.strip()] = val.strip()
564
source = open(self.path, 'rb')
566
target = open(save_path, 'wb')
568
osutils.pumpfile(source, target)
574
def _provide_operations(self):
575
return [('Save', self.perform_save)]
578
class MergeDirectiveController(DiffController):
580
def __init__(self, path, directive, window=None):
581
super(MergeDirectiveController, self).__init__(
582
path, directive.patch.splitlines(True), window)
583
self.directive = directive
584
self.merge_target = None
586
def _provide_operations(self):
587
return [('Merge', self.perform_merge), ('Save', self.perform_save)]
589
def perform_merge(self, window):
590
if self.merge_target is None:
592
self.merge_target = self.window._get_merge_target()
593
except SelectCancelled:
595
tree = workingtree.WorkingTree.open(self.merge_target)
599
if tree.has_changes():
600
raise errors.UncommittedChanges(tree)
601
merger, verified = _mod_merge.Merger.from_mergeable(
602
tree, self.directive, pb=None)
603
merger.merge_type = _mod_merge.Merge3Merger
604
conflict_count = merger.do_merge()
606
if conflict_count == 0:
607
self.window._merge_successful()
609
self.window._conflicts()
610
# There are conflicts to be resolved.
611
self.window.destroy()
613
self.window._handle_error(e)
618
def iter_changes_to_status(source, target):
619
"""Determine the differences between trees.
621
This is a wrapper around iter_changes which just yields more
622
understandable results.
624
:param source: The source tree (basis tree)
625
:param target: The target tree
626
:return: A list of (file_id, real_path, change_type, display_path)
631
renamed_and_modified = 'renamed and modified'
632
modified = 'modified'
633
kind_changed = 'kind changed'
636
# TODO: Handle metadata changes
643
for (file_id, paths, changed_content, versioned, parent_ids, names,
644
kinds, executables) in target.iter_changes(source):
646
# Skip the root entry if it isn't very interesting
647
if parent_ids == (None, None):
654
source_marker = osutils.kind_marker(kinds[0])
658
# We assume bzr will flag only files in that case,
659
# there may be a bzr bug there as only files seems to
660
# not receive any kind.
661
marker = osutils.kind_marker('file')
663
marker = osutils.kind_marker(kinds[0])
665
marker = osutils.kind_marker(kinds[1])
668
if real_path is None:
670
assert real_path is not None
672
present_source = versioned[0] and kinds[0] is not None
673
present_target = versioned[1] and kinds[1] is not None
675
if kinds[0] is None and kinds[1] is None:
676
change_type = missing
677
display_path = real_path + marker
678
elif present_source != present_target:
682
assert present_source
683
change_type = removed
684
display_path = real_path + marker
685
elif names[0] != names[1] or parent_ids[0] != parent_ids[1]:
687
if changed_content or executables[0] != executables[1]:
689
change_type = renamed_and_modified
691
change_type = renamed
692
display_path = (paths[0] + source_marker
693
+ ' => ' + paths[1] + marker)
694
elif kinds[0] != kinds[1]:
695
change_type = kind_changed
696
display_path = (paths[0] + source_marker
697
+ ' => ' + paths[1] + marker)
698
elif changed_content or executables[0] != executables[1]:
699
change_type = modified
700
display_path = real_path + marker
702
assert False, "How did we get here?"
704
status.append((file_id, real_path, change_type, display_path))