15
15
"""Graphical support for Bazaar using GTK.
17
17
This plugin includes:
18
commit-notify Start the graphical notifier of commits.
18
19
gannotate GTK+ annotate.
19
20
gbranch GTK+ branching.
20
21
gcheckout GTK+ checkout.
21
gcommit GTK+ commit dialog.
22
gcommit GTK+ commit dialog
22
23
gconflicts GTK+ conflicts.
23
24
gdiff Show differences in working tree in a GTK+ Window.
24
25
ginit Initialise a new branch.
25
gmerge GTK+ merge dialog
26
26
gmissing GTK+ missing revisions dialog.
27
27
gpreferences GTK+ preferences dialog.
29
gsend GTK+ send merge directive.
30
gstatus GTK+ status dialog.
29
gstatus GTK+ status dialog
31
30
gtags Manage branch tags.
32
31
visualise Graphically visualise this branch.
44
43
version_string = '%d.%d.%d%s%d' % version_info
45
44
__version__ = version_string
47
required_bzrlib = (1, 3)
49
46
def check_bzrlib_version(desired):
50
47
"""Check that bzrlib is compatible.
52
49
If version is < bzr-gtk version, assume incompatible.
50
If version == bzr-gtk version, assume completely compatible
51
If version == bzr-gtk version + 1, assume compatible, with deprecations
52
Otherwise, assume incompatible.
54
desired_plus = (desired[0], desired[1]+1)
54
55
bzrlib_version = bzrlib.version_info[:2]
56
if bzrlib_version == desired or (bzrlib_version == desired_plus and
57
bzrlib.version_info[3] == 'dev'):
56
60
from bzrlib.trace import warning
57
61
except ImportError:
62
66
warning('Installed Bazaar version %s is too old to be used with bzr-gtk'
63
67
' %s.' % (bzrlib.__version__, __version__))
64
68
raise BzrError('Version mismatch: %r, %r' % (version_info, bzrlib.version_info) )
70
warning('bzr-gtk is not up to date with installed bzr version %s.'
71
' \nThere should be a newer version available, e.g. %i.%i.'
72
% (bzrlib.__version__, bzrlib_version[0], bzrlib_version[1]))
67
75
if version_info[2] == "final":
68
check_bzrlib_version(required_bzrlib)
76
check_bzrlib_version(version_info[:2])
70
78
from bzrlib.trace import warning
71
79
if __name__ != 'bzrlib.plugins.gtk':
104
111
bzrlib.ui.ui_factory = GtkUIFactory()
108
return [os.path.dirname(__file__),
109
"/usr/share/bzr-gtk",
110
"/usr/local/share/bzr-gtk"]
113
def data_path(*args):
114
for basedir in data_basedirs():
115
path = os.path.join(basedir, *args)
116
if os.path.exists(path):
121
def icon_path(*args):
122
return data_path(os.path.join('icons', *args))
126
pygtk = import_pygtk()
129
except RuntimeError, e:
130
if str(e) == "could not open display":
115
return os.path.dirname(__file__)
136
118
class GTKCommand(Command):
137
119
"""Abstract class providing GTK specific run commands."""
121
def open_display(self):
122
pygtk = import_pygtk()
125
except RuntimeError, e:
126
if str(e) == "could not open display":
141
133
dialog = self.get_gtk_dialog(os.path.abspath('.'))
196
188
if revision is not None:
197
189
if len(revision) == 1:
199
revision_id = revision[0].as_revision_id(tree1.branch)
191
revision_id = revision[0].in_history(branch).rev_id
200
192
tree2 = branch.repository.revision_tree(revision_id)
201
193
elif len(revision) == 2:
202
revision_id_0 = revision[0].as_revision_id(branch)
194
revision_id_0 = revision[0].in_history(branch).rev_id
203
195
tree2 = branch.repository.revision_tree(revision_id_0)
204
revision_id_1 = revision[1].as_revision_id(branch)
196
revision_id_1 = revision[1].in_history(branch).rev_id
205
197
tree1 = branch.repository.revision_tree(revision_id_1)
232
def start_viz_window(branch, revisions, limit=None):
224
def start_viz_window(branch, revision, limit=None):
233
225
"""Start viz on branch with revision revision.
235
227
:return: The viz window object.
237
from viz import BranchWindow
238
return BranchWindow(branch, revisions, limit)
229
from viz.branchwin import BranchWindow
232
pp.set_branch(branch, revision, limit)
233
# cleanup locks when the window is closed
234
pp.connect("destroy", lambda w: branch.unlock())
241
238
class cmd_visualise(Command):
252
249
Option('limit', "Maximum number of revisions to display.",
254
takes_args = [ "locations*" ]
251
takes_args = [ "location?" ]
255
252
aliases = [ "visualize", "vis", "viz" ]
257
def run(self, locations_list, revision=None, limit=None):
254
def run(self, location=".", revision=None, limit=None):
259
if locations_list is None:
260
locations_list = ["."]
262
for location in locations_list:
263
(br, path) = branch.Branch.open_containing(location)
256
(br, path) = branch.Branch.open_containing(location)
264
259
if revision is None:
265
revids.append(br.last_revision())
260
revid = br.last_revision()
267
revids.append(revision[0].as_revision_id(br))
269
pp = start_viz_window(br, revids, limit)
270
pp.connect("destroy", lambda w: gtk.main_quit())
264
(revno, revid) = revision[0].in_history(br)
267
pp = start_viz_window(br, revid, limit)
268
pp.connect("destroy", lambda w: gtk.main_quit())
275
275
class cmd_gannotate(GTKCommand):
314
314
if revision is not None:
315
315
if len(revision) != 1:
316
316
raise BzrCommandError("Only 1 revion may be specified.")
317
revision_id = revision[0].as_revision_id(br)
317
revision_id = revision[0].in_history(br).rev_id
318
318
tree = br.repository.revision_tree(revision_id)
320
320
revision_id = getattr(tree, 'get_revision_id', lambda: None)()
322
window = GAnnotateWindow(all, plain, branch=br)
322
window = GAnnotateWindow(all, plain)
323
323
window.connect("destroy", lambda w: gtk.main_quit())
324
window.set_title(path + " - gannotate")
324
325
config = GAnnotateConfig(window)
386
387
aliases = [ "gst" ]
387
388
takes_args = ['PATH?']
388
takes_options = ['revision']
390
def run(self, path='.', revision=None):
391
def run(self, path='.'):
393
gtk = self.open_display()
393
394
from status import StatusDialog
394
395
(wt, wt_path) = workingtree.WorkingTree.open_containing(path)
396
if revision is not None:
398
revision_id = revision[0].as_revision_id(wt.branch)
400
from bzrlib.errors import BzrError
401
raise BzrError('Revision %r doesn\'t exist' % revision[0].user_spec )
405
status = StatusDialog(wt, wt_path, revision_id)
396
status = StatusDialog(wt, wt_path)
406
397
status.connect("destroy", gtk.main_quit)
410
class cmd_gsend(GTKCommand):
411
"""GTK+ send merge directive.
415
(br, path) = branch.Branch.open_containing(".")
417
from bzrlib.plugins.gtk.mergedirective import SendMergeDirectiveDialog
418
from StringIO import StringIO
419
dialog = SendMergeDirectiveDialog(br)
420
if dialog.run() == gtk.RESPONSE_OK:
422
outf.writelines(dialog.get_merge_directive().to_lines())
423
mail_client = br.get_config().get_mail_client()
424
mail_client.compose_merge_request(dialog.get_mail_to(), "[MERGE]",
430
402
class cmd_gconflicts(GTKCommand):
433
405
Select files from the list of conflicts and run an external utility to
437
409
(wt, path) = workingtree.WorkingTree.open_containing('.')
439
411
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
440
412
dialog = ConflictsDialog(wt)
444
417
class cmd_gpreferences(GTKCommand):
445
418
""" GTK+ preferences dialog.
450
423
from bzrlib.plugins.gtk.preferences import PreferencesWindow
451
424
dialog = PreferencesWindow()
455
class cmd_gmerge(Command):
456
""" GTK+ merge dialog
459
takes_args = ["merge_from_path?"]
460
def run(self, merge_from_path=None):
461
from bzrlib import workingtree
462
from bzrlib.plugins.gtk.dialog import error_dialog
463
from bzrlib.plugins.gtk.merge import MergeDialog
465
(wt, path) = workingtree.WorkingTree.open_containing('.')
466
old_tree = wt.branch.repository.revision_tree(wt.branch.last_revision())
467
delta = wt.changes_from(old_tree)
468
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
469
error_dialog(_i18n('There are local changes in the branch'),
470
_i18n('Please commit or revert the changes before merging.'))
472
parent_branch_path = wt.branch.get_parent()
473
merge = MergeDialog(wt, path, parent_branch_path)
474
response = merge.run()
478
429
class cmd_gmissing(Command):
479
430
""" GTK+ missing revisions dialog.
551
500
for cmd in commands:
552
501
register_command(cmd)
504
class cmd_commit_notify(GTKCommand):
505
"""Run the bzr commit notifier.
507
This is a background program which will pop up a notification on the users
508
screen when a commit occurs.
512
from notify import NotifyPopupMenu
513
gtk = self.open_display()
514
menu = NotifyPopupMenu()
515
icon = gtk.status_icon_new_from_file(os.path.join(data_path(), "bzr-icon-64.png"))
516
icon.connect('popup-menu', menu.display)
522
from bzrlib.bzrdir import BzrDir
523
from bzrlib import errors
524
from bzrlib.osutils import format_date
525
from bzrlib.transport import get_transport
526
if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
528
from bzrlib.plugins.dbus import activity
529
bus = dbus.SessionBus()
530
# get the object so we can subscribe to callbacks from it.
531
broadcast_service = bus.get_object(
532
activity.Broadcast.DBUS_NAME,
533
activity.Broadcast.DBUS_PATH)
535
def catch_branch(revision_id, urls):
536
# TODO: show all the urls, or perhaps choose the 'best'.
539
if isinstance(revision_id, unicode):
540
revision_id = revision_id.encode('utf8')
541
transport = get_transport(url)
542
a_dir = BzrDir.open_from_transport(transport)
543
branch = a_dir.open_branch()
544
revno = branch.revision_id_to_revno(revision_id)
545
revision = branch.repository.get_revision(revision_id)
546
summary = 'New revision %d in %s' % (revno, url)
547
body = 'Committer: %s\n' % revision.committer
548
body += 'Date: %s\n' % format_date(revision.timestamp,
551
body += revision.message
552
body = cgi.escape(body)
553
nw = pynotify.Notification(summary, body)
554
def start_viz(notification=None, action=None, data=None):
555
"""Start the viz program."""
556
pp = start_viz_window(branch, revision_id)
558
def start_branch(notification=None, action=None, data=None):
559
"""Start a Branch dialog"""
560
from bzrlib.plugins.gtk.branch import BranchDialog
561
bd = BranchDialog(remote_path=url)
563
nw.add_action("inspect", "Inspect", start_viz, None)
564
nw.add_action("branch", "Branch", start_branch, None)
570
broadcast_service.connect_to_signal("Revision", catch_branch,
571
dbus_interface=activity.Broadcast.DBUS_INTERFACE)
572
pynotify.init("bzr commit-notify")
575
register_command(cmd_commit_notify)
555
578
class cmd_gselftest(GTKCommand):
556
579
"""Version of selftest that displays a notification at the end"""
592
615
takes_options = ['verbose',
593
616
Option('one', short_name='1',
594
help='Stop when one test fails.'),
595
Option('benchmark', help='Run the benchmarks.'),
617
help='stop when one test fails'),
618
Option('benchmark', help='run the benchmarks.'),
596
619
Option('lsprof-timed',
597
help='Generate lsprof output for benchmarked'
620
help='generate lsprof output for benchmarked'
598
621
' sections of code.'),
599
622
Option('list-only',
600
help='List the tests instead of running them.'),
623
help='list the tests instead of running them'),
601
624
Option('randomize', type=str, argname="SEED",
602
help='Randomize the order of tests using the given'
603
' seed or "now" for the current time.'),
625
help='randomize the order of tests using the given'
626
' seed or "now" for the current time'),
605
628
takes_args = ['testspecs*']