Merge lp:~jelmer/bzr/uncommit-remove-tags into lp:bzr
- uncommit-remove-tags
- Merge into bzr.dev
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 | ||||
Related bugs: |
|
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.
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://
iEYEARECAAYFAk5
k8oAoNeYK5humf4
=j8AV
-----END PGP SIGNATURE-----
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
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_
(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://
iEYEARECAAYFAk5
eI0An09oAtrCdza
=qfkU
-----END PGP SIGNATURE-----
Preview Diff
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 | 4979 | takes_options = ['verbose', 'revision', | 4979 | takes_options = ['verbose', 'revision', |
6 | 4980 | Option('dry-run', help='Don\'t actually make changes.'), | 4980 | Option('dry-run', help='Don\'t actually make changes.'), |
7 | 4981 | Option('force', help='Say yes to all questions.'), | 4981 | Option('force', help='Say yes to all questions.'), |
8 | 4982 | Option('keep-tags', | ||
9 | 4983 | help='Keep tags that point to removed revisions.'), | ||
10 | 4982 | Option('local', | 4984 | Option('local', |
11 | 4983 | help="Only remove the commits from the local branch" | 4985 | help="Only remove the commits from the local branch" |
12 | 4984 | " when in a checkout." | 4986 | " when in a checkout." |
13 | @@ -4988,9 +4990,8 @@ | |||
14 | 4988 | aliases = [] | 4990 | aliases = [] |
15 | 4989 | encoding_type = 'replace' | 4991 | encoding_type = 'replace' |
16 | 4990 | 4992 | ||
20 | 4991 | def run(self, location=None, | 4993 | def run(self, location=None, dry_run=False, verbose=False, |
21 | 4992 | dry_run=False, verbose=False, | 4994 | revision=None, force=False, local=False, keep_tags=False): |
19 | 4993 | revision=None, force=False, local=False): | ||
22 | 4994 | if location is None: | 4995 | if location is None: |
23 | 4995 | location = u'.' | 4996 | location = u'.' |
24 | 4996 | control, relpath = bzrdir.BzrDir.open_containing(location) | 4997 | control, relpath = bzrdir.BzrDir.open_containing(location) |
25 | @@ -5005,9 +5006,11 @@ | |||
26 | 5005 | self.add_cleanup(tree.lock_write().unlock) | 5006 | self.add_cleanup(tree.lock_write().unlock) |
27 | 5006 | else: | 5007 | else: |
28 | 5007 | self.add_cleanup(b.lock_write().unlock) | 5008 | self.add_cleanup(b.lock_write().unlock) |
30 | 5008 | return self._run(b, tree, dry_run, verbose, revision, force, local=local) | 5009 | return self._run(b, tree, dry_run, verbose, revision, force, |
31 | 5010 | local, keep_tags) | ||
32 | 5009 | 5011 | ||
34 | 5010 | def _run(self, b, tree, dry_run, verbose, revision, force, local=False): | 5012 | def _run(self, b, tree, dry_run, verbose, revision, force, local, |
35 | 5013 | keep_tags): | ||
36 | 5011 | from bzrlib.log import log_formatter, show_log | 5014 | from bzrlib.log import log_formatter, show_log |
37 | 5012 | from bzrlib.uncommit import uncommit | 5015 | from bzrlib.uncommit import uncommit |
38 | 5013 | 5016 | ||
39 | @@ -5059,7 +5062,7 @@ | |||
40 | 5059 | mutter('Uncommitting from {%s} to {%s}', | 5062 | mutter('Uncommitting from {%s} to {%s}', |
41 | 5060 | last_rev_id, rev_id) | 5063 | last_rev_id, rev_id) |
42 | 5061 | uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose, | 5064 | uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose, |
44 | 5062 | revno=revno, local=local) | 5065 | revno=revno, local=local, keep_tags=keep_tags) |
45 | 5063 | self.outf.write('You can restore the old tip by running:\n' | 5066 | self.outf.write('You can restore the old tip by running:\n' |
46 | 5064 | ' bzr pull . -r revid:%s\n' % last_rev_id) | 5067 | ' bzr pull . -r revid:%s\n' % last_rev_id) |
47 | 5065 | 5068 | ||
48 | 5066 | 5069 | ||
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 | 18 | 18 | ||
54 | 19 | import os | 19 | import os |
55 | 20 | 20 | ||
57 | 21 | from bzrlib import uncommit, workingtree | 21 | from bzrlib import uncommit |
58 | 22 | from bzrlib.bzrdir import BzrDirMetaFormat1 | 22 | from bzrlib.bzrdir import BzrDirMetaFormat1 |
60 | 23 | from bzrlib.errors import BzrError, BoundBranchOutOfDate | 23 | from bzrlib.errors import BoundBranchOutOfDate |
61 | 24 | from bzrlib.tests import TestCaseWithTransport | 24 | from bzrlib.tests import TestCaseWithTransport |
62 | 25 | from bzrlib.tests.script import ( | 25 | from bzrlib.tests.script import ( |
63 | 26 | run_script, | 26 | run_script, |
64 | @@ -280,3 +280,17 @@ | |||
65 | 280 | tree.commit(u'\u1234 message') | 280 | tree.commit(u'\u1234 message') |
66 | 281 | out, err = self.run_bzr('uncommit --force tree', encoding='ascii') | 281 | out, err = self.run_bzr('uncommit --force tree', encoding='ascii') |
67 | 282 | self.assertContainsRe(out, r'\? message') | 282 | self.assertContainsRe(out, r'\? message') |
68 | 283 | |||
69 | 284 | def test_uncommit_removes_tags(self): | ||
70 | 285 | tree = self.make_branch_and_tree('tree') | ||
71 | 286 | revid = tree.commit('message') | ||
72 | 287 | tree.branch.tags.set_tag("atag", revid) | ||
73 | 288 | out, err = self.run_bzr('uncommit --force tree') | ||
74 | 289 | self.assertEquals({}, tree.branch.tags.get_tag_dict()) | ||
75 | 290 | |||
76 | 291 | def test_uncommit_keep_tags(self): | ||
77 | 292 | tree = self.make_branch_and_tree('tree') | ||
78 | 293 | revid = tree.commit('message') | ||
79 | 294 | tree.branch.tags.set_tag("atag", revid) | ||
80 | 295 | out, err = self.run_bzr('uncommit --keep-tags --force tree') | ||
81 | 296 | self.assertEquals({"atag": revid}, tree.branch.tags.get_tag_dict()) | ||
82 | 283 | 297 | ||
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 | 96 | # If this tree isn't bound, local=True raises an exception | 96 | # If this tree isn't bound, local=True raises an exception |
88 | 97 | self.assertRaises(errors.LocalRequiresBoundBranch, | 97 | self.assertRaises(errors.LocalRequiresBoundBranch, |
89 | 98 | uncommit.uncommit, tree.branch, tree=tree, local=True) | 98 | uncommit.uncommit, tree.branch, tree=tree, local=True) |
90 | 99 | |||
91 | 100 | def test_uncommit_remove_tags(self): | ||
92 | 101 | tree, history = self.make_linear_tree() | ||
93 | 102 | self.assertEqual(history[1], tree.last_revision()) | ||
94 | 103 | self.assertEqual((2, history[1]), tree.branch.last_revision_info()) | ||
95 | 104 | tree.branch.tags.set_tag(u"pointsatexisting", history[0]) | ||
96 | 105 | tree.branch.tags.set_tag(u"pointsatremoved", history[1]) | ||
97 | 106 | uncommit.uncommit(tree.branch, tree=tree) | ||
98 | 107 | self.assertEqual(history[0], tree.last_revision()) | ||
99 | 108 | self.assertEqual((1, history[0]), tree.branch.last_revision_info()) | ||
100 | 109 | self.assertEqual({ | ||
101 | 110 | "pointsatexisting": history[0] | ||
102 | 111 | }, tree.branch.tags.get_tag_dict()) | ||
103 | 112 | |||
104 | 113 | def test_uncommit_keep_tags(self): | ||
105 | 114 | tree, history = self.make_linear_tree() | ||
106 | 115 | self.assertEqual(history[1], tree.last_revision()) | ||
107 | 116 | self.assertEqual((2, history[1]), tree.branch.last_revision_info()) | ||
108 | 117 | tree.branch.tags.set_tag(u"pointsatexisting", history[0]) | ||
109 | 118 | tree.branch.tags.set_tag(u"pointsatremoved", history[1]) | ||
110 | 119 | uncommit.uncommit(tree.branch, tree=tree, keep_tags=True) | ||
111 | 120 | self.assertEqual(history[0], tree.last_revision()) | ||
112 | 121 | self.assertEqual((1, history[0]), tree.branch.last_revision_info()) | ||
113 | 122 | self.assertEqual({ | ||
114 | 123 | "pointsatexisting": history[0], | ||
115 | 124 | "pointsatremoved": history[1], | ||
116 | 125 | }, tree.branch.tags.get_tag_dict()) | ||
117 | 99 | 126 | ||
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 | 26 | from bzrlib.errors import BoundBranchOutOfDate | 26 | from bzrlib.errors import BoundBranchOutOfDate |
123 | 27 | 27 | ||
124 | 28 | 28 | ||
125 | 29 | def remove_tags(branch, graph, old_tip, new_tip): | ||
126 | 30 | """Remove tags on revisions between old_tip and new_tip. | ||
127 | 31 | |||
128 | 32 | :param branch: Branch to remove tags from | ||
129 | 33 | :param graph: Graph object for branch repository | ||
130 | 34 | :param old_tip: Old branch tip | ||
131 | 35 | :param new_tip: New branch tip | ||
132 | 36 | :return: Names of the removed tags | ||
133 | 37 | """ | ||
134 | 38 | reverse_tags = branch.tags.get_reverse_tag_dict() | ||
135 | 39 | ancestors = graph.find_unique_ancestors(old_tip, [new_tip]) | ||
136 | 40 | removed_tags = [] | ||
137 | 41 | for revid, tags in reverse_tags.iteritems(): | ||
138 | 42 | if not revid in ancestors: | ||
139 | 43 | continue | ||
140 | 44 | for tag in tags: | ||
141 | 45 | branch.tags.delete_tag(tag) | ||
142 | 46 | removed_tags.append(tag) | ||
143 | 47 | return removed_tags | ||
144 | 48 | |||
145 | 49 | |||
146 | 29 | def uncommit(branch, dry_run=False, verbose=False, revno=None, tree=None, | 50 | def uncommit(branch, dry_run=False, verbose=False, revno=None, tree=None, |
148 | 30 | local=False): | 51 | local=False, keep_tags=False): |
149 | 31 | """Remove the last revision from the supplied branch. | 52 | """Remove the last revision from the supplied branch. |
150 | 32 | 53 | ||
151 | 33 | :param dry_run: Don't actually change anything | 54 | :param dry_run: Don't actually change anything |
152 | @@ -36,6 +57,8 @@ | |||
153 | 36 | :param local: If this branch is bound, only remove the revisions from the | 57 | :param local: If this branch is bound, only remove the revisions from the |
154 | 37 | local branch. If this branch is not bound, it is an error to pass | 58 | local branch. If this branch is not bound, it is an error to pass |
155 | 38 | local=True. | 59 | local=True. |
156 | 60 | :param keep_tags: Whether to keep tags pointing at the removed revisions | ||
157 | 61 | around. | ||
158 | 39 | """ | 62 | """ |
159 | 40 | unlockable = [] | 63 | unlockable = [] |
160 | 41 | try: | 64 | try: |
161 | @@ -105,6 +128,8 @@ | |||
162 | 105 | hook_new_tip = None | 128 | hook_new_tip = None |
163 | 106 | hook(hook_local, hook_master, old_revno, old_tip, new_revno, | 129 | hook(hook_local, hook_master, old_revno, old_tip, new_revno, |
164 | 107 | hook_new_tip) | 130 | hook_new_tip) |
165 | 131 | if branch.supports_tags() and not keep_tags: | ||
166 | 132 | remove_tags(branch, graph, old_tip, new_revision_id) | ||
167 | 108 | if tree is not None: | 133 | if tree is not None: |
168 | 109 | if not _mod_revision.is_null(new_revision_id): | 134 | if not _mod_revision.is_null(new_revision_id): |
169 | 110 | parents = [new_revision_id] | 135 | parents = [new_revision_id] |
170 | 111 | 136 | ||
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 | 419 | return None, False, None | 419 | return None, False, None |
176 | 420 | # XXX: Friction: parent_candidates should return a list not a dict | 420 | # XXX: Friction: parent_candidates should return a list not a dict |
177 | 421 | # so that we don't have to walk the inventories again. | 421 | # so that we don't have to walk the inventories again. |
180 | 422 | parent_candiate_entries = ie.parent_candidates(parent_invs) | 422 | parent_candidate_entries = ie.parent_candidates(parent_invs) |
181 | 423 | head_set = self._heads(ie.file_id, parent_candiate_entries.keys()) | 423 | head_set = self._heads(ie.file_id, parent_candidate_entries.keys()) |
182 | 424 | heads = [] | 424 | heads = [] |
183 | 425 | for inv in parent_invs: | 425 | for inv in parent_invs: |
184 | 426 | if inv.has_id(ie.file_id): | 426 | if inv.has_id(ie.file_id): |
185 | @@ -441,7 +441,7 @@ | |||
186 | 441 | store = True | 441 | store = True |
187 | 442 | if not store: | 442 | if not store: |
188 | 443 | # There is a single head, look it up for comparison | 443 | # There is a single head, look it up for comparison |
190 | 444 | parent_entry = parent_candiate_entries[heads[0]] | 444 | parent_entry = parent_candidate_entries[heads[0]] |
191 | 445 | # if the non-content specific data has changed, we'll be writing a | 445 | # if the non-content specific data has changed, we'll be writing a |
192 | 446 | # node: | 446 | # node: |
193 | 447 | if (parent_entry.parent_id != ie.parent_id or | 447 | if (parent_entry.parent_id != ie.parent_id or |
194 | @@ -559,7 +559,7 @@ | |||
195 | 559 | :param iter_changes: An iter_changes iterator with the changes to apply | 559 | :param iter_changes: An iter_changes iterator with the changes to apply |
196 | 560 | to basis_revision_id. The iterator must not include any items with | 560 | to basis_revision_id. The iterator must not include any items with |
197 | 561 | a current kind of None - missing items must be either filtered out | 561 | a current kind of None - missing items must be either filtered out |
199 | 562 | or errored-on beefore record_iter_changes sees the item. | 562 | or errored-on before record_iter_changes sees the item. |
200 | 563 | :param _entry_factory: Private method to bind entry_factory locally for | 563 | :param _entry_factory: Private method to bind entry_factory locally for |
201 | 564 | performance. | 564 | performance. |
202 | 565 | :return: A generator of (file_id, relpath, fs_hash) tuples for use with | 565 | :return: A generator of (file_id, relpath, fs_hash) tuples for use with |
203 | 566 | 566 | ||
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 | 100 | Entering an empty commit message in the message editor still triggers | 100 | Entering an empty commit message in the message editor still triggers |
209 | 101 | an error. (Jelmer Vernooij) | 101 | an error. (Jelmer Vernooij) |
210 | 102 | 102 | ||
211 | 103 | * ``bzr uncommit`` will now remove tags that refer to removed revisions. | ||
212 | 104 | The ``--keep-tags`` option can be used to prevent this behaviour. | ||
213 | 105 | (Jelmer Vernooij, #605814) | ||
214 | 106 | |||
215 | 103 | * Locations printed by ``bzr upgrade`` are now formatted before display. | 107 | * Locations printed by ``bzr upgrade`` are now formatted before display. |
216 | 104 | (Jelmer Vernooij) | 108 | (Jelmer Vernooij) |
217 | 105 | 109 | ||
218 | 106 | 110 | ||
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 | 93 | one or more files. Some users like to alias ``commit`` to ``commit --strict`` | 93 | one or more files. Some users like to alias ``commit`` to ``commit --strict`` |
224 | 94 | so that commits fail if unknown files are found in the tree. | 94 | so that commits fail if unknown files are found in the tree. |
225 | 95 | 95 | ||
226 | 96 | Tags for uncommitted revisions are removed from the branch unless | ||
227 | 97 | ``--keep-tags`` was specified. | ||
228 | 98 | |||
229 | 96 | Note: While the ``merge`` command is not introduced until the next | 99 | Note: While the ``merge`` command is not introduced until the next |
230 | 97 | chapter, it is worth noting now that ``uncommit`` restores any pending | 100 | chapter, it is worth noting now that ``uncommit`` restores any pending |
231 | 98 | merges. (Running ``bzr status`` after ``uncommit`` will show these.) | 101 | merges. (Running ``bzr status`` after ``uncommit`` will show these.) |
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.