Merge lp:~jelmer/bzr/uncommit-remove-tags into lp:bzr

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merged at revision: 6094
Proposed branch: lp:~jelmer/bzr/uncommit-remove-tags
Merge into: lp:bzr
Diff against target: 231 lines (+89/-13)
7 files modified
bzrlib/builtins.py (+9/-6)
bzrlib/tests/blackbox/test_uncommit.py (+16/-2)
bzrlib/tests/test_uncommit.py (+27/-0)
bzrlib/uncommit.py (+26/-1)
bzrlib/vf_repository.py (+4/-4)
doc/en/release-notes/bzr-2.5.txt (+4/-0)
doc/en/user-guide/undoing_mistakes.txt (+3/-0)
To merge this branch: bzr merge lp:~jelmer/bzr/uncommit-remove-tags
Reviewer Review Type Date Requested Status
Martin Pool Approve
Review via email: mp+72403@code.launchpad.net

Commit message

Remove tags in `bzr uncommit`.

Description of the change

Finishing off some code that I still had around in my working area...

Remove tags when uncommitting revisions. A new option --keep-tags prevents
this behaviour.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

That's good.

[tweak] It's a bit safer to add new keyword parameters at the end.

[tweak] Please mention it in the user guide under uncommit.

review: Approve
Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 8/24/2011 7:30 AM, Martin Pool wrote:
> Review: Approve That's good.
>
> [tweak] It's a bit safer to add new keyword parameters at the end.
>
> [tweak] Please mention it in the user guide under uncommit.

I'm a bit concerned that we do a graph search to find what tags to
remove. I guess we've only been doing a linear search to resolve what
revisions to display?

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk5UyAIACgkQJdeBCYSNAAPBegCfVrVDCtKFCvmBgJ7OEg7WGgWc
k8oAoNeYK5humf4yWFpI2lD+t4oh2GbX
=j8AV
-----END PGP SIGNATURE-----

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

On 08/24/2011 11:46 AM, John A Meinel wrote:
> On 8/24/2011 7:30 AM, Martin Pool wrote:
>> Review: Approve That's good.
>>
>> [tweak] It's a bit safer to add new keyword parameters at the end.
>>
>> [tweak] Please mention it in the user guide under uncommit.
> I'm a bit concerned that we do a graph search to find what tags to
> remove. I guess we've only been doing a linear search to resolve what
> revisions to display?
>
Yep. We could potentially avoid redoing the get_parent_map calls for the
revisions on the mainline, but at the cost of more code complexity.

Cheers,

Jelmer

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 8/24/2011 12:10 PM, Jelmer Vernooij wrote:
> On 08/24/2011 11:46 AM, John A Meinel wrote:
>> On 8/24/2011 7:30 AM, Martin Pool wrote:
>>> Review: Approve That's good.
>>>
>>> [tweak] It's a bit safer to add new keyword parameters at the
>>> end.
>>>
>>> [tweak] Please mention it in the user guide under uncommit.
>> I'm a bit concerned that we do a graph search to find what tags
>> to remove. I guess we've only been doing a linear search to
>> resolve what revisions to display?
>>
> Yep. We could potentially avoid redoing the get_parent_map calls
> for the revisions on the mainline, but at the cost of more code
> complexity.
>
> Cheers,
>
> Jelmer
>

Right, if we only know about mainline, it isn't worth worrying about.
As long as things are well cached, etc.

I'd worry a bit about performance, but uncommit should usually be
local enough in scope it should be ok. Though I wish our
find_unique_ancestors code didn't have some really bad behaviors
(landing bzr-2.1 into bzr-2.4 causes a lot of graph searching, though
some of that is just because we are 'skipping over' 2 years of history.)

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk5U1J8ACgkQJdeBCYSNAAP8LwCeJYZt9z82Vc0tH6bYzSy6kStN
eI0An09oAtrCdzasSV4UVpFNZ1GlhogZ
=qfkU
-----END PGP SIGNATURE-----

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/builtins.py'
2--- bzrlib/builtins.py 2011-08-19 22:26:03 +0000
3+++ bzrlib/builtins.py 2011-08-24 09:34:52 +0000
4@@ -4979,6 +4979,8 @@
5 takes_options = ['verbose', 'revision',
6 Option('dry-run', help='Don\'t actually make changes.'),
7 Option('force', help='Say yes to all questions.'),
8+ Option('keep-tags',
9+ help='Keep tags that point to removed revisions.'),
10 Option('local',
11 help="Only remove the commits from the local branch"
12 " when in a checkout."
13@@ -4988,9 +4990,8 @@
14 aliases = []
15 encoding_type = 'replace'
16
17- def run(self, location=None,
18- dry_run=False, verbose=False,
19- revision=None, force=False, local=False):
20+ def run(self, location=None, dry_run=False, verbose=False,
21+ revision=None, force=False, local=False, keep_tags=False):
22 if location is None:
23 location = u'.'
24 control, relpath = bzrdir.BzrDir.open_containing(location)
25@@ -5005,9 +5006,11 @@
26 self.add_cleanup(tree.lock_write().unlock)
27 else:
28 self.add_cleanup(b.lock_write().unlock)
29- return self._run(b, tree, dry_run, verbose, revision, force, local=local)
30+ return self._run(b, tree, dry_run, verbose, revision, force,
31+ local, keep_tags)
32
33- def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
34+ def _run(self, b, tree, dry_run, verbose, revision, force, local,
35+ keep_tags):
36 from bzrlib.log import log_formatter, show_log
37 from bzrlib.uncommit import uncommit
38
39@@ -5059,7 +5062,7 @@
40 mutter('Uncommitting from {%s} to {%s}',
41 last_rev_id, rev_id)
42 uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
43- revno=revno, local=local)
44+ revno=revno, local=local, keep_tags=keep_tags)
45 self.outf.write('You can restore the old tip by running:\n'
46 ' bzr pull . -r revid:%s\n' % last_rev_id)
47
48
49=== modified file 'bzrlib/tests/blackbox/test_uncommit.py'
50--- bzrlib/tests/blackbox/test_uncommit.py 2010-09-15 09:35:42 +0000
51+++ bzrlib/tests/blackbox/test_uncommit.py 2011-08-24 09:34:52 +0000
52@@ -18,9 +18,9 @@
53
54 import os
55
56-from bzrlib import uncommit, workingtree
57+from bzrlib import uncommit
58 from bzrlib.bzrdir import BzrDirMetaFormat1
59-from bzrlib.errors import BzrError, BoundBranchOutOfDate
60+from bzrlib.errors import BoundBranchOutOfDate
61 from bzrlib.tests import TestCaseWithTransport
62 from bzrlib.tests.script import (
63 run_script,
64@@ -280,3 +280,17 @@
65 tree.commit(u'\u1234 message')
66 out, err = self.run_bzr('uncommit --force tree', encoding='ascii')
67 self.assertContainsRe(out, r'\? message')
68+
69+ def test_uncommit_removes_tags(self):
70+ tree = self.make_branch_and_tree('tree')
71+ revid = tree.commit('message')
72+ tree.branch.tags.set_tag("atag", revid)
73+ out, err = self.run_bzr('uncommit --force tree')
74+ self.assertEquals({}, tree.branch.tags.get_tag_dict())
75+
76+ def test_uncommit_keep_tags(self):
77+ tree = self.make_branch_and_tree('tree')
78+ revid = tree.commit('message')
79+ tree.branch.tags.set_tag("atag", revid)
80+ out, err = self.run_bzr('uncommit --keep-tags --force tree')
81+ self.assertEquals({"atag": revid}, tree.branch.tags.get_tag_dict())
82
83=== modified file 'bzrlib/tests/test_uncommit.py'
84--- bzrlib/tests/test_uncommit.py 2011-05-13 12:51:05 +0000
85+++ bzrlib/tests/test_uncommit.py 2011-08-24 09:34:52 +0000
86@@ -96,3 +96,30 @@
87 # If this tree isn't bound, local=True raises an exception
88 self.assertRaises(errors.LocalRequiresBoundBranch,
89 uncommit.uncommit, tree.branch, tree=tree, local=True)
90+
91+ def test_uncommit_remove_tags(self):
92+ tree, history = self.make_linear_tree()
93+ self.assertEqual(history[1], tree.last_revision())
94+ self.assertEqual((2, history[1]), tree.branch.last_revision_info())
95+ tree.branch.tags.set_tag(u"pointsatexisting", history[0])
96+ tree.branch.tags.set_tag(u"pointsatremoved", history[1])
97+ uncommit.uncommit(tree.branch, tree=tree)
98+ self.assertEqual(history[0], tree.last_revision())
99+ self.assertEqual((1, history[0]), tree.branch.last_revision_info())
100+ self.assertEqual({
101+ "pointsatexisting": history[0]
102+ }, tree.branch.tags.get_tag_dict())
103+
104+ def test_uncommit_keep_tags(self):
105+ tree, history = self.make_linear_tree()
106+ self.assertEqual(history[1], tree.last_revision())
107+ self.assertEqual((2, history[1]), tree.branch.last_revision_info())
108+ tree.branch.tags.set_tag(u"pointsatexisting", history[0])
109+ tree.branch.tags.set_tag(u"pointsatremoved", history[1])
110+ uncommit.uncommit(tree.branch, tree=tree, keep_tags=True)
111+ self.assertEqual(history[0], tree.last_revision())
112+ self.assertEqual((1, history[0]), tree.branch.last_revision_info())
113+ self.assertEqual({
114+ "pointsatexisting": history[0],
115+ "pointsatremoved": history[1],
116+ }, tree.branch.tags.get_tag_dict())
117
118=== modified file 'bzrlib/uncommit.py'
119--- bzrlib/uncommit.py 2011-06-16 12:50:32 +0000
120+++ bzrlib/uncommit.py 2011-08-24 09:34:52 +0000
121@@ -26,8 +26,29 @@
122 from bzrlib.errors import BoundBranchOutOfDate
123
124
125+def remove_tags(branch, graph, old_tip, new_tip):
126+ """Remove tags on revisions between old_tip and new_tip.
127+
128+ :param branch: Branch to remove tags from
129+ :param graph: Graph object for branch repository
130+ :param old_tip: Old branch tip
131+ :param new_tip: New branch tip
132+ :return: Names of the removed tags
133+ """
134+ reverse_tags = branch.tags.get_reverse_tag_dict()
135+ ancestors = graph.find_unique_ancestors(old_tip, [new_tip])
136+ removed_tags = []
137+ for revid, tags in reverse_tags.iteritems():
138+ if not revid in ancestors:
139+ continue
140+ for tag in tags:
141+ branch.tags.delete_tag(tag)
142+ removed_tags.append(tag)
143+ return removed_tags
144+
145+
146 def uncommit(branch, dry_run=False, verbose=False, revno=None, tree=None,
147- local=False):
148+ local=False, keep_tags=False):
149 """Remove the last revision from the supplied branch.
150
151 :param dry_run: Don't actually change anything
152@@ -36,6 +57,8 @@
153 :param local: If this branch is bound, only remove the revisions from the
154 local branch. If this branch is not bound, it is an error to pass
155 local=True.
156+ :param keep_tags: Whether to keep tags pointing at the removed revisions
157+ around.
158 """
159 unlockable = []
160 try:
161@@ -105,6 +128,8 @@
162 hook_new_tip = None
163 hook(hook_local, hook_master, old_revno, old_tip, new_revno,
164 hook_new_tip)
165+ if branch.supports_tags() and not keep_tags:
166+ remove_tags(branch, graph, old_tip, new_revision_id)
167 if tree is not None:
168 if not _mod_revision.is_null(new_revision_id):
169 parents = [new_revision_id]
170
171=== modified file 'bzrlib/vf_repository.py'
172--- bzrlib/vf_repository.py 2011-08-02 11:18:43 +0000
173+++ bzrlib/vf_repository.py 2011-08-24 09:34:52 +0000
174@@ -419,8 +419,8 @@
175 return None, False, None
176 # XXX: Friction: parent_candidates should return a list not a dict
177 # so that we don't have to walk the inventories again.
178- parent_candiate_entries = ie.parent_candidates(parent_invs)
179- head_set = self._heads(ie.file_id, parent_candiate_entries.keys())
180+ parent_candidate_entries = ie.parent_candidates(parent_invs)
181+ head_set = self._heads(ie.file_id, parent_candidate_entries.keys())
182 heads = []
183 for inv in parent_invs:
184 if inv.has_id(ie.file_id):
185@@ -441,7 +441,7 @@
186 store = True
187 if not store:
188 # There is a single head, look it up for comparison
189- parent_entry = parent_candiate_entries[heads[0]]
190+ parent_entry = parent_candidate_entries[heads[0]]
191 # if the non-content specific data has changed, we'll be writing a
192 # node:
193 if (parent_entry.parent_id != ie.parent_id or
194@@ -559,7 +559,7 @@
195 :param iter_changes: An iter_changes iterator with the changes to apply
196 to basis_revision_id. The iterator must not include any items with
197 a current kind of None - missing items must be either filtered out
198- or errored-on beefore record_iter_changes sees the item.
199+ or errored-on before record_iter_changes sees the item.
200 :param _entry_factory: Private method to bind entry_factory locally for
201 performance.
202 :return: A generator of (file_id, relpath, fs_hash) tuples for use with
203
204=== modified file 'doc/en/release-notes/bzr-2.5.txt'
205--- doc/en/release-notes/bzr-2.5.txt 2011-08-23 10:27:54 +0000
206+++ doc/en/release-notes/bzr-2.5.txt 2011-08-24 09:34:52 +0000
207@@ -100,6 +100,10 @@
208 Entering an empty commit message in the message editor still triggers
209 an error. (Jelmer Vernooij)
210
211+* ``bzr uncommit`` will now remove tags that refer to removed revisions.
212+ The ``--keep-tags`` option can be used to prevent this behaviour.
213+ (Jelmer Vernooij, #605814)
214+
215 * Locations printed by ``bzr upgrade`` are now formatted before display.
216 (Jelmer Vernooij)
217
218
219=== modified file 'doc/en/user-guide/undoing_mistakes.txt'
220--- doc/en/user-guide/undoing_mistakes.txt 2009-11-23 15:29:24 +0000
221+++ doc/en/user-guide/undoing_mistakes.txt 2011-08-24 09:34:52 +0000
222@@ -93,6 +93,9 @@
223 one or more files. Some users like to alias ``commit`` to ``commit --strict``
224 so that commits fail if unknown files are found in the tree.
225
226+Tags for uncommitted revisions are removed from the branch unless
227+``--keep-tags`` was specified.
228+
229 Note: While the ``merge`` command is not introduced until the next
230 chapter, it is worth noting now that ``uncommit`` restores any pending
231 merges. (Running ``bzr status`` after ``uncommit`` will show these.)