Merge lp:~maria-captains/bzr-gtk/gcommit-fixes into lp:bzr-gtk/gtk2

Proposed by Sergei Golubchik
Status: Work in progress
Proposed branch: lp:~maria-captains/bzr-gtk/gcommit-fixes
Merge into: lp:bzr-gtk/gtk2
Diff against target: 261 lines (+128/-5)
3 files modified
NEWS (+4/-0)
bugtrackerex.py (+47/-0)
commit.py (+77/-5)
To merge this branch: bzr merge lp:~maria-captains/bzr-gtk/gcommit-fixes
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) Needs Fixing
Review via email: mp+48206@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Thanks for working on this! :)

I don't understand why bugtrackerex.py is necessary. If the existing code in bzrlib.bugtracker isn't sufficient, please extend it rather than duplicating it here.

Is there any particular reason why you've added a configuration option, rather than always allowing users to specify the bug # ? I think it'd be reasonable to enable this functionality unconditionally.

Revision history for this message
Jelmer Vernooij (jelmer) :
review: Needs Fixing
Revision history for this message
Sergei Golubchik (sergii) wrote :

Hi, Jelmer!

On Feb 01, Jelmer Vernooij wrote:
> Thanks for working on this! :)
>
> I don't understand why bugtrackerex.py is necessary. If the existing
> code in bzrlib.bugtracker isn't sufficient, please extend it rather
> than duplicating it here.

I wanted to stay within bzr-gtk.

How to do that otherwise - you want me to submit a merge request to bzr
tree with this change (extending bzrlib.bugtracker), and when it's
accepted do another one for bzr-gtk ?

That would be the right way, I agree. Although I afraid that bzr, as a
bigger project with more developers will be slower to accept a patch.

But I could try, by all means.

> Is there any particular reason why you've added a configuration
> option, rather than always allowing users to specify the bug # ? I
> think it'd be reasonable to enable this functionality unconditionally.

I copied this from per-file comments. It also has a configuration
option.

I got an idea (from the comments in the code) that it was done
to have more screen space dedicated to diff and changeset comment and
not waste it on per-file comments, when user does not want them.

So, I figured out that it applies as well to "bugs" input field.

If you'd like I can remove it, sure.

Regards,
Sergei

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Hi Sergei,

On Tue, Feb 01, 2011 at 09:36:45PM -0000, Sergei wrote:
> On Feb 01, Jelmer Vernooij wrote:
> > Thanks for working on this! :)
> >
> > I don't understand why bugtrackerex.py is necessary. If the existing
> > code in bzrlib.bugtracker isn't sufficient, please extend it rather
> > than duplicating it here.
> I wanted to stay within bzr-gtk.
>
> How to do that otherwise - you want me to submit a merge request to bzr
> tree with this change (extending bzrlib.bugtracker), and when it's
> accepted do another one for bzr-gtk ?
Yeah, that'd be great. That way others can also take advantage of your
improvements.

> That would be the right way, I agree. Although I afraid that bzr, as a
> bigger project with more developers will be slower to accept a patch.
>
> But I could try, by all means.
Bazaar has higher quality standards for new code, in particular requiring unit
tests for new code. On the other hand, Bazaar has a dedicated patch pilot
who helps community members land finish and land patches. If there's anything I can
help with, please ping me on email or IRC (#bzr).

> > Is there any particular reason why you've added a configuration
> > option, rather than always allowing users to specify the bug # ? I
> > think it'd be reasonable to enable this functionality unconditionally.
> I copied this from per-file comments. It also has a configuration
> option.
per file commit comments is a feature that's really just a hack in bzr-gtk at
the moment, unlike bugs which is supported by bzr core. IIUC it was mainly
added because a particular project needed it, and so they have the chance to
enable it if they want to.

> I got an idea (from the comments in the code) that it was done
> to have more screen space dedicated to diff and changeset comment and
> not waste it on per-file comments, when user does not want them.
>
> So, I figured out that it applies as well to "bugs" input field.
>
> If you'd like I can remove it, sure.
Please do, I think this is useful enough to be enabled by default rather
than just for those people who happen to know about the magic option.

Cheers,

Jelmer

Revision history for this message
Sergei Golubchik (sergii) wrote :

Hi, Jelmer!

On Feb 01, Jelmer Vernooij wrote:
> > >
> > > I don't understand why bugtrackerex.py is necessary. If the
> > > existing code in bzrlib.bugtracker isn't sufficient, please extend
> > > it rather than duplicating it here.

You see, the functionality I added to bugtrackerex is not needed for bzr
core.

Let me explain how bugs work in bzr.

bzr commit --fixes takes a list of bug ids in the form of
[tracker]:[id]. Then it converts them into urls, e.g. lp:123 is
converted into https://bugs.launchpad.net/bugs/123.
It generates a string, something like this
'\n'.join([url + ' fixes' for url in bug_url_list])
which then gets stored with the changeset.

All tools that show bugs, say, bzr log or bzr uncommit, get this string,
split on '\n', take the first word, ignore the second (yes!), and show
urls.

It certainly looks like whoever did that planned to have more complex
feature, where instead of "fixes" there could be different words
describing the relation of the bug number to the changeset. For example,
"fixes", "tests", etc. But it was not implemented.

Anyway, my point is - for 'bzr uncommit' to work in bzr-gtk, it needs to
save bug ids in the lp:123 form (to be able to show them in
gcommit). And to do that, it needs to *reverse engineer* bug urls back
into [tracker]:[id]. That's what bugtrackerex.py is doing.

But bzr core does not need that at all. It never needs to convert bug
urls back into bug ids.

You still want me to submit a merge request to bzr core?

Regards,
Sergei

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Setting this back to work-in-progress for the moment so it doesn't show up in the review queue. Please set back to needs-review when it uses the new bugtracker infrastructure in bzrlib.

Unmerged revisions

708. By Sergei Golubchik

Added support for bugs (as in "bzr commit --fixes") to gcommit.
This includes reverse-engineering of bug urls by extending the core
bzr classes. Because bzr does that to bugs :(

Only lp: gnome: and debian: urls are supported by this changeset.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2011-01-16 18:55:13 +0000
+++ NEWS 2011-02-01 17:50:14 +0000
@@ -1,5 +1,9 @@
10.100.0 UNRELEASED10.100.0 UNRELEASED
22
3 FEATURES
4
5 * Added support for bugs (as in "bzr commit --fixes") to gcommit.
6
3 BUG FIXES7 BUG FIXES
48
5 * Clarify 'visualise' help about the multiple branches feature9 * Clarify 'visualise' help about the multiple branches feature
610
=== added file 'bugtrackerex.py'
--- bugtrackerex.py 1970-01-01 00:00:00 +0000
+++ bugtrackerex.py 2011-02-01 17:50:14 +0000
@@ -0,0 +1,47 @@
1from bzrlib.bugtracker import *
2from bzrlib import errors
3
4def _iter_bug_fix_urls(fixes, branch):
5 # Configure the properties for bug fixing attributes.
6 for fixed_bug in fixes:
7 tokens = fixed_bug.split(':')
8 if len(tokens) != 2:
9 raise errors.BzrCommandError(
10 "Invalid bug %s. Must be in the form of 'tracker:id'. "
11 "See \"bzr help bugs\" for more information on this "
12 "feature.\nCommit refused." % fixed_bug)
13 tag, bug_id = tokens
14 try:
15 yield get_bug_url(tag, branch, bug_id)
16 except errors.UnknownBugTrackerAbbreviation:
17 raise errors.BzrCommandError(
18 'Unrecognized bug %s. Commit refused.' % fixed_bug)
19 except errors.MalformedBugIdentifier, e:
20 raise errors.BzrCommandError(
21 "%s\nCommit refused." % (str(e),))
22
23def encode_fixes_bug_ids(fixes, branch):
24 return encode_fixes_bug_urls(_iter_bug_fix_urls(fixes, branch))
25
26def get_bugtracker_and_bug_id(url, branch):
27 """this is the reverse of get_bug_url - return an
28 abbreviated_bugtracker_name and bug_id when given a full bug url
29 """
30 for tracker_name in tracker_registry.keys():
31 tracker = tracker_registry.get(tracker_name)
32 if hasattr(tracker, 'reverse_engineer'):
33 abbreviation, bug_id = tracker.reverse_engineer(url, branch)
34 if bug_id is not None:
35 return abbreviation, bug_id
36 # raising an error here corrupts the tree in a way that
37 # is not obvious to recover from. As it's only a value to help
38 # the user to fill in the bug number on the next commit, don't
39 # abort if it cannot be reversed.
40 return 'UNRECOGNIZED', url
41
42def _UniqueIntegerBugTracker_reverse_engineer(self, url, branch):
43 if url.startswith(self.base_url):
44 return self.abbreviation, url[len(self.base_url):]
45 return None, None
46
47UniqueIntegerBugTracker.reverse_engineer = _UniqueIntegerBugTracker_reverse_engineer
048
=== modified file 'commit.py'
--- commit.py 2010-07-21 20:21:17 +0000
+++ commit.py 2011-02-01 17:50:14 +0000
@@ -125,6 +125,7 @@
125 # It used to set all changes but this one to False125 # It used to set all changes but this one to False
126 self._selected = selected126 self._selected = selected
127 self._enable_per_file_commits = True127 self._enable_per_file_commits = True
128 self._enable_bugs_fixed = True
128 self._commit_all_changes = True129 self._commit_all_changes = True
129 self.committed_revision_id = None # Nothing has been committed yet130 self.committed_revision_id = None # Nothing has been committed yet
130 self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)131 self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
@@ -148,6 +149,7 @@
148 self._fill_in_files()149 self._fill_in_files()
149 self._fill_in_checkout()150 self._fill_in_checkout()
150 self._fill_in_per_file_info()151 self._fill_in_per_file_info()
152 self._fill_in_bug_info()
151153
152 def _fill_in_pending(self):154 def _fill_in_pending(self):
153 if not self._pending:155 if not self._pending:
@@ -279,6 +281,18 @@
279 self._file_message_expander.hide()281 self._file_message_expander.hide()
280 self._global_message_label.set_markup(_i18n('<b>Commit Message</b>'))282 self._global_message_label.set_markup(_i18n('<b>Commit Message</b>'))
281283
284 def _fill_in_bug_info(self):
285 config = self._wt.branch.get_config()
286 enable_bugs_fixed = config.get_user_option('bugs_fixed')
287 if (enable_bugs_fixed is None
288 or enable_bugs_fixed.lower()
289 not in ('y', 'yes', 'on', 'enable', '1', 't', 'true')):
290 self._enable_bugs_fixed = False
291 else:
292 self._enable_bugs_fixed = True
293 if not self._enable_bugs_fixed:
294 self._bugs_fixed_expander.hide()
295
282 def _compute_delta(self):296 def _compute_delta(self):
283 self._delta = self._wt.changes_from(self._basis_tree)297 self._delta = self._wt.changes_from(self._basis_tree)
284298
@@ -350,6 +364,7 @@
350 self._right_pane_table.set_col_spacings(5)364 self._right_pane_table.set_col_spacings(5)
351 self._right_pane_table_row = 0365 self._right_pane_table_row = 0
352 self._construct_diff_view()366 self._construct_diff_view()
367 self._construct_bugs_fixed()
353 self._construct_file_message()368 self._construct_file_message()
354 self._construct_global_message()369 self._construct_global_message()
355370
@@ -528,6 +543,27 @@
528 self._add_to_right_table(self._diff_view, 4, True)543 self._add_to_right_table(self._diff_view, 4, True)
529 self._diff_view.show()544 self._diff_view.show()
530545
546 def _construct_bugs_fixed(self):
547 scroller = gtk.ScrolledWindow()
548 scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
549
550 self._bugs_fixed_text_view = gtk.TextView()
551 scroller.add(self._bugs_fixed_text_view)
552 scroller.set_shadow_type(gtk.SHADOW_IN)
553 scroller.show()
554
555 self._bugs_fixed_text_view.modify_font(pango.FontDescription("Monospace"))
556 self._bugs_fixed_text_view.set_wrap_mode(gtk.WRAP_WORD)
557 self._bugs_fixed_text_view.set_accepts_tab(False)
558 self._bugs_fixed_text_view.show()
559
560 self._bugs_fixed_expander = gtk.Expander(_i18n('Bugs fixed'))
561 self._bugs_fixed_expander.set_expanded(False)
562 self._bugs_fixed_expander.add(scroller)
563 self._add_to_right_table(self._bugs_fixed_expander, 1, False)
564 self._set_bugs_fixed_list(self._saved_commit_messages_manager.get()[2])
565 self._bugs_fixed_expander.show()
566
531 def _construct_file_message(self):567 def _construct_file_message(self):
532 scroller = gtk.ScrolledWindow()568 scroller = gtk.ScrolledWindow()
533 scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)569 scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
@@ -683,7 +719,8 @@
683 mgr = SavedCommitMessagesManager()719 mgr = SavedCommitMessagesManager()
684 self._saved_commit_messages_manager = mgr720 self._saved_commit_messages_manager = mgr
685 mgr.insert(self._get_global_commit_message(),721 mgr.insert(self._get_global_commit_message(),
686 self._get_specific_files()[1])722 self._get_specific_files()[1],
723 self._get_bugs_fixed_list())
687 if mgr.is_not_empty(): # maybe worth saving724 if mgr.is_not_empty(): # maybe worth saving
688 response = self._question_dialog(725 response = self._question_dialog(
689 _i18n('Commit cancelled'),726 _i18n('Commit cancelled'),
@@ -702,6 +739,7 @@
702739
703 def _do_commit(self):740 def _do_commit(self):
704 message = self._get_global_commit_message()741 message = self._get_global_commit_message()
742 bugs_fixed = self._get_bugs_fixed_list()
705743
706 if message == '':744 if message == '':
707 response = self._question_dialog(745 response = self._question_dialog(
@@ -738,6 +776,12 @@
738 revprops = {}776 revprops = {}
739 if file_info:777 if file_info:
740 revprops['file-info'] = bencode.bencode(file_info).decode('UTF-8')778 revprops['file-info'] = bencode.bencode(file_info).decode('UTF-8')
779 if bugs_fixed:
780 from bzrlib.plugins.gtk import bugtrackerex
781 fixes = bugs_fixed.split()
782 bug_property = bugtrackerex.encode_fixes_bug_ids(fixes, self._wt.branch)
783 if bug_property:
784 revprops['bugs'] = bug_property
741 try:785 try:
742 rev_id = self._wt.commit(message,786 rev_id = self._wt.commit(message,
743 allow_pointless=False,787 allow_pointless=False,
@@ -769,12 +813,26 @@
769 text = buf.get_text(start, end)813 text = buf.get_text(start, end)
770 return _sanitize_and_decode_message(text)814 return _sanitize_and_decode_message(text)
771815
816 def _get_bugs_fixed_list(self):
817 buf = self._bugs_fixed_text_view.get_buffer()
818 start, end = buf.get_bounds()
819 text = buf.get_text(start, end)
820 return _sanitize_and_decode_message(text)
821
772 def _set_global_commit_message(self, message):822 def _set_global_commit_message(self, message):
773 """Just a helper for the test suite."""823 """Just a helper for the test suite."""
774 if isinstance(message, unicode):824 if isinstance(message, unicode):
775 message = message.encode('UTF-8')825 message = message.encode('UTF-8')
776 self._global_message_text_view.get_buffer().set_text(message)826 self._global_message_text_view.get_buffer().set_text(message)
777827
828 def _set_bugs_fixed_list(self, message):
829 """Just a helper for the test suite."""
830 if isinstance(message, unicode):
831 message = message.encode('UTF-8')
832 if len(message) > 0:
833 self._bugs_fixed_text_view.get_buffer().set_text(message)
834 self._bugs_fixed_expander.set_expanded(True)
835
778 def _set_file_commit_message(self, message):836 def _set_file_commit_message(self, message):
779 """Helper for the test suite."""837 """Helper for the test suite."""
780 if isinstance(message, unicode):838 if isinstance(message, unicode):
@@ -809,6 +867,7 @@
809 if branch is None:867 if branch is None:
810 self.global_message = u''868 self.global_message = u''
811 self.file_messages = {}869 self.file_messages = {}
870 self.bugs_fixed = u''
812 else:871 else:
813 config = branch.get_config()872 config = branch.get_config()
814 self.global_message = config.get_user_option(873 self.global_message = config.get_user_option(
@@ -821,14 +880,17 @@
821 file_messages.encode('UTF-8'))880 file_messages.encode('UTF-8'))
822 else:881 else:
823 self.file_messages = {}882 self.file_messages = {}
883 self.bugs_fixed = config.get_user_option('gtk_bugs_fixed_list')
884 if self.bugs_fixed is None:
885 self.bugs_fixed = u''
824886
825 def get(self):887 def get(self):
826 return self.global_message, self.file_messages888 return self.global_message, self.file_messages, self.bugs_fixed
827889
828 def is_not_empty(self):890 def is_not_empty(self):
829 return bool(self.global_message or self.file_messages)891 return bool(self.global_message or self.file_messages or self.bugs_fixed)
830892
831 def insert(self, global_message, file_info):893 def insert(self, global_message, file_info, bugs_fixed):
832 """Formats per-file commit messages (list of dictionaries, one per file)894 """Formats per-file commit messages (list of dictionaries, one per file)
833 into one utf-8 file_id->message dictionary and merges this with895 into one utf-8 file_id->message dictionary and merges this with
834 previously existing dictionary. Merges global commit message too."""896 previously existing dictionary. Merges global commit message too."""
@@ -847,6 +909,10 @@
847 + self.global_message909 + self.global_message
848 else:910 else:
849 self.global_message = global_message911 self.global_message = global_message
912 if self.bugs_fixed:
913 self.bugs_fixed = bugs_fixed + ' ' + self.bugs_fixed
914 else:
915 self.bugs_fixed = bugs_fixed
850916
851 def save(self, tree, branch):917 def save(self, tree, branch):
852 # We store in branch's config, which can be a problem if two gcommit918 # We store in branch's config, which can be a problem if two gcommit
@@ -863,6 +929,7 @@
863 config.set_user_option(929 config.set_user_option(
864 'gtk_file_commit_messages',930 'gtk_file_commit_messages',
865 bencode.bencode(self.file_messages).decode('UTF-8'))931 bencode.bencode(self.file_messages).decode('UTF-8'))
932 config.set_user_option('gtk_bugs_fixed_list', self.bugs_fixed)
866933
867934
868def save_commit_messages(local, master, old_revno, old_revid,935def save_commit_messages(local, master, old_revno, old_revid,
@@ -886,8 +953,13 @@
886 else:953 else:
887 file_info = bencode.bdecode(file_info.encode('UTF-8'))954 file_info = bencode.bdecode(file_info.encode('UTF-8'))
888 global_message = osutils.safe_unicode(rev.message)955 global_message = osutils.safe_unicode(rev.message)
956
957 from bzrlib.plugins.gtk import bugtrackerex
958 bugs_fixed = ' '.join('%s:%s' % bugtrackerex.get_bugtracker_and_bug_id(url, b)
959 for url,status in rev.iter_bugs())
960
889 # Concatenate comment of the uncommitted revision961 # Concatenate comment of the uncommitted revision
890 mgr.insert(global_message, file_info)962 mgr.insert(global_message, file_info, bugs_fixed)
891963
892 parents = graph.get_parent_map([rev_id]).get(rev_id, None)964 parents = graph.get_parent_map([rev_id]).get(rev_id, None)
893 if not parents:965 if not parents:

Subscribers

People subscribed via source and target branches

to all changes:
to status/vote changes: