This patch, proposed by Anne Mohsen, allows saving the commit messages
from uncommit so that they can be reused later. It's currently
lacking a test, but I believe not everything in bzr-gtk is well tested
automatically, so perhaps that is not making things worse. Apparently
it is highly desired so I'm going to both post it and start reviewing
it myself.
I'm not sure if putting it into the branch config is the ideal
location but it's probably reasonable.
--
Martin <http://launchpad.net/~mbp/>
=== modified file 'bzrlib/uncommit.py'
--- bzrlib/uncommit.py 2008-05-29 22:15:28 +0000
+++ bzrlib/uncommit.py 2008-09-01 09:13:36 +0000
@@ -26,7 +26,7 @@
)
from bzrlib.branch import Branch
from bzrlib.errors import BoundBranchOutOfDate
-
+from bzrlib.util import bencode
def uncommit(branch, dry_run=False, verbose=False, revno=None, tree=None,
local=False):
@@ -70,6 +70,7 @@
revid_iterator = branch.repository.iter_reverse_revision_history(
old_tip)
+ saved_commit_messages_manager = SavedCommitMessagesManager(tree, branch)
cur_revno = old_revno
new_revision_id = old_tip
graph = branch.repository.get_graph()
@@ -80,6 +81,20 @@
if verbose:
print 'Removing revno %d: %s' % (cur_revno, rev_id)
cur_revno -= 1
+ # NB: performance would be better using the revision graph rather
+ # than the whole revision.
+ rev = branch.repository.get_revision(rev_id)
+ file_info = rev.properties.get('file-info', None)
+ if file_info is None:
+ file_info = {}
+ else:
+ file_info = bencode.bdecode(file_info.encode('UTF-8'))
+ global_message = rev.message
+ if not isinstance(global_message, unicode):
+ # rev.message is sometimes unicode, sometimes str
+ global_message = global_message.decode('UTF-8')
+ # Concatenate comments of all undone revisions:
+ saved_commit_messages_manager.insert(global_message, file_info)
parents = graph.get_parent_map([rev_id]).get(rev_id, None)
if not parents:
continue
@@ -94,6 +109,7 @@
new_revision_id = _mod_revision.NULL_REVISION
if not dry_run:
+ saved_commit_messages_manager.save(tree, branch)
if master is not None:
master.set_last_revision_info(new_revno, new_revision_id)
branch.set_last_revision_info(new_revno, new_revision_id)
@@ -119,3 +135,58 @@
finally:
for item in reversed(unlockable):
item.unlock()
+
+class SavedCommitMessagesManager:
+ """Saves global commit message and utf-8 file_id->message dictionary
+ of per-file commit messages on disk. Re-reads them later for re-using."""
+ def __init__(self, tree=None, branch=None):
+ """If branch is None, builds empty messages, otherwise reads them
+ from branch's disk storage. 'tree' argument is for the future."""
+ if branch is None:
+ self.global_message = u''
+ self.file_messages = {}
+ else:
+ config = branch.get_config()._get_branch_data_config()
+ self.global_message = config.get_user_option('gtk_global_commit_message')
+ if self.global_message is None:
+ self.global_message = u''
+ file_messages = config.get_user_option('gtk_file_commit_messages')
+ if file_messages: # unicode and B-encoded:
+ self.file_messages = bencode.bdecode(file_messages.encode('UTF-8'))
+ else:
+ self.file_messages = {}
+ def get(self):
+ return self.global_message, self.file_messages
+ def is_not_empty(self):
+ return bool(self.global_message or self.file_messages)
+ def insert(self, global_message, file_info):
+ """Formats per-file commit messages (list of dictionaries, one per file)
+ into one utf-8 file_id->message dictionary and merges this with
+ previously existing dictionary. Merges global commit message too."""
+ file_messages = {}
+ for fi in file_info:
+ file_message = fi['message']
+ if file_message:
+ file_messages[fi['file_id']] = file_message # utf-8 strings
+ for k,v in file_messages.iteritems():
+ try:
+ self.file_messages[k] = v + '\n******\n' + self.file_messages[k]
+ except KeyError:
+ self.file_messages[k] = v
+ if self.global_message:
+ self.global_message = global_message + '\n******\n' + self.global_message
+ else:
+ self.global_message = global_message
+ def save(self, tree, branch):
+ # We store in branch's config, which can be a problem if two gcommit
+ # are done in two checkouts of one single branch (comments overwrite
+ # each other). Ideally should be in working tree. But uncommit does
+ # not always have a working tree, though it always has a branch.
+ # 'tree' argument is for the future
+ config = branch.get_config()
+ # should it be named "gtk_" or some more neutral name ("gui_" ?) to
+ # be compatible with qbzr in the future?
+ config.set_user_option('gtk_global_commit_message', self.global_message)
+ # bencode() does not know unicode objects but set_user_option() requires one:
+ config.set_user_option('gtk_file_commit_messages',
+ bencode.bencode(self.file_messages).decode('UTF-8'))
=== modified file 'commit.py'
--- commit.py 2008-08-25 17:20:50 +0000
+++ commit.py 2008-09-01 09:22:53 +0000
@@ -30,6 +30,12 @@
from bzrlib import errors, osutils
from bzrlib.trace import mutter
from bzrlib.util import bencode
+try:
+ from bzrlib.uncommit import SavedCommitMessagesManager
+except ImportError: # old bzrlib
+ can_save_commit_messages = False
+else:
+ can_save_commit_messages = True
from bzrlib.plugins.gtk import _i18n
from bzrlib.plugins.gtk.dialog import question_dialog
@@ -103,8 +109,8 @@
def __init__(self, wt, selected=None, parent=None):
gtk.Dialog.__init__(self, title="Commit to %s" % wt.basedir,
parent=parent,
- flags=0,
- buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
+ flags=0)
+ self.connect('delete-event', self._on_delete_window)
self._wt = wt
# TODO: Do something with this value, it is used by Olive
@@ -113,6 +119,8 @@
self._enable_per_file_commits = True
self._commit_all_changes = True
self.committed_revision_id = None # Nothing has been committed yet
+ if can_save_commit_messages:
+ self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
self.setup_params()
self.construct()
@@ -187,19 +195,27 @@
self._basis_tree.lock_read()
try:
from diff import iter_changes_to_status
+ if can_save_commit_messages:
+ saved_file_messages = self._saved_commit_messages_manager.get()[1]
+ else:
+ saved_file_messages = {}
for (file_id, real_path, change_type, display_path
) in iter_changes_to_status(self._basis_tree, self._wt):
if self._selected and real_path != self._selected:
enabled = False
else:
enabled = True
+ try:
+ default_message = saved_file_messages[file_id]
+ except KeyError:
+ default_message = ''
item_iter = store.append([
file_id,
real_path.encode('UTF-8'),
enabled,
display_path.encode('UTF-8'),
change_type,
- '', # Initial comment
+ default_message, # Initial comment
])
if self._selected and enabled:
initial_cursor = store.get_path(item_iter)
@@ -337,6 +353,10 @@
self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
def _construct_action_pane(self):
+ self._button_cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
+ self._button_cancel.connect('clicked', self._on_cancel_clicked)
+ self._button_cancel.show()
+ self.action_area.pack_end(self._button_cancel)
self._button_commit = gtk.Button(_i18n("Comm_it"), use_underline=True)
self._button_commit.connect('clicked', self._on_commit_clicked)
self._button_commit.set_flags(gtk.CAN_DEFAULT)
@@ -538,6 +558,8 @@
scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self._global_message_text_view = gtk.TextView()
+ if can_save_commit_messages:
+ self._set_global_commit_message(self._saved_commit_messages_manager.get()[0])
self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
scroller.add(self._global_message_text_view)
scroller.set_shadow_type(gtk.SHADOW_IN)
@@ -642,6 +664,32 @@
return files, []
@show_bzr_error
+ def _on_cancel_clicked(self, button):
+ """ Cancel button clicked handler. """
+ self._do_cancel()
+
+ @show_bzr_error
+ def _on_delete_window(self, source, event):
+ """ Delete window handler. """
+ self._do_cancel()
+
+ def _do_cancel(self):
+ """If requested, saves commit messages when cancelling gcommit; they are re-used by a next gcommit"""
+ if can_save_commit_messages:
+ self._saved_commit_messages_manager = SavedCommitMessagesManager()
+ self._saved_commit_messages_manager.insert(self._get_global_commit_message(),
+ self._get_specific_files()[1])
+ if self._saved_commit_messages_manager.is_not_empty(): # maybe worth saving
+ response = question_dialog(_i18n('Commit cancelled'),
+ _i18n('Do you want to save your commit messages ?'),
+ parent=self)
+ if response == gtk.RESPONSE_NO:
+ # save nothing and destroy old comments if any
+ self._saved_commit_messages_manager = SavedCommitMessagesManager()
+ self._saved_commit_messages_manager.save(self._wt, self._wt.branch)
+ self.response(gtk.RESPONSE_CANCEL) # close window
+
+ @show_bzr_error
def _on_commit_clicked(self, button):
""" Commit button clicked handler. """
self._do_commit()
@@ -705,6 +753,8 @@
specific_files=specific_files,
revprops=revprops)
self.committed_revision_id = rev_id
+ # destroy old comments if any
+ SavedCommitMessagesManager().save(self._wt, self._wt.branch)
self.response(gtk.RESPONSE_OK)
def _get_global_commit_message(self):
@@ -713,7 +763,6 @@
return buf.get_text(start, end).decode('utf-8')
def _set_global_commit_message(self, message):
- """Just a helper for the test suite."""
if isinstance(message, unicode):
message = message.encode('UTF-8')
self._global_message_text_view.get_buffer().set_text(message)
--
bzr-gtk mailing list
[email protected]
Modify settings or unsubscribe at:
https://lists.canonical.com/mailman/listinfo/bzr-gtk