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
1=== modified file 'NEWS'
2--- NEWS 2011-01-16 18:55:13 +0000
3+++ NEWS 2011-02-01 17:50:14 +0000
4@@ -1,5 +1,9 @@
5 0.100.0 UNRELEASED
6
7+ FEATURES
8+
9+ * Added support for bugs (as in "bzr commit --fixes") to gcommit.
10+
11 BUG FIXES
12
13 * Clarify 'visualise' help about the multiple branches feature
14
15=== added file 'bugtrackerex.py'
16--- bugtrackerex.py 1970-01-01 00:00:00 +0000
17+++ bugtrackerex.py 2011-02-01 17:50:14 +0000
18@@ -0,0 +1,47 @@
19+from bzrlib.bugtracker import *
20+from bzrlib import errors
21+
22+def _iter_bug_fix_urls(fixes, branch):
23+ # Configure the properties for bug fixing attributes.
24+ for fixed_bug in fixes:
25+ tokens = fixed_bug.split(':')
26+ if len(tokens) != 2:
27+ raise errors.BzrCommandError(
28+ "Invalid bug %s. Must be in the form of 'tracker:id'. "
29+ "See \"bzr help bugs\" for more information on this "
30+ "feature.\nCommit refused." % fixed_bug)
31+ tag, bug_id = tokens
32+ try:
33+ yield get_bug_url(tag, branch, bug_id)
34+ except errors.UnknownBugTrackerAbbreviation:
35+ raise errors.BzrCommandError(
36+ 'Unrecognized bug %s. Commit refused.' % fixed_bug)
37+ except errors.MalformedBugIdentifier, e:
38+ raise errors.BzrCommandError(
39+ "%s\nCommit refused." % (str(e),))
40+
41+def encode_fixes_bug_ids(fixes, branch):
42+ return encode_fixes_bug_urls(_iter_bug_fix_urls(fixes, branch))
43+
44+def get_bugtracker_and_bug_id(url, branch):
45+ """this is the reverse of get_bug_url - return an
46+ abbreviated_bugtracker_name and bug_id when given a full bug url
47+ """
48+ for tracker_name in tracker_registry.keys():
49+ tracker = tracker_registry.get(tracker_name)
50+ if hasattr(tracker, 'reverse_engineer'):
51+ abbreviation, bug_id = tracker.reverse_engineer(url, branch)
52+ if bug_id is not None:
53+ return abbreviation, bug_id
54+ # raising an error here corrupts the tree in a way that
55+ # is not obvious to recover from. As it's only a value to help
56+ # the user to fill in the bug number on the next commit, don't
57+ # abort if it cannot be reversed.
58+ return 'UNRECOGNIZED', url
59+
60+def _UniqueIntegerBugTracker_reverse_engineer(self, url, branch):
61+ if url.startswith(self.base_url):
62+ return self.abbreviation, url[len(self.base_url):]
63+ return None, None
64+
65+UniqueIntegerBugTracker.reverse_engineer = _UniqueIntegerBugTracker_reverse_engineer
66
67=== modified file 'commit.py'
68--- commit.py 2010-07-21 20:21:17 +0000
69+++ commit.py 2011-02-01 17:50:14 +0000
70@@ -125,6 +125,7 @@
71 # It used to set all changes but this one to False
72 self._selected = selected
73 self._enable_per_file_commits = True
74+ self._enable_bugs_fixed = True
75 self._commit_all_changes = True
76 self.committed_revision_id = None # Nothing has been committed yet
77 self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
78@@ -148,6 +149,7 @@
79 self._fill_in_files()
80 self._fill_in_checkout()
81 self._fill_in_per_file_info()
82+ self._fill_in_bug_info()
83
84 def _fill_in_pending(self):
85 if not self._pending:
86@@ -279,6 +281,18 @@
87 self._file_message_expander.hide()
88 self._global_message_label.set_markup(_i18n('<b>Commit Message</b>'))
89
90+ def _fill_in_bug_info(self):
91+ config = self._wt.branch.get_config()
92+ enable_bugs_fixed = config.get_user_option('bugs_fixed')
93+ if (enable_bugs_fixed is None
94+ or enable_bugs_fixed.lower()
95+ not in ('y', 'yes', 'on', 'enable', '1', 't', 'true')):
96+ self._enable_bugs_fixed = False
97+ else:
98+ self._enable_bugs_fixed = True
99+ if not self._enable_bugs_fixed:
100+ self._bugs_fixed_expander.hide()
101+
102 def _compute_delta(self):
103 self._delta = self._wt.changes_from(self._basis_tree)
104
105@@ -350,6 +364,7 @@
106 self._right_pane_table.set_col_spacings(5)
107 self._right_pane_table_row = 0
108 self._construct_diff_view()
109+ self._construct_bugs_fixed()
110 self._construct_file_message()
111 self._construct_global_message()
112
113@@ -528,6 +543,27 @@
114 self._add_to_right_table(self._diff_view, 4, True)
115 self._diff_view.show()
116
117+ def _construct_bugs_fixed(self):
118+ scroller = gtk.ScrolledWindow()
119+ scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
120+
121+ self._bugs_fixed_text_view = gtk.TextView()
122+ scroller.add(self._bugs_fixed_text_view)
123+ scroller.set_shadow_type(gtk.SHADOW_IN)
124+ scroller.show()
125+
126+ self._bugs_fixed_text_view.modify_font(pango.FontDescription("Monospace"))
127+ self._bugs_fixed_text_view.set_wrap_mode(gtk.WRAP_WORD)
128+ self._bugs_fixed_text_view.set_accepts_tab(False)
129+ self._bugs_fixed_text_view.show()
130+
131+ self._bugs_fixed_expander = gtk.Expander(_i18n('Bugs fixed'))
132+ self._bugs_fixed_expander.set_expanded(False)
133+ self._bugs_fixed_expander.add(scroller)
134+ self._add_to_right_table(self._bugs_fixed_expander, 1, False)
135+ self._set_bugs_fixed_list(self._saved_commit_messages_manager.get()[2])
136+ self._bugs_fixed_expander.show()
137+
138 def _construct_file_message(self):
139 scroller = gtk.ScrolledWindow()
140 scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
141@@ -683,7 +719,8 @@
142 mgr = SavedCommitMessagesManager()
143 self._saved_commit_messages_manager = mgr
144 mgr.insert(self._get_global_commit_message(),
145- self._get_specific_files()[1])
146+ self._get_specific_files()[1],
147+ self._get_bugs_fixed_list())
148 if mgr.is_not_empty(): # maybe worth saving
149 response = self._question_dialog(
150 _i18n('Commit cancelled'),
151@@ -702,6 +739,7 @@
152
153 def _do_commit(self):
154 message = self._get_global_commit_message()
155+ bugs_fixed = self._get_bugs_fixed_list()
156
157 if message == '':
158 response = self._question_dialog(
159@@ -738,6 +776,12 @@
160 revprops = {}
161 if file_info:
162 revprops['file-info'] = bencode.bencode(file_info).decode('UTF-8')
163+ if bugs_fixed:
164+ from bzrlib.plugins.gtk import bugtrackerex
165+ fixes = bugs_fixed.split()
166+ bug_property = bugtrackerex.encode_fixes_bug_ids(fixes, self._wt.branch)
167+ if bug_property:
168+ revprops['bugs'] = bug_property
169 try:
170 rev_id = self._wt.commit(message,
171 allow_pointless=False,
172@@ -769,12 +813,26 @@
173 text = buf.get_text(start, end)
174 return _sanitize_and_decode_message(text)
175
176+ def _get_bugs_fixed_list(self):
177+ buf = self._bugs_fixed_text_view.get_buffer()
178+ start, end = buf.get_bounds()
179+ text = buf.get_text(start, end)
180+ return _sanitize_and_decode_message(text)
181+
182 def _set_global_commit_message(self, message):
183 """Just a helper for the test suite."""
184 if isinstance(message, unicode):
185 message = message.encode('UTF-8')
186 self._global_message_text_view.get_buffer().set_text(message)
187
188+ def _set_bugs_fixed_list(self, message):
189+ """Just a helper for the test suite."""
190+ if isinstance(message, unicode):
191+ message = message.encode('UTF-8')
192+ if len(message) > 0:
193+ self._bugs_fixed_text_view.get_buffer().set_text(message)
194+ self._bugs_fixed_expander.set_expanded(True)
195+
196 def _set_file_commit_message(self, message):
197 """Helper for the test suite."""
198 if isinstance(message, unicode):
199@@ -809,6 +867,7 @@
200 if branch is None:
201 self.global_message = u''
202 self.file_messages = {}
203+ self.bugs_fixed = u''
204 else:
205 config = branch.get_config()
206 self.global_message = config.get_user_option(
207@@ -821,14 +880,17 @@
208 file_messages.encode('UTF-8'))
209 else:
210 self.file_messages = {}
211+ self.bugs_fixed = config.get_user_option('gtk_bugs_fixed_list')
212+ if self.bugs_fixed is None:
213+ self.bugs_fixed = u''
214
215 def get(self):
216- return self.global_message, self.file_messages
217+ return self.global_message, self.file_messages, self.bugs_fixed
218
219 def is_not_empty(self):
220- return bool(self.global_message or self.file_messages)
221+ return bool(self.global_message or self.file_messages or self.bugs_fixed)
222
223- def insert(self, global_message, file_info):
224+ def insert(self, global_message, file_info, bugs_fixed):
225 """Formats per-file commit messages (list of dictionaries, one per file)
226 into one utf-8 file_id->message dictionary and merges this with
227 previously existing dictionary. Merges global commit message too."""
228@@ -847,6 +909,10 @@
229 + self.global_message
230 else:
231 self.global_message = global_message
232+ if self.bugs_fixed:
233+ self.bugs_fixed = bugs_fixed + ' ' + self.bugs_fixed
234+ else:
235+ self.bugs_fixed = bugs_fixed
236
237 def save(self, tree, branch):
238 # We store in branch's config, which can be a problem if two gcommit
239@@ -863,6 +929,7 @@
240 config.set_user_option(
241 'gtk_file_commit_messages',
242 bencode.bencode(self.file_messages).decode('UTF-8'))
243+ config.set_user_option('gtk_bugs_fixed_list', self.bugs_fixed)
244
245
246 def save_commit_messages(local, master, old_revno, old_revid,
247@@ -886,8 +953,13 @@
248 else:
249 file_info = bencode.bdecode(file_info.encode('UTF-8'))
250 global_message = osutils.safe_unicode(rev.message)
251+
252+ from bzrlib.plugins.gtk import bugtrackerex
253+ bugs_fixed = ' '.join('%s:%s' % bugtrackerex.get_bugtracker_and_bug_id(url, b)
254+ for url,status in rev.iter_bugs())
255+
256 # Concatenate comment of the uncommitted revision
257- mgr.insert(global_message, file_info)
258+ mgr.insert(global_message, file_info, bugs_fixed)
259
260 parents = graph.get_parent_map([rev_id]).get(rev_id, None)
261 if not parents:

Subscribers

People subscribed via source and target branches

to all changes:
to status/vote changes: