Status: | Merged |
---|---|
Approved by: | Jelmer Vernooij |
Approved revision: | no longer in the source branch. |
Merge reported by: | The Breezy Bot |
Merged at revision: | not available |
Proposed branch: | lp:~jelmer/brz/trunk-3.1 |
Merge into: | lp:brz |
Diff against target: |
1889 lines (+542/-334) 25 files modified
breezy/branch.py (+42/-26) breezy/bzr/branch.py (+3/-1) breezy/bzr/bzrdir.py (+4/-2) breezy/bzr/knitpack_repo.py (+0/-6) breezy/bzr/remote.py (+2/-2) breezy/bzr/tag.py (+113/-0) breezy/controldir.py (+15/-8) breezy/git/branch.py (+152/-90) breezy/git/dir.py (+5/-4) breezy/git/interrepo.py (+12/-6) breezy/git/workingtree.py (+2/-2) breezy/plugins/propose/github.py (+6/-4) breezy/plugins/propose/gitlabs.py (+3/-3) breezy/plugins/propose/launchpad.py (+15/-11) breezy/plugins/weave_fmt/bzrdir.py (+2/-2) breezy/propose.py (+1/-1) breezy/tag.py (+80/-160) breezy/tests/per_branch/test_pull.py (+1/-1) breezy/tests/per_branch/test_tags.py (+15/-0) breezy/tests/per_controldir/test_push.py (+11/-0) breezy/tests/per_interbranch/test_pull.py (+21/-1) breezy/tests/per_interbranch/test_push.py (+20/-0) breezy/tests/test_foreign.py (+1/-1) breezy/tests/test_tag.py (+14/-1) breezy/workingtree.py (+2/-2) |
To merge this branch: | bzr merge lp:~jelmer/brz/trunk-3.1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+379600@code.launchpad.net |
Commit message
Merge lp:brz/3.1.
Description of the change
Merge lp:brz/3.1.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'breezy/branch.py' | |||
2 | --- breezy/branch.py 2020-02-18 01:57:45 +0000 | |||
3 | +++ breezy/branch.py 2020-02-21 04:09:29 +0000 | |||
4 | @@ -1188,7 +1188,8 @@ | |||
5 | 1188 | if revno < 1 or revno > self.revno(): | 1188 | if revno < 1 or revno > self.revno(): |
6 | 1189 | raise errors.InvalidRevisionNumber(revno) | 1189 | raise errors.InvalidRevisionNumber(revno) |
7 | 1190 | 1190 | ||
9 | 1191 | def clone(self, to_controldir, revision_id=None, repository_policy=None): | 1191 | def clone(self, to_controldir, revision_id=None, repository_policy=None, |
10 | 1192 | tag_selector=None): | ||
11 | 1192 | """Clone this branch into to_controldir preserving all semantic values. | 1193 | """Clone this branch into to_controldir preserving all semantic values. |
12 | 1193 | 1194 | ||
13 | 1194 | Most API users will want 'create_clone_on_transport', which creates a | 1195 | Most API users will want 'create_clone_on_transport', which creates a |
14 | @@ -1201,11 +1202,12 @@ | |||
15 | 1201 | with self.lock_read(), result.lock_write(): | 1202 | with self.lock_read(), result.lock_write(): |
16 | 1202 | if repository_policy is not None: | 1203 | if repository_policy is not None: |
17 | 1203 | repository_policy.configure_branch(result) | 1204 | repository_policy.configure_branch(result) |
19 | 1204 | self.copy_content_into(result, revision_id=revision_id) | 1205 | self.copy_content_into( |
20 | 1206 | result, revision_id=revision_id, tag_selector=tag_selector) | ||
21 | 1205 | return result | 1207 | return result |
22 | 1206 | 1208 | ||
23 | 1207 | def sprout(self, to_controldir, revision_id=None, repository_policy=None, | 1209 | def sprout(self, to_controldir, revision_id=None, repository_policy=None, |
25 | 1208 | repository=None, lossy=False): | 1210 | repository=None, lossy=False, tag_selector=None): |
26 | 1209 | """Create a new line of development from the branch, into to_controldir. | 1211 | """Create a new line of development from the branch, into to_controldir. |
27 | 1210 | 1212 | ||
28 | 1211 | to_controldir controls the branch format. | 1213 | to_controldir controls the branch format. |
29 | @@ -1222,7 +1224,8 @@ | |||
30 | 1222 | with self.lock_read(), result.lock_write(): | 1224 | with self.lock_read(), result.lock_write(): |
31 | 1223 | if repository_policy is not None: | 1225 | if repository_policy is not None: |
32 | 1224 | repository_policy.configure_branch(result) | 1226 | repository_policy.configure_branch(result) |
34 | 1225 | self.copy_content_into(result, revision_id=revision_id) | 1227 | self.copy_content_into( |
35 | 1228 | result, revision_id=revision_id, tag_selector=tag_selector) | ||
36 | 1226 | master_url = self.get_bound_location() | 1229 | master_url = self.get_bound_location() |
37 | 1227 | if master_url is None: | 1230 | if master_url is None: |
38 | 1228 | result.set_parent(self.user_url) | 1231 | result.set_parent(self.user_url) |
39 | @@ -1255,14 +1258,16 @@ | |||
40 | 1255 | revno = 1 | 1258 | revno = 1 |
41 | 1256 | destination.set_last_revision_info(revno, revision_id) | 1259 | destination.set_last_revision_info(revno, revision_id) |
42 | 1257 | 1260 | ||
44 | 1258 | def copy_content_into(self, destination, revision_id=None): | 1261 | def copy_content_into(self, destination, revision_id=None, tag_selector=None): |
45 | 1259 | """Copy the content of self into destination. | 1262 | """Copy the content of self into destination. |
46 | 1260 | 1263 | ||
47 | 1261 | revision_id: if not None, the revision history in the new branch will | 1264 | revision_id: if not None, the revision history in the new branch will |
48 | 1262 | be truncated to end with revision_id. | 1265 | be truncated to end with revision_id. |
49 | 1266 | tag_selector: Optional callback that receives a tag name | ||
50 | 1267 | and should return a boolean to indicate whether a tag should be copied | ||
51 | 1263 | """ | 1268 | """ |
52 | 1264 | return InterBranch.get(self, destination).copy_content_into( | 1269 | return InterBranch.get(self, destination).copy_content_into( |
54 | 1265 | revision_id=revision_id) | 1270 | revision_id=revision_id, tag_selector=tag_selector) |
55 | 1266 | 1271 | ||
56 | 1267 | def update_references(self, target): | 1272 | def update_references(self, target): |
57 | 1268 | if not self._format.supports_reference_locations: | 1273 | if not self._format.supports_reference_locations: |
58 | @@ -1307,7 +1312,8 @@ | |||
59 | 1307 | 1312 | ||
60 | 1308 | def create_clone_on_transport(self, to_transport, revision_id=None, | 1313 | def create_clone_on_transport(self, to_transport, revision_id=None, |
61 | 1309 | stacked_on=None, create_prefix=False, | 1314 | stacked_on=None, create_prefix=False, |
63 | 1310 | use_existing_dir=False, no_tree=None): | 1315 | use_existing_dir=False, no_tree=None, |
64 | 1316 | tag_selector=None): | ||
65 | 1311 | """Create a clone of this branch and its bzrdir. | 1317 | """Create a clone of this branch and its bzrdir. |
66 | 1312 | 1318 | ||
67 | 1313 | :param to_transport: The transport to clone onto. | 1319 | :param to_transport: The transport to clone onto. |
68 | @@ -1327,7 +1333,7 @@ | |||
69 | 1327 | dir_to = self.controldir.clone_on_transport( | 1333 | dir_to = self.controldir.clone_on_transport( |
70 | 1328 | to_transport, revision_id=revision_id, stacked_on=stacked_on, | 1334 | to_transport, revision_id=revision_id, stacked_on=stacked_on, |
71 | 1329 | create_prefix=create_prefix, use_existing_dir=use_existing_dir, | 1335 | create_prefix=create_prefix, use_existing_dir=use_existing_dir, |
73 | 1330 | no_tree=no_tree) | 1336 | no_tree=no_tree, tag_selector=tag_selector) |
74 | 1331 | return dir_to.open_branch() | 1337 | return dir_to.open_branch() |
75 | 1332 | 1338 | ||
76 | 1333 | def create_checkout(self, to_location, revision_id=None, | 1339 | def create_checkout(self, to_location, revision_id=None, |
77 | @@ -2054,7 +2060,7 @@ | |||
78 | 2054 | raise NotImplementedError(klass._get_branch_formats_to_test) | 2060 | raise NotImplementedError(klass._get_branch_formats_to_test) |
79 | 2055 | 2061 | ||
80 | 2056 | def pull(self, overwrite=False, stop_revision=None, | 2062 | def pull(self, overwrite=False, stop_revision=None, |
82 | 2057 | possible_transports=None, local=False): | 2063 | possible_transports=None, local=False, tag_selector=None): |
83 | 2058 | """Mirror source into target branch. | 2064 | """Mirror source into target branch. |
84 | 2059 | 2065 | ||
85 | 2060 | The target branch is considered to be 'local', having low latency. | 2066 | The target branch is considered to be 'local', having low latency. |
86 | @@ -2064,18 +2070,21 @@ | |||
87 | 2064 | raise NotImplementedError(self.pull) | 2070 | raise NotImplementedError(self.pull) |
88 | 2065 | 2071 | ||
89 | 2066 | def push(self, overwrite=False, stop_revision=None, lossy=False, | 2072 | def push(self, overwrite=False, stop_revision=None, lossy=False, |
91 | 2067 | _override_hook_source_branch=None): | 2073 | _override_hook_source_branch=None, tag_selector=None): |
92 | 2068 | """Mirror the source branch into the target branch. | 2074 | """Mirror the source branch into the target branch. |
93 | 2069 | 2075 | ||
94 | 2070 | The source branch is considered to be 'local', having low latency. | 2076 | The source branch is considered to be 'local', having low latency. |
95 | 2071 | """ | 2077 | """ |
96 | 2072 | raise NotImplementedError(self.push) | 2078 | raise NotImplementedError(self.push) |
97 | 2073 | 2079 | ||
99 | 2074 | def copy_content_into(self, revision_id=None): | 2080 | def copy_content_into(self, revision_id=None, tag_selector=None): |
100 | 2075 | """Copy the content of source into target | 2081 | """Copy the content of source into target |
101 | 2076 | 2082 | ||
104 | 2077 | revision_id: if not None, the revision history in the new branch will | 2083 | :param revision_id: |
105 | 2078 | be truncated to end with revision_id. | 2084 | if not None, the revision history in the new branch will |
106 | 2085 | be truncated to end with revision_id. | ||
107 | 2086 | :param tag_selector: Optional callback that can decide | ||
108 | 2087 | to copy or not copy tags. | ||
109 | 2079 | """ | 2088 | """ |
110 | 2080 | raise NotImplementedError(self.copy_content_into) | 2089 | raise NotImplementedError(self.copy_content_into) |
111 | 2081 | 2090 | ||
112 | @@ -2122,7 +2131,7 @@ | |||
113 | 2122 | return format._custom_format | 2131 | return format._custom_format |
114 | 2123 | return format | 2132 | return format |
115 | 2124 | 2133 | ||
117 | 2125 | def copy_content_into(self, revision_id=None): | 2134 | def copy_content_into(self, revision_id=None, tag_selector=None): |
118 | 2126 | """Copy the content of source into target | 2135 | """Copy the content of source into target |
119 | 2127 | 2136 | ||
120 | 2128 | revision_id: if not None, the revision history in the new branch will | 2137 | revision_id: if not None, the revision history in the new branch will |
121 | @@ -2139,7 +2148,7 @@ | |||
122 | 2139 | if parent: | 2148 | if parent: |
123 | 2140 | self.target.set_parent(parent) | 2149 | self.target.set_parent(parent) |
124 | 2141 | if self.source._push_should_merge_tags(): | 2150 | if self.source._push_should_merge_tags(): |
126 | 2142 | self.source.tags.merge_to(self.target.tags) | 2151 | self.source.tags.merge_to(self.target.tags, selector=tag_selector) |
127 | 2143 | 2152 | ||
128 | 2144 | def fetch(self, stop_revision=None, limit=None, lossy=False): | 2153 | def fetch(self, stop_revision=None, limit=None, lossy=False): |
129 | 2145 | if self.target.base == self.source.base: | 2154 | if self.target.base == self.source.base: |
130 | @@ -2200,7 +2209,8 @@ | |||
131 | 2200 | 2209 | ||
132 | 2201 | def pull(self, overwrite=False, stop_revision=None, | 2210 | def pull(self, overwrite=False, stop_revision=None, |
133 | 2202 | possible_transports=None, run_hooks=True, | 2211 | possible_transports=None, run_hooks=True, |
135 | 2203 | _override_hook_target=None, local=False): | 2212 | _override_hook_target=None, local=False, |
136 | 2213 | tag_selector=None): | ||
137 | 2204 | """Pull from source into self, updating my master if any. | 2214 | """Pull from source into self, updating my master if any. |
138 | 2205 | 2215 | ||
139 | 2206 | :param run_hooks: Private parameter - if false, this branch | 2216 | :param run_hooks: Private parameter - if false, this branch |
140 | @@ -2231,15 +2241,17 @@ | |||
141 | 2231 | if master_branch: | 2241 | if master_branch: |
142 | 2232 | # pull from source into master. | 2242 | # pull from source into master. |
143 | 2233 | master_branch.pull( | 2243 | master_branch.pull( |
145 | 2234 | self.source, overwrite, stop_revision, run_hooks=False) | 2244 | self.source, overwrite, stop_revision, run_hooks=False, |
146 | 2245 | tag_selector=tag_selector) | ||
147 | 2235 | return self._pull( | 2246 | return self._pull( |
148 | 2236 | overwrite, stop_revision, _hook_master=master_branch, | 2247 | overwrite, stop_revision, _hook_master=master_branch, |
149 | 2237 | run_hooks=run_hooks, | 2248 | run_hooks=run_hooks, |
150 | 2238 | _override_hook_target=_override_hook_target, | 2249 | _override_hook_target=_override_hook_target, |
152 | 2239 | merge_tags_to_master=not source_is_master) | 2250 | merge_tags_to_master=not source_is_master, |
153 | 2251 | tag_selector=tag_selector) | ||
154 | 2240 | 2252 | ||
155 | 2241 | def push(self, overwrite=False, stop_revision=None, lossy=False, | 2253 | def push(self, overwrite=False, stop_revision=None, lossy=False, |
157 | 2242 | _override_hook_source_branch=None): | 2254 | _override_hook_source_branch=None, tag_selector=None): |
158 | 2243 | """See InterBranch.push. | 2255 | """See InterBranch.push. |
159 | 2244 | 2256 | ||
160 | 2245 | This is the basic concrete implementation of push() | 2257 | This is the basic concrete implementation of push() |
161 | @@ -2271,18 +2283,21 @@ | |||
162 | 2271 | with master_branch.lock_write(): | 2283 | with master_branch.lock_write(): |
163 | 2272 | # push into the master from the source branch. | 2284 | # push into the master from the source branch. |
164 | 2273 | master_inter = InterBranch.get(self.source, master_branch) | 2285 | master_inter = InterBranch.get(self.source, master_branch) |
166 | 2274 | master_inter._basic_push(overwrite, stop_revision) | 2286 | master_inter._basic_push( |
167 | 2287 | overwrite, stop_revision, tag_selector=tag_selector) | ||
168 | 2275 | # and push into the target branch from the source. Note | 2288 | # and push into the target branch from the source. Note |
169 | 2276 | # that we push from the source branch again, because it's | 2289 | # that we push from the source branch again, because it's |
170 | 2277 | # considered the highest bandwidth repository. | 2290 | # considered the highest bandwidth repository. |
172 | 2278 | result = self._basic_push(overwrite, stop_revision) | 2291 | result = self._basic_push( |
173 | 2292 | overwrite, stop_revision, tag_selector=tag_selector) | ||
174 | 2279 | result.master_branch = master_branch | 2293 | result.master_branch = master_branch |
175 | 2280 | result.local_branch = self.target | 2294 | result.local_branch = self.target |
176 | 2281 | _run_hooks() | 2295 | _run_hooks() |
177 | 2282 | else: | 2296 | else: |
178 | 2283 | master_branch = None | 2297 | master_branch = None |
179 | 2284 | # no master branch | 2298 | # no master branch |
181 | 2285 | result = self._basic_push(overwrite, stop_revision) | 2299 | result = self._basic_push( |
182 | 2300 | overwrite, stop_revision, tag_selector=tag_selector) | ||
183 | 2286 | # TODO: Why set master_branch and local_branch if there's no | 2301 | # TODO: Why set master_branch and local_branch if there's no |
184 | 2287 | # binding? Maybe cleaner to just leave them unset? -- mbp | 2302 | # binding? Maybe cleaner to just leave them unset? -- mbp |
185 | 2288 | # 20070504 | 2303 | # 20070504 |
186 | @@ -2291,7 +2306,7 @@ | |||
187 | 2291 | _run_hooks() | 2306 | _run_hooks() |
188 | 2292 | return result | 2307 | return result |
189 | 2293 | 2308 | ||
191 | 2294 | def _basic_push(self, overwrite, stop_revision): | 2309 | def _basic_push(self, overwrite, stop_revision, tag_selector=None): |
192 | 2295 | """Basic implementation of push without bound branches or hooks. | 2310 | """Basic implementation of push without bound branches or hooks. |
193 | 2296 | 2311 | ||
194 | 2297 | Must be called with source read locked and target write locked. | 2312 | Must be called with source read locked and target write locked. |
195 | @@ -2310,7 +2325,7 @@ | |||
196 | 2310 | if self.source._push_should_merge_tags(): | 2325 | if self.source._push_should_merge_tags(): |
197 | 2311 | result.tag_updates, result.tag_conflicts = ( | 2326 | result.tag_updates, result.tag_conflicts = ( |
198 | 2312 | self.source.tags.merge_to( | 2327 | self.source.tags.merge_to( |
200 | 2313 | self.target.tags, "tags" in overwrite)) | 2328 | self.target.tags, "tags" in overwrite, selector=tag_selector)) |
201 | 2314 | self.update_references() | 2329 | self.update_references() |
202 | 2315 | result.new_revno, result.new_revid = self.target.last_revision_info() | 2330 | result.new_revno, result.new_revid = self.target.last_revision_info() |
203 | 2316 | return result | 2331 | return result |
204 | @@ -2318,7 +2333,7 @@ | |||
205 | 2318 | def _pull(self, overwrite=False, stop_revision=None, | 2333 | def _pull(self, overwrite=False, stop_revision=None, |
206 | 2319 | possible_transports=None, _hook_master=None, run_hooks=True, | 2334 | possible_transports=None, _hook_master=None, run_hooks=True, |
207 | 2320 | _override_hook_target=None, local=False, | 2335 | _override_hook_target=None, local=False, |
209 | 2321 | merge_tags_to_master=True): | 2336 | merge_tags_to_master=True, tag_selector=None): |
210 | 2322 | """See Branch.pull. | 2337 | """See Branch.pull. |
211 | 2323 | 2338 | ||
212 | 2324 | This function is the core worker, used by GenericInterBranch.pull to | 2339 | This function is the core worker, used by GenericInterBranch.pull to |
213 | @@ -2362,7 +2377,8 @@ | |||
214 | 2362 | result.tag_updates, result.tag_conflicts = ( | 2377 | result.tag_updates, result.tag_conflicts = ( |
215 | 2363 | self.source.tags.merge_to( | 2378 | self.source.tags.merge_to( |
216 | 2364 | self.target.tags, "tags" in overwrite, | 2379 | self.target.tags, "tags" in overwrite, |
218 | 2365 | ignore_master=not merge_tags_to_master)) | 2380 | ignore_master=not merge_tags_to_master, |
219 | 2381 | selector=tag_selector)) | ||
220 | 2366 | self.update_references() | 2382 | self.update_references() |
221 | 2367 | result.new_revno, result.new_revid = ( | 2383 | result.new_revno, result.new_revid = ( |
222 | 2368 | self.target.last_revision_info()) | 2384 | self.target.last_revision_info()) |
223 | 2369 | 2385 | ||
224 | === modified file 'breezy/bzr/branch.py' | |||
225 | --- breezy/bzr/branch.py 2020-02-18 01:57:45 +0000 | |||
226 | +++ breezy/bzr/branch.py 2020-02-21 04:09:29 +0000 | |||
227 | @@ -27,6 +27,8 @@ | |||
228 | 27 | lockdir, | 27 | lockdir, |
229 | 28 | rio, | 28 | rio, |
230 | 29 | shelf, | 29 | shelf, |
231 | 30 | ) | ||
232 | 31 | from breezy.bzr import ( | ||
233 | 30 | tag as _mod_tag, | 32 | tag as _mod_tag, |
234 | 31 | ) | 33 | ) |
235 | 32 | """) | 34 | """) |
236 | @@ -1022,7 +1024,7 @@ | |||
237 | 1022 | def _make_reference_clone_function(format, a_branch): | 1024 | def _make_reference_clone_function(format, a_branch): |
238 | 1023 | """Create a clone() routine for a branch dynamically.""" | 1025 | """Create a clone() routine for a branch dynamically.""" |
239 | 1024 | def clone(to_bzrdir, revision_id=None, | 1026 | def clone(to_bzrdir, revision_id=None, |
241 | 1025 | repository_policy=None): | 1027 | repository_policy=None, tag_selector=None): |
242 | 1026 | """See Branch.clone().""" | 1028 | """See Branch.clone().""" |
243 | 1027 | return format.initialize(to_bzrdir, target_branch=a_branch) | 1029 | return format.initialize(to_bzrdir, target_branch=a_branch) |
244 | 1028 | # cannot obey revision_id limits when cloning a reference ... | 1030 | # cannot obey revision_id limits when cloning a reference ... |
245 | 1029 | 1031 | ||
246 | === modified file 'breezy/bzr/bzrdir.py' | |||
247 | --- breezy/bzr/bzrdir.py 2020-02-18 01:57:45 +0000 | |||
248 | +++ breezy/bzr/bzrdir.py 2020-02-21 04:09:29 +0000 | |||
249 | @@ -141,7 +141,8 @@ | |||
250 | 141 | 141 | ||
251 | 142 | def clone_on_transport(self, transport, revision_id=None, | 142 | def clone_on_transport(self, transport, revision_id=None, |
252 | 143 | force_new_repo=False, preserve_stacking=False, stacked_on=None, | 143 | force_new_repo=False, preserve_stacking=False, stacked_on=None, |
254 | 144 | create_prefix=False, use_existing_dir=True, no_tree=False): | 144 | create_prefix=False, use_existing_dir=True, no_tree=False, |
255 | 145 | tag_selector=None): | ||
256 | 145 | """Clone this bzrdir and its contents to transport verbatim. | 146 | """Clone this bzrdir and its contents to transport verbatim. |
257 | 146 | 147 | ||
258 | 147 | :param transport: The transport for the location to produce the clone | 148 | :param transport: The transport for the location to produce the clone |
259 | @@ -233,7 +234,8 @@ | |||
260 | 233 | if local_branch is not None: | 234 | if local_branch is not None: |
261 | 234 | local_branch.clone( | 235 | local_branch.clone( |
262 | 235 | result, revision_id=revision_id, | 236 | result, revision_id=revision_id, |
264 | 236 | repository_policy=repository_policy) | 237 | repository_policy=repository_policy, |
265 | 238 | tag_selector=tag_selector) | ||
266 | 237 | try: | 239 | try: |
267 | 238 | # Cheaper to check if the target is not local, than to try making | 240 | # Cheaper to check if the target is not local, than to try making |
268 | 239 | # the tree and fail. | 241 | # the tree and fail. |
269 | 240 | 242 | ||
270 | === modified file 'breezy/bzr/knitpack_repo.py' | |||
271 | --- breezy/bzr/knitpack_repo.py 2020-02-18 01:57:45 +0000 | |||
272 | +++ breezy/bzr/knitpack_repo.py 2020-02-21 04:09:29 +0000 | |||
273 | @@ -264,8 +264,6 @@ | |||
274 | 264 | class RepositoryFormatKnitPack5(RepositoryFormatPack): | 264 | class RepositoryFormatKnitPack5(RepositoryFormatPack): |
275 | 265 | """Repository that supports external references to allow stacking. | 265 | """Repository that supports external references to allow stacking. |
276 | 266 | 266 | ||
277 | 267 | New in release 1.6. | ||
278 | 268 | |||
279 | 269 | Supports external lookups, which results in non-truncated ghosts after | 267 | Supports external lookups, which results in non-truncated ghosts after |
280 | 270 | reconcile compared to pack-0.92 formats. | 268 | reconcile compared to pack-0.92 formats. |
281 | 271 | """ | 269 | """ |
282 | @@ -303,8 +301,6 @@ | |||
283 | 303 | class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack): | 301 | class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack): |
284 | 304 | """A repository with rich roots and stacking. | 302 | """A repository with rich roots and stacking. |
285 | 305 | 303 | ||
286 | 306 | New in release 1.6.1. | ||
287 | 307 | |||
288 | 308 | Supports stacking on other repositories, allowing data to be accessed | 304 | Supports stacking on other repositories, allowing data to be accessed |
289 | 309 | without being stored locally. | 305 | without being stored locally. |
290 | 310 | """ | 306 | """ |
291 | @@ -344,8 +340,6 @@ | |||
292 | 344 | class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack): | 340 | class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack): |
293 | 345 | """A repository with rich roots and external references. | 341 | """A repository with rich roots and external references. |
294 | 346 | 342 | ||
295 | 347 | New in release 1.6. | ||
296 | 348 | |||
297 | 349 | Supports external lookups, which results in non-truncated ghosts after | 343 | Supports external lookups, which results in non-truncated ghosts after |
298 | 350 | reconcile compared to pack-0.92 formats. | 344 | reconcile compared to pack-0.92 formats. |
299 | 351 | 345 | ||
300 | 352 | 346 | ||
301 | === modified file 'breezy/bzr/remote.py' | |||
302 | --- breezy/bzr/remote.py 2020-02-18 01:57:45 +0000 | |||
303 | +++ breezy/bzr/remote.py 2020-02-21 04:09:29 +0000 | |||
304 | @@ -4019,12 +4019,12 @@ | |||
305 | 4019 | source, overwrite=overwrite, stop_revision=stop_revision, | 4019 | source, overwrite=overwrite, stop_revision=stop_revision, |
306 | 4020 | _override_hook_target=self, **kwargs) | 4020 | _override_hook_target=self, **kwargs) |
307 | 4021 | 4021 | ||
309 | 4022 | def push(self, target, overwrite=False, stop_revision=None, lossy=False): | 4022 | def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None): |
310 | 4023 | with self.lock_read(): | 4023 | with self.lock_read(): |
311 | 4024 | self._ensure_real() | 4024 | self._ensure_real() |
312 | 4025 | return self._real_branch.push( | 4025 | return self._real_branch.push( |
313 | 4026 | target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy, | 4026 | target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy, |
315 | 4027 | _override_hook_source_branch=self) | 4027 | _override_hook_source_branch=self, tag_selector=tag_selector) |
316 | 4028 | 4028 | ||
317 | 4029 | def peek_lock_mode(self): | 4029 | def peek_lock_mode(self): |
318 | 4030 | return self._lock_mode | 4030 | return self._lock_mode |
319 | 4031 | 4031 | ||
320 | === added file 'breezy/bzr/tag.py' | |||
321 | --- breezy/bzr/tag.py 1970-01-01 00:00:00 +0000 | |||
322 | +++ breezy/bzr/tag.py 2020-02-21 04:09:29 +0000 | |||
323 | @@ -0,0 +1,113 @@ | |||
324 | 1 | # Copyright (C) 2005-2012 Canonical Ltd | ||
325 | 2 | # Copyright (C) 2020 Breezy Developers | ||
326 | 3 | # | ||
327 | 4 | # This program is free software; you can redistribute it and/or modify | ||
328 | 5 | # it under the terms of the GNU General Public License as published by | ||
329 | 6 | # the Free Software Foundation; either version 2 of the License, or | ||
330 | 7 | # (at your option) any later version. | ||
331 | 8 | # | ||
332 | 9 | # This program is distributed in the hope that it will be useful, | ||
333 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
334 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
335 | 12 | # GNU General Public License for more details. | ||
336 | 13 | # | ||
337 | 14 | # You should have received a copy of the GNU General Public License | ||
338 | 15 | # along with this program; if not, write to the Free Software | ||
339 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
340 | 17 | |||
341 | 18 | from __future__ import absolute_import | ||
342 | 19 | |||
343 | 20 | from ..tag import Tags | ||
344 | 21 | |||
345 | 22 | from .. import ( | ||
346 | 23 | bencode, | ||
347 | 24 | errors, | ||
348 | 25 | trace, | ||
349 | 26 | ) | ||
350 | 27 | |||
351 | 28 | |||
352 | 29 | class BasicTags(Tags): | ||
353 | 30 | """Tag storage in an unversioned branch control file. | ||
354 | 31 | """ | ||
355 | 32 | |||
356 | 33 | def set_tag(self, tag_name, tag_target): | ||
357 | 34 | """Add a tag definition to the branch. | ||
358 | 35 | |||
359 | 36 | Behaviour if the tag is already present is not defined (yet). | ||
360 | 37 | """ | ||
361 | 38 | # all done with a write lock held, so this looks atomic | ||
362 | 39 | with self.branch.lock_write(): | ||
363 | 40 | master = self.branch.get_master_branch() | ||
364 | 41 | if master is not None: | ||
365 | 42 | master.tags.set_tag(tag_name, tag_target) | ||
366 | 43 | td = self.get_tag_dict() | ||
367 | 44 | td[tag_name] = tag_target | ||
368 | 45 | self._set_tag_dict(td) | ||
369 | 46 | |||
370 | 47 | def lookup_tag(self, tag_name): | ||
371 | 48 | """Return the referent string of a tag""" | ||
372 | 49 | td = self.get_tag_dict() | ||
373 | 50 | try: | ||
374 | 51 | return td[tag_name] | ||
375 | 52 | except KeyError: | ||
376 | 53 | raise errors.NoSuchTag(tag_name) | ||
377 | 54 | |||
378 | 55 | def get_tag_dict(self): | ||
379 | 56 | with self.branch.lock_read(): | ||
380 | 57 | try: | ||
381 | 58 | tag_content = self.branch._get_tags_bytes() | ||
382 | 59 | except errors.NoSuchFile: | ||
383 | 60 | # ugly, but only abentley should see this :) | ||
384 | 61 | trace.warning('No branch/tags file in %s. ' | ||
385 | 62 | 'This branch was probably created by bzr 0.15pre. ' | ||
386 | 63 | 'Create an empty file to silence this message.' | ||
387 | 64 | % (self.branch, )) | ||
388 | 65 | return {} | ||
389 | 66 | return self._deserialize_tag_dict(tag_content) | ||
390 | 67 | |||
391 | 68 | def delete_tag(self, tag_name): | ||
392 | 69 | """Delete a tag definition. | ||
393 | 70 | """ | ||
394 | 71 | with self.branch.lock_write(): | ||
395 | 72 | d = self.get_tag_dict() | ||
396 | 73 | try: | ||
397 | 74 | del d[tag_name] | ||
398 | 75 | except KeyError: | ||
399 | 76 | raise errors.NoSuchTag(tag_name) | ||
400 | 77 | master = self.branch.get_master_branch() | ||
401 | 78 | if master is not None: | ||
402 | 79 | try: | ||
403 | 80 | master.tags.delete_tag(tag_name) | ||
404 | 81 | except errors.NoSuchTag: | ||
405 | 82 | pass | ||
406 | 83 | self._set_tag_dict(d) | ||
407 | 84 | |||
408 | 85 | def _set_tag_dict(self, new_dict): | ||
409 | 86 | """Replace all tag definitions | ||
410 | 87 | |||
411 | 88 | WARNING: Calling this on an unlocked branch will lock it, and will | ||
412 | 89 | replace the tags without warning on conflicts. | ||
413 | 90 | |||
414 | 91 | :param new_dict: Dictionary from tag name to target. | ||
415 | 92 | """ | ||
416 | 93 | return self.branch._set_tags_bytes(self._serialize_tag_dict(new_dict)) | ||
417 | 94 | |||
418 | 95 | def _serialize_tag_dict(self, tag_dict): | ||
419 | 96 | td = dict((k.encode('utf-8'), v) | ||
420 | 97 | for k, v in tag_dict.items()) | ||
421 | 98 | return bencode.bencode(td) | ||
422 | 99 | |||
423 | 100 | def _deserialize_tag_dict(self, tag_content): | ||
424 | 101 | """Convert the tag file into a dictionary of tags""" | ||
425 | 102 | # was a special case to make initialization easy, an empty definition | ||
426 | 103 | # is an empty dictionary | ||
427 | 104 | if tag_content == b'': | ||
428 | 105 | return {} | ||
429 | 106 | try: | ||
430 | 107 | r = {} | ||
431 | 108 | for k, v in bencode.bdecode(tag_content).items(): | ||
432 | 109 | r[k.decode('utf-8')] = v | ||
433 | 110 | return r | ||
434 | 111 | except ValueError as e: | ||
435 | 112 | raise ValueError("failed to deserialize tag dictionary %r: %s" | ||
436 | 113 | % (tag_content, e)) | ||
437 | 0 | 114 | ||
438 | === modified file 'breezy/controldir.py' | |||
439 | --- breezy/controldir.py 2020-02-18 01:57:45 +0000 | |||
440 | +++ breezy/controldir.py 2020-02-21 04:09:29 +0000 | |||
441 | @@ -398,7 +398,8 @@ | |||
442 | 398 | raise NotImplementedError(self.sprout) | 398 | raise NotImplementedError(self.sprout) |
443 | 399 | 399 | ||
444 | 400 | def push_branch(self, source, revision_id=None, overwrite=False, | 400 | def push_branch(self, source, revision_id=None, overwrite=False, |
446 | 401 | remember=False, create_prefix=False, lossy=False): | 401 | remember=False, create_prefix=False, lossy=False, |
447 | 402 | tag_selector=None): | ||
448 | 402 | """Push the source branch into this ControlDir.""" | 403 | """Push the source branch into this ControlDir.""" |
449 | 403 | br_to = None | 404 | br_to = None |
450 | 404 | # If we can open a branch, use its direct repository, otherwise see | 405 | # If we can open a branch, use its direct repository, otherwise see |
451 | @@ -422,7 +423,9 @@ | |||
452 | 422 | # revision | 423 | # revision |
453 | 423 | revision_id = source.last_revision() | 424 | revision_id = source.last_revision() |
454 | 424 | repository_to.fetch(source.repository, revision_id=revision_id) | 425 | repository_to.fetch(source.repository, revision_id=revision_id) |
456 | 425 | br_to = source.sprout(self, revision_id=revision_id, lossy=lossy) | 426 | br_to = source.sprout( |
457 | 427 | self, revision_id=revision_id, lossy=lossy, | ||
458 | 428 | tag_selector=tag_selector) | ||
459 | 426 | if source.get_push_location() is None or remember: | 429 | if source.get_push_location() is None or remember: |
460 | 427 | # FIXME: Should be done only if we succeed ? -- vila 2012-01-18 | 430 | # FIXME: Should be done only if we succeed ? -- vila 2012-01-18 |
461 | 428 | source.set_push_location(br_to.base) | 431 | source.set_push_location(br_to.base) |
462 | @@ -442,17 +445,19 @@ | |||
463 | 442 | tree_to = self.open_workingtree() | 445 | tree_to = self.open_workingtree() |
464 | 443 | except errors.NotLocalUrl: | 446 | except errors.NotLocalUrl: |
465 | 444 | push_result.branch_push_result = source.push( | 447 | push_result.branch_push_result = source.push( |
467 | 445 | br_to, overwrite, stop_revision=revision_id, lossy=lossy) | 448 | br_to, overwrite, stop_revision=revision_id, lossy=lossy, |
468 | 449 | tag_selector=tag_selector) | ||
469 | 446 | push_result.workingtree_updated = False | 450 | push_result.workingtree_updated = False |
470 | 447 | except errors.NoWorkingTree: | 451 | except errors.NoWorkingTree: |
471 | 448 | push_result.branch_push_result = source.push( | 452 | push_result.branch_push_result = source.push( |
473 | 449 | br_to, overwrite, stop_revision=revision_id, lossy=lossy) | 453 | br_to, overwrite, stop_revision=revision_id, lossy=lossy, |
474 | 454 | tag_selector=tag_selector) | ||
475 | 450 | push_result.workingtree_updated = None # Not applicable | 455 | push_result.workingtree_updated = None # Not applicable |
476 | 451 | else: | 456 | else: |
477 | 452 | with tree_to.lock_write(): | 457 | with tree_to.lock_write(): |
478 | 453 | push_result.branch_push_result = source.push( | 458 | push_result.branch_push_result = source.push( |
479 | 454 | tree_to.branch, overwrite, stop_revision=revision_id, | 459 | tree_to.branch, overwrite, stop_revision=revision_id, |
481 | 455 | lossy=lossy) | 460 | lossy=lossy, tag_selector=tag_selector) |
482 | 456 | tree_to.update() | 461 | tree_to.update() |
483 | 457 | push_result.workingtree_updated = True | 462 | push_result.workingtree_updated = True |
484 | 458 | push_result.old_revno = push_result.branch_push_result.old_revno | 463 | push_result.old_revno = push_result.branch_push_result.old_revno |
485 | @@ -491,7 +496,7 @@ | |||
486 | 491 | raise NotImplementedError(self.check_conversion_target) | 496 | raise NotImplementedError(self.check_conversion_target) |
487 | 492 | 497 | ||
488 | 493 | def clone(self, url, revision_id=None, force_new_repo=False, | 498 | def clone(self, url, revision_id=None, force_new_repo=False, |
490 | 494 | preserve_stacking=False): | 499 | preserve_stacking=False, tag_selector=None): |
491 | 495 | """Clone this controldir and its contents to url verbatim. | 500 | """Clone this controldir and its contents to url verbatim. |
492 | 496 | 501 | ||
493 | 497 | :param url: The url create the clone at. If url's last component does | 502 | :param url: The url create the clone at. If url's last component does |
494 | @@ -507,11 +512,13 @@ | |||
495 | 507 | return self.clone_on_transport(_mod_transport.get_transport(url), | 512 | return self.clone_on_transport(_mod_transport.get_transport(url), |
496 | 508 | revision_id=revision_id, | 513 | revision_id=revision_id, |
497 | 509 | force_new_repo=force_new_repo, | 514 | force_new_repo=force_new_repo, |
499 | 510 | preserve_stacking=preserve_stacking) | 515 | preserve_stacking=preserve_stacking, |
500 | 516 | tag_selector=tag_selector) | ||
501 | 511 | 517 | ||
502 | 512 | def clone_on_transport(self, transport, revision_id=None, | 518 | def clone_on_transport(self, transport, revision_id=None, |
503 | 513 | force_new_repo=False, preserve_stacking=False, stacked_on=None, | 519 | force_new_repo=False, preserve_stacking=False, stacked_on=None, |
505 | 514 | create_prefix=False, use_existing_dir=True, no_tree=False): | 520 | create_prefix=False, use_existing_dir=True, no_tree=False, |
506 | 521 | tag_selector=None): | ||
507 | 515 | """Clone this controldir and its contents to transport verbatim. | 522 | """Clone this controldir and its contents to transport verbatim. |
508 | 516 | 523 | ||
509 | 517 | :param transport: The transport for the location to produce the clone | 524 | :param transport: The transport for the location to produce the clone |
510 | 518 | 525 | ||
511 | === modified file 'breezy/git/branch.py' | |||
512 | --- breezy/git/branch.py 2020-02-18 01:57:45 +0000 | |||
513 | +++ breezy/git/branch.py 2020-02-21 04:09:29 +0000 | |||
514 | @@ -41,7 +41,6 @@ | |||
515 | 41 | lock, | 41 | lock, |
516 | 42 | repository as _mod_repository, | 42 | repository as _mod_repository, |
517 | 43 | revision, | 43 | revision, |
518 | 44 | tag, | ||
519 | 45 | trace, | 44 | trace, |
520 | 46 | transport, | 45 | transport, |
521 | 47 | urlutils, | 46 | urlutils, |
522 | @@ -50,6 +49,10 @@ | |||
523 | 50 | from ..revision import ( | 49 | from ..revision import ( |
524 | 51 | NULL_REVISION, | 50 | NULL_REVISION, |
525 | 52 | ) | 51 | ) |
526 | 52 | from ..tag import ( | ||
527 | 53 | Tags, | ||
528 | 54 | InterTags, | ||
529 | 55 | ) | ||
530 | 53 | from ..trace import ( | 56 | from ..trace import ( |
531 | 54 | is_quiet, | 57 | is_quiet, |
532 | 55 | mutter, | 58 | mutter, |
533 | @@ -111,44 +114,75 @@ | |||
534 | 111 | return self._lookup_revno(self.new_revid) | 114 | return self._lookup_revno(self.new_revid) |
535 | 112 | 115 | ||
536 | 113 | 116 | ||
546 | 114 | class GitTags(tag.BasicTags): | 117 | class InterTagsFromGitToRemoteGit(InterTags): |
547 | 115 | """Ref-based tag dictionary.""" | 118 | |
548 | 116 | 119 | @classmethod | |
549 | 117 | def __init__(self, branch): | 120 | def is_compatible(klass, source, target): |
550 | 118 | self.branch = branch | 121 | if not isinstance(source, GitTags): |
551 | 119 | self.repository = branch.repository | 122 | return False |
552 | 120 | 123 | if not isinstance(target, GitTags): | |
553 | 121 | def _merge_to_remote_git(self, target_repo, source_tag_refs, | 124 | return False |
554 | 122 | overwrite=False): | 125 | if getattr(target.branch.repository, "_git", None) is not None: |
555 | 126 | return False | ||
556 | 127 | return True | ||
557 | 128 | |||
558 | 129 | def merge(self, overwrite=False, ignore_master=False, selector=None): | ||
559 | 130 | if self.source.branch.repository.has_same_location(self.target.branch.repository): | ||
560 | 131 | return {}, [] | ||
561 | 123 | updates = {} | 132 | updates = {} |
562 | 124 | conflicts = [] | 133 | conflicts = [] |
563 | 134 | source_tag_refs = self.source.branch.get_tag_refs() | ||
564 | 125 | 135 | ||
565 | 126 | def get_changed_refs(old_refs): | 136 | def get_changed_refs(old_refs): |
566 | 127 | ret = dict(old_refs) | 137 | ret = dict(old_refs) |
567 | 128 | for ref_name, tag_name, peeled, unpeeled in ( | 138 | for ref_name, tag_name, peeled, unpeeled in ( |
568 | 129 | source_tag_refs.iteritems()): | 139 | source_tag_refs.iteritems()): |
569 | 140 | if selector and not selector(tag_name): | ||
570 | 141 | continue | ||
571 | 130 | if old_refs.get(ref_name) == unpeeled: | 142 | if old_refs.get(ref_name) == unpeeled: |
572 | 131 | pass | 143 | pass |
573 | 132 | elif overwrite or ref_name not in old_refs: | 144 | elif overwrite or ref_name not in old_refs: |
574 | 133 | ret[ref_name] = unpeeled | 145 | ret[ref_name] = unpeeled |
576 | 134 | updates[tag_name] = target_repo.lookup_foreign_revision_id( | 146 | updates[tag_name] = self.target.branch.repository.lookup_foreign_revision_id( |
577 | 135 | peeled) | 147 | peeled) |
578 | 148 | self.target.branch._tag_refs = None | ||
579 | 136 | else: | 149 | else: |
580 | 137 | conflicts.append( | 150 | conflicts.append( |
581 | 138 | (tag_name, | 151 | (tag_name, |
582 | 139 | self.repository.lookup_foreign_revision_id(peeled), | 152 | self.repository.lookup_foreign_revision_id(peeled), |
584 | 140 | target_repo.lookup_foreign_revision_id( | 153 | self.target.branch.repository.lookup_foreign_revision_id( |
585 | 141 | old_refs[ref_name]))) | 154 | old_refs[ref_name]))) |
586 | 142 | return ret | 155 | return ret |
588 | 143 | target_repo.controldir.send_pack( | 156 | self.target.branch.repository.controldir.send_pack( |
589 | 144 | get_changed_refs, lambda have, want: []) | 157 | get_changed_refs, lambda have, want: []) |
594 | 145 | return updates, conflicts | 158 | return updates, set(conflicts) |
595 | 146 | 159 | ||
596 | 147 | def _merge_to_local_git(self, target_repo, source_tag_refs, | 160 | |
597 | 148 | overwrite=False): | 161 | class InterTagsFromGitToLocalGit(InterTags): |
598 | 162 | |||
599 | 163 | @classmethod | ||
600 | 164 | def is_compatible(klass, source, target): | ||
601 | 165 | if not isinstance(source, GitTags): | ||
602 | 166 | return False | ||
603 | 167 | if not isinstance(target, GitTags): | ||
604 | 168 | return False | ||
605 | 169 | if getattr(target.branch.repository, "_git", None) is None: | ||
606 | 170 | return False | ||
607 | 171 | return True | ||
608 | 172 | |||
609 | 173 | def merge(self, overwrite=False, ignore_master=False, selector=None): | ||
610 | 174 | if self.source.branch.repository.has_same_location(self.target.branch.repository): | ||
611 | 175 | return {}, [] | ||
612 | 176 | |||
613 | 149 | conflicts = [] | 177 | conflicts = [] |
614 | 150 | updates = {} | 178 | updates = {} |
615 | 179 | source_tag_refs = self.source.branch.get_tag_refs() | ||
616 | 180 | |||
617 | 181 | target_repo = self.target.branch.repository | ||
618 | 182 | |||
619 | 151 | for ref_name, tag_name, peeled, unpeeled in source_tag_refs: | 183 | for ref_name, tag_name, peeled, unpeeled in source_tag_refs: |
620 | 184 | if selector and not selector(tag_name): | ||
621 | 185 | continue | ||
622 | 152 | if target_repo._git.refs.get(ref_name) == unpeeled: | 186 | if target_repo._git.refs.get(ref_name) == unpeeled: |
623 | 153 | pass | 187 | pass |
624 | 154 | elif overwrite or ref_name not in target_repo._git.refs: | 188 | elif overwrite or ref_name not in target_repo._git.refs: |
625 | @@ -164,9 +198,10 @@ | |||
626 | 164 | tag_name) | 198 | tag_name) |
627 | 165 | continue | 199 | continue |
628 | 166 | target_repo._git.refs[ref_name] = unpeeled or peeled | 200 | target_repo._git.refs[ref_name] = unpeeled or peeled |
629 | 201 | self.target.branch._tag_refs = None | ||
630 | 167 | else: | 202 | else: |
631 | 168 | try: | 203 | try: |
633 | 169 | source_revid = self.repository.lookup_foreign_revision_id( | 204 | source_revid = self.source.branch.repository.lookup_foreign_revision_id( |
634 | 170 | peeled) | 205 | peeled) |
635 | 171 | target_revid = target_repo.lookup_foreign_revision_id( | 206 | target_revid = target_repo.lookup_foreign_revision_id( |
636 | 172 | target_repo._git.refs[ref_name]) | 207 | target_repo._git.refs[ref_name]) |
637 | @@ -179,32 +214,54 @@ | |||
638 | 179 | tag_name) | 214 | tag_name) |
639 | 180 | continue | 215 | continue |
640 | 181 | conflicts.append((tag_name, source_revid, target_revid)) | 216 | conflicts.append((tag_name, source_revid, target_revid)) |
658 | 182 | return updates, conflicts | 217 | return updates, set(conflicts) |
659 | 183 | 218 | ||
660 | 184 | def _merge_to_git(self, to_tags, source_tag_refs, overwrite=False): | 219 | |
661 | 185 | target_repo = to_tags.repository | 220 | class InterTagsFromGitToNonGit(InterTags): |
662 | 186 | if self.repository.has_same_location(target_repo): | 221 | |
663 | 187 | return {}, [] | 222 | @classmethod |
664 | 188 | try: | 223 | def is_compatible(klass, source, target): |
665 | 189 | if getattr(target_repo, "_git", None): | 224 | if not isinstance(source, GitTags): |
666 | 190 | return self._merge_to_local_git( | 225 | return False |
667 | 191 | target_repo, source_tag_refs, overwrite) | 226 | if isinstance(target, GitTags): |
668 | 192 | else: | 227 | return False |
669 | 193 | return self._merge_to_remote_git( | 228 | return True |
670 | 194 | target_repo, source_tag_refs, overwrite) | 229 | |
671 | 195 | finally: | 230 | def merge(self, overwrite=False, ignore_master=False, selector=None): |
672 | 196 | to_tags.branch._tag_refs = None | 231 | """See Tags.merge_to.""" |
673 | 197 | 232 | source_tag_refs = self.source.branch.get_tag_refs() | |
674 | 198 | def _merge_to_non_git(self, to_tags, source_tag_refs, overwrite=False): | 233 | if ignore_master: |
675 | 234 | master = None | ||
676 | 235 | else: | ||
677 | 236 | master = self.target.branch.get_master_branch() | ||
678 | 237 | with contextlib.ExitStack() as es: | ||
679 | 238 | if master is not None: | ||
680 | 239 | es.enter_context(master.lock_write()) | ||
681 | 240 | updates, conflicts = self._merge_to( | ||
682 | 241 | self.target, source_tag_refs, overwrite=overwrite, | ||
683 | 242 | selector=selector) | ||
684 | 243 | if master is not None: | ||
685 | 244 | extra_updates, extra_conflicts = self._merge_to( | ||
686 | 245 | master.tags, overwrite=overwrite, | ||
687 | 246 | source_tag_refs=source_tag_refs, | ||
688 | 247 | ignore_master=ignore_master, selector=selector) | ||
689 | 248 | updates.update(extra_updates) | ||
690 | 249 | conflicts.update(extra_conflicts) | ||
691 | 250 | return updates, conflicts | ||
692 | 251 | |||
693 | 252 | def _merge_to(self, to_tags, source_tag_refs, overwrite=False, | ||
694 | 253 | selector=None): | ||
695 | 199 | unpeeled_map = defaultdict(set) | 254 | unpeeled_map = defaultdict(set) |
696 | 200 | conflicts = [] | 255 | conflicts = [] |
697 | 201 | updates = {} | 256 | updates = {} |
698 | 202 | result = dict(to_tags.get_tag_dict()) | 257 | result = dict(to_tags.get_tag_dict()) |
699 | 203 | for ref_name, tag_name, peeled, unpeeled in source_tag_refs: | 258 | for ref_name, tag_name, peeled, unpeeled in source_tag_refs: |
700 | 259 | if selector and not selector(tag_name): | ||
701 | 260 | continue | ||
702 | 204 | if unpeeled is not None: | 261 | if unpeeled is not None: |
703 | 205 | unpeeled_map[peeled].add(unpeeled) | 262 | unpeeled_map[peeled].add(unpeeled) |
704 | 206 | try: | 263 | try: |
706 | 207 | bzr_revid = self.branch.lookup_foreign_revision_id(peeled) | 264 | bzr_revid = self.source.branch.lookup_foreign_revision_id(peeled) |
707 | 208 | except NotCommitError: | 265 | except NotCommitError: |
708 | 209 | continue | 266 | continue |
709 | 210 | if result.get(tag_name) == bzr_revid: | 267 | if result.get(tag_name) == bzr_revid: |
710 | @@ -219,36 +276,20 @@ | |||
711 | 219 | map_file = UnpeelMap.from_repository(to_tags.branch.repository) | 276 | map_file = UnpeelMap.from_repository(to_tags.branch.repository) |
712 | 220 | map_file.update(unpeeled_map) | 277 | map_file.update(unpeeled_map) |
713 | 221 | map_file.save_in_repository(to_tags.branch.repository) | 278 | map_file.save_in_repository(to_tags.branch.repository) |
744 | 222 | return updates, conflicts | 279 | return updates, set(conflicts) |
745 | 223 | 280 | ||
746 | 224 | def merge_to(self, to_tags, overwrite=False, ignore_master=False, | 281 | |
747 | 225 | source_tag_refs=None): | 282 | InterTags.register_optimiser(InterTagsFromGitToRemoteGit) |
748 | 226 | """See Tags.merge_to.""" | 283 | InterTags.register_optimiser(InterTagsFromGitToLocalGit) |
749 | 227 | if source_tag_refs is None: | 284 | InterTags.register_optimiser(InterTagsFromGitToNonGit) |
750 | 228 | source_tag_refs = self.branch.get_tag_refs() | 285 | |
751 | 229 | if self == to_tags: | 286 | |
752 | 230 | return {}, [] | 287 | class GitTags(Tags): |
753 | 231 | if isinstance(to_tags, GitTags): | 288 | """Ref-based tag dictionary.""" |
754 | 232 | return self._merge_to_git(to_tags, source_tag_refs, | 289 | |
755 | 233 | overwrite=overwrite) | 290 | def __init__(self, branch): |
756 | 234 | else: | 291 | self.branch = branch |
757 | 235 | if ignore_master: | 292 | self.repository = branch.repository |
728 | 236 | master = None | ||
729 | 237 | else: | ||
730 | 238 | master = to_tags.branch.get_master_branch() | ||
731 | 239 | with contextlib.ExitStack() as es: | ||
732 | 240 | if master is not None: | ||
733 | 241 | es.enter_context(master.lock_write()) | ||
734 | 242 | updates, conflicts = self._merge_to_non_git( | ||
735 | 243 | to_tags, source_tag_refs, overwrite=overwrite) | ||
736 | 244 | if master is not None: | ||
737 | 245 | extra_updates, extra_conflicts = self.merge_to( | ||
738 | 246 | master.tags, overwrite=overwrite, | ||
739 | 247 | source_tag_refs=source_tag_refs, | ||
740 | 248 | ignore_master=ignore_master) | ||
741 | 249 | updates.update(extra_updates) | ||
742 | 250 | conflicts += extra_conflicts | ||
743 | 251 | return updates, conflicts | ||
758 | 252 | 293 | ||
759 | 253 | def get_tag_dict(self): | 294 | def get_tag_dict(self): |
760 | 254 | ret = {} | 295 | ret = {} |
761 | @@ -262,6 +303,15 @@ | |||
762 | 262 | ret[tag_name] = bzr_revid | 303 | ret[tag_name] = bzr_revid |
763 | 263 | return ret | 304 | return ret |
764 | 264 | 305 | ||
765 | 306 | def lookup_tag(self, tag_name): | ||
766 | 307 | """Return the referent string of a tag""" | ||
767 | 308 | # TODO(jelmer): Replace with something more efficient for local tags. | ||
768 | 309 | td = self.get_tag_dict() | ||
769 | 310 | try: | ||
770 | 311 | return td[tag_name] | ||
771 | 312 | except KeyError: | ||
772 | 313 | raise errors.NoSuchTag(tag_name) | ||
773 | 314 | |||
774 | 265 | 315 | ||
775 | 266 | class LocalGitTagDict(GitTags): | 316 | class LocalGitTagDict(GitTags): |
776 | 267 | """Dictionary with tags in a local repository.""" | 317 | """Dictionary with tags in a local repository.""" |
777 | @@ -612,9 +662,10 @@ | |||
778 | 612 | return revision.NULL_REVISION | 662 | return revision.NULL_REVISION |
779 | 613 | return self.lookup_foreign_revision_id(self.head) | 663 | return self.lookup_foreign_revision_id(self.head) |
780 | 614 | 664 | ||
782 | 615 | def _basic_push(self, target, overwrite=False, stop_revision=None): | 665 | def _basic_push(self, target, overwrite=False, stop_revision=None, |
783 | 666 | tag_selector=None): | ||
784 | 616 | return branch.InterBranch.get(self, target)._basic_push( | 667 | return branch.InterBranch.get(self, target)._basic_push( |
786 | 617 | overwrite, stop_revision) | 668 | overwrite, stop_revision, tag_selector=tag_selector) |
787 | 618 | 669 | ||
788 | 619 | def lookup_foreign_revision_id(self, foreign_revid): | 670 | def lookup_foreign_revision_id(self, foreign_revid): |
789 | 620 | try: | 671 | try: |
790 | @@ -934,7 +985,7 @@ | |||
791 | 934 | stop_revision, fetch_tags=fetch_tags, limit=limit, lossy=lossy) | 985 | stop_revision, fetch_tags=fetch_tags, limit=limit, lossy=lossy) |
792 | 935 | return _mod_repository.FetchResult() | 986 | return _mod_repository.FetchResult() |
793 | 936 | 987 | ||
795 | 937 | def fetch_objects(self, stop_revision, fetch_tags, limit=None, lossy=False): | 988 | def fetch_objects(self, stop_revision, fetch_tags, limit=None, lossy=False, tag_selector=None): |
796 | 938 | interrepo = self._get_interrepo(self.source, self.target) | 989 | interrepo = self._get_interrepo(self.source, self.target) |
797 | 939 | if fetch_tags is None: | 990 | if fetch_tags is None: |
798 | 940 | c = self.source.get_config_stack() | 991 | c = self.source.get_config_stack() |
799 | @@ -952,7 +1003,7 @@ | |||
800 | 952 | else: | 1003 | else: |
801 | 953 | self._last_revid = stop_revision | 1004 | self._last_revid = stop_revision |
802 | 954 | real = interrepo.get_determine_wants_revids( | 1005 | real = interrepo.get_determine_wants_revids( |
804 | 955 | [self._last_revid], include_tags=fetch_tags) | 1006 | [self._last_revid], include_tags=fetch_tags, tag_selector=tag_selector) |
805 | 956 | return real(heads) | 1007 | return real(heads) |
806 | 957 | pack_hint, head, refs = interrepo.fetch_objects( | 1008 | pack_hint, head, refs = interrepo.fetch_objects( |
807 | 958 | determine_wants, self.source.mapping, limit=limit, | 1009 | determine_wants, self.source.mapping, limit=limit, |
808 | @@ -962,8 +1013,8 @@ | |||
809 | 962 | self.target.repository.pack(hint=pack_hint) | 1013 | self.target.repository.pack(hint=pack_hint) |
810 | 963 | return head, refs | 1014 | return head, refs |
811 | 964 | 1015 | ||
814 | 965 | def _update_revisions(self, stop_revision=None, overwrite=False): | 1016 | def _update_revisions(self, stop_revision=None, overwrite=False, tag_selector=None): |
815 | 966 | head, refs = self.fetch_objects(stop_revision, fetch_tags=None) | 1017 | head, refs = self.fetch_objects(stop_revision, fetch_tags=None, tag_selector=tag_selector) |
816 | 967 | if overwrite: | 1018 | if overwrite: |
817 | 968 | prev_last_revid = None | 1019 | prev_last_revid = None |
818 | 969 | else: | 1020 | else: |
819 | @@ -988,7 +1039,7 @@ | |||
820 | 988 | pass | 1039 | pass |
821 | 989 | 1040 | ||
822 | 990 | def _basic_pull(self, stop_revision, overwrite, run_hooks, | 1041 | def _basic_pull(self, stop_revision, overwrite, run_hooks, |
824 | 991 | _override_hook_target, _hook_master): | 1042 | _override_hook_target, _hook_master, tag_selector=None): |
825 | 992 | if overwrite is True: | 1043 | if overwrite is True: |
826 | 993 | overwrite = set(["history", "tags"]) | 1044 | overwrite = set(["history", "tags"]) |
827 | 994 | elif not overwrite: | 1045 | elif not overwrite: |
828 | @@ -1005,7 +1056,8 @@ | |||
829 | 1005 | (result.old_revno, result.old_revid) = \ | 1056 | (result.old_revno, result.old_revid) = \ |
830 | 1006 | self.target.last_revision_info() | 1057 | self.target.last_revision_info() |
831 | 1007 | result.new_git_head, remote_refs = self._update_revisions( | 1058 | result.new_git_head, remote_refs = self._update_revisions( |
833 | 1008 | stop_revision, overwrite=("history" in overwrite)) | 1059 | stop_revision, overwrite=("history" in overwrite), |
834 | 1060 | tag_selector=tag_selector) | ||
835 | 1009 | tags_ret = self.source.tags.merge_to( | 1061 | tags_ret = self.source.tags.merge_to( |
836 | 1010 | self.target.tags, ("tags" in overwrite), ignore_master=True) | 1062 | self.target.tags, ("tags" in overwrite), ignore_master=True) |
837 | 1011 | if isinstance(tags_ret, tuple): | 1063 | if isinstance(tags_ret, tuple): |
838 | @@ -1028,7 +1080,7 @@ | |||
839 | 1028 | 1080 | ||
840 | 1029 | def pull(self, overwrite=False, stop_revision=None, | 1081 | def pull(self, overwrite=False, stop_revision=None, |
841 | 1030 | possible_transports=None, _hook_master=None, run_hooks=True, | 1082 | possible_transports=None, _hook_master=None, run_hooks=True, |
843 | 1031 | _override_hook_target=None, local=False): | 1083 | _override_hook_target=None, local=False, tag_selector=None): |
844 | 1032 | """See Branch.pull. | 1084 | """See Branch.pull. |
845 | 1033 | 1085 | ||
846 | 1034 | :param _hook_master: Private parameter - set the branch to | 1086 | :param _hook_master: Private parameter - set the branch to |
847 | @@ -1066,9 +1118,10 @@ | |||
848 | 1066 | master_branch = None | 1118 | master_branch = None |
849 | 1067 | return self._basic_pull(stop_revision, overwrite, run_hooks, | 1119 | return self._basic_pull(stop_revision, overwrite, run_hooks, |
850 | 1068 | _override_hook_target, | 1120 | _override_hook_target, |
852 | 1069 | _hook_master=master_branch) | 1121 | _hook_master=master_branch, |
853 | 1122 | tag_selector=tag_selector) | ||
854 | 1070 | 1123 | ||
856 | 1071 | def _basic_push(self, overwrite, stop_revision): | 1124 | def _basic_push(self, overwrite, stop_revision, tag_selector=None): |
857 | 1072 | if overwrite is True: | 1125 | if overwrite is True: |
858 | 1073 | overwrite = set(["history", "tags"]) | 1126 | overwrite = set(["history", "tags"]) |
859 | 1074 | elif not overwrite: | 1127 | elif not overwrite: |
860 | @@ -1078,9 +1131,11 @@ | |||
861 | 1078 | result.target_branch = self.target | 1131 | result.target_branch = self.target |
862 | 1079 | result.old_revno, result.old_revid = self.target.last_revision_info() | 1132 | result.old_revno, result.old_revid = self.target.last_revision_info() |
863 | 1080 | result.new_git_head, remote_refs = self._update_revisions( | 1133 | result.new_git_head, remote_refs = self._update_revisions( |
865 | 1081 | stop_revision, overwrite=("history" in overwrite)) | 1134 | stop_revision, overwrite=("history" in overwrite), |
866 | 1135 | tag_selector=tag_selector) | ||
867 | 1082 | tags_ret = self.source.tags.merge_to( | 1136 | tags_ret = self.source.tags.merge_to( |
869 | 1083 | self.target.tags, "tags" in overwrite, ignore_master=True) | 1137 | self.target.tags, "tags" in overwrite, ignore_master=True, |
870 | 1138 | selector=tag_selector) | ||
871 | 1084 | (result.tag_updates, result.tag_conflicts) = tags_ret | 1139 | (result.tag_updates, result.tag_conflicts) = tags_ret |
872 | 1085 | result.new_revno, result.new_revid = self.target.last_revision_info() | 1140 | result.new_revno, result.new_revid = self.target.last_revision_info() |
873 | 1086 | self.update_references(revid=result.new_revid) | 1141 | self.update_references(revid=result.new_revid) |
874 | @@ -1109,7 +1164,7 @@ | |||
875 | 1109 | return (isinstance(source, LocalGitBranch) and | 1164 | return (isinstance(source, LocalGitBranch) and |
876 | 1110 | isinstance(target, RemoteGitBranch)) | 1165 | isinstance(target, RemoteGitBranch)) |
877 | 1111 | 1166 | ||
879 | 1112 | def _basic_push(self, overwrite, stop_revision): | 1167 | def _basic_push(self, overwrite, stop_revision, tag_selector=None): |
880 | 1113 | result = GitBranchPushResult() | 1168 | result = GitBranchPushResult() |
881 | 1114 | result.source_branch = self.source | 1169 | result.source_branch = self.source |
882 | 1115 | result.target_branch = self.target | 1170 | result.target_branch = self.target |
883 | @@ -1134,6 +1189,8 @@ | |||
884 | 1134 | result.new_revid = stop_revision | 1189 | result.new_revid = stop_revision |
885 | 1135 | for name, sha in ( | 1190 | for name, sha in ( |
886 | 1136 | self.source.repository._git.refs.as_dict(b"refs/tags").items()): | 1191 | self.source.repository._git.refs.as_dict(b"refs/tags").items()): |
887 | 1192 | if tag_selector and not tag_selector(name): | ||
888 | 1193 | continue | ||
889 | 1137 | if sha not in self.source.repository._git: | 1194 | if sha not in self.source.repository._git: |
890 | 1138 | trace.mutter('Ignoring missing SHA: %s', sha) | 1195 | trace.mutter('Ignoring missing SHA: %s', sha) |
891 | 1139 | continue | 1196 | continue |
892 | @@ -1173,7 +1230,7 @@ | |||
893 | 1173 | interrepo.fetch_objects(determine_wants, limit=limit, lossy=lossy) | 1230 | interrepo.fetch_objects(determine_wants, limit=limit, lossy=lossy) |
894 | 1174 | return _mod_repository.FetchResult() | 1231 | return _mod_repository.FetchResult() |
895 | 1175 | 1232 | ||
897 | 1176 | def _basic_push(self, overwrite=False, stop_revision=None): | 1233 | def _basic_push(self, overwrite=False, stop_revision=None, tag_selector=None): |
898 | 1177 | if overwrite is True: | 1234 | if overwrite is True: |
899 | 1178 | overwrite = set(["history", "tags"]) | 1235 | overwrite = set(["history", "tags"]) |
900 | 1179 | elif not overwrite: | 1236 | elif not overwrite: |
901 | @@ -1189,8 +1246,8 @@ | |||
902 | 1189 | other_branch=self.source) | 1246 | other_branch=self.source) |
903 | 1190 | tags_ret = self.source.tags.merge_to( | 1247 | tags_ret = self.source.tags.merge_to( |
904 | 1191 | self.target.tags, | 1248 | self.target.tags, |
907 | 1192 | source_tag_refs=remote_refs_dict_to_tag_refs(refs), | 1249 | overwrite=("tags" in overwrite), |
908 | 1193 | overwrite=("tags" in overwrite)) | 1250 | selector=tag_selector) |
909 | 1194 | if isinstance(tags_ret, tuple): | 1251 | if isinstance(tags_ret, tuple): |
910 | 1195 | (result.tag_updates, result.tag_conflicts) = tags_ret | 1252 | (result.tag_updates, result.tag_conflicts) = tags_ret |
911 | 1196 | else: | 1253 | else: |
912 | @@ -1218,7 +1275,8 @@ | |||
913 | 1218 | return result.refs, stop_revision | 1275 | return result.refs, stop_revision |
914 | 1219 | 1276 | ||
915 | 1220 | def pull(self, stop_revision=None, overwrite=False, | 1277 | def pull(self, stop_revision=None, overwrite=False, |
917 | 1221 | possible_transports=None, run_hooks=True, local=False): | 1278 | possible_transports=None, run_hooks=True, local=False, |
918 | 1279 | tag_selector=None): | ||
919 | 1222 | # This type of branch can't be bound. | 1280 | # This type of branch can't be bound. |
920 | 1223 | if local: | 1281 | if local: |
921 | 1224 | raise errors.LocalRequiresBoundBranch() | 1282 | raise errors.LocalRequiresBoundBranch() |
922 | @@ -1239,7 +1297,7 @@ | |||
923 | 1239 | other_branch=self.source) | 1297 | other_branch=self.source) |
924 | 1240 | tags_ret = self.source.tags.merge_to( | 1298 | tags_ret = self.source.tags.merge_to( |
925 | 1241 | self.target.tags, overwrite=("tags" in overwrite), | 1299 | self.target.tags, overwrite=("tags" in overwrite), |
927 | 1242 | source_tag_refs=remote_refs_dict_to_tag_refs(refs)) | 1300 | selector=tag_selector) |
928 | 1243 | if isinstance(tags_ret, tuple): | 1301 | if isinstance(tags_ret, tuple): |
929 | 1244 | (result.tag_updates, result.tag_conflicts) = tags_ret | 1302 | (result.tag_updates, result.tag_conflicts) = tags_ret |
930 | 1245 | else: | 1303 | else: |
931 | @@ -1307,7 +1365,7 @@ | |||
932 | 1307 | refs[ref] = (None, revid) | 1365 | refs[ref] = (None, revid) |
933 | 1308 | return refs, main_ref, (stop_revno, stop_revision) | 1366 | return refs, main_ref, (stop_revno, stop_revision) |
934 | 1309 | 1367 | ||
936 | 1310 | def _update_refs(self, result, old_refs, new_refs, overwrite): | 1368 | def _update_refs(self, result, old_refs, new_refs, overwrite, tag_selector): |
937 | 1311 | mutter("updating refs. old refs: %r, new refs: %r", | 1369 | mutter("updating refs. old refs: %r, new refs: %r", |
938 | 1312 | old_refs, new_refs) | 1370 | old_refs, new_refs) |
939 | 1313 | result.tag_updates = {} | 1371 | result.tag_updates = {} |
940 | @@ -1346,6 +1404,8 @@ | |||
941 | 1346 | except ValueError: | 1404 | except ValueError: |
942 | 1347 | pass | 1405 | pass |
943 | 1348 | else: | 1406 | else: |
944 | 1407 | if tag_selector and not tag_selector(tag_name): | ||
945 | 1408 | continue | ||
946 | 1349 | result.tag_updates[tag_name] = revid | 1409 | result.tag_updates[tag_name] = revid |
947 | 1350 | ret[ref] = (git_sha, revid) | 1410 | ret[ref] = (git_sha, revid) |
948 | 1351 | else: | 1411 | else: |
949 | @@ -1381,7 +1441,8 @@ | |||
950 | 1381 | for (old_revid, (new_sha, new_revid)) in revidmap.items()}) | 1441 | for (old_revid, (new_sha, new_revid)) in revidmap.items()}) |
951 | 1382 | 1442 | ||
952 | 1383 | def pull(self, overwrite=False, stop_revision=None, local=False, | 1443 | def pull(self, overwrite=False, stop_revision=None, local=False, |
954 | 1384 | possible_transports=None, run_hooks=True, _stop_revno=None): | 1444 | possible_transports=None, run_hooks=True, _stop_revno=None, |
955 | 1445 | tag_selector=None): | ||
956 | 1385 | result = GitBranchPullResult() | 1446 | result = GitBranchPullResult() |
957 | 1386 | result.source_branch = self.source | 1447 | result.source_branch = self.source |
958 | 1387 | result.target_branch = self.target | 1448 | result.target_branch = self.target |
959 | @@ -1390,7 +1451,7 @@ | |||
960 | 1390 | stop_revision, stop_revno=_stop_revno) | 1451 | stop_revision, stop_revno=_stop_revno) |
961 | 1391 | 1452 | ||
962 | 1392 | def update_refs(old_refs): | 1453 | def update_refs(old_refs): |
964 | 1393 | return self._update_refs(result, old_refs, new_refs, overwrite) | 1454 | return self._update_refs(result, old_refs, new_refs, overwrite, tag_selector) |
965 | 1394 | try: | 1455 | try: |
966 | 1395 | result.revidmap, old_refs, new_refs = ( | 1456 | result.revidmap, old_refs, new_refs = ( |
967 | 1396 | self.interrepo.fetch_refs(update_refs, lossy=False)) | 1457 | self.interrepo.fetch_refs(update_refs, lossy=False)) |
968 | @@ -1410,7 +1471,8 @@ | |||
969 | 1410 | return result | 1471 | return result |
970 | 1411 | 1472 | ||
971 | 1412 | def push(self, overwrite=False, stop_revision=None, lossy=False, | 1473 | def push(self, overwrite=False, stop_revision=None, lossy=False, |
973 | 1413 | _override_hook_source_branch=None, _stop_revno=None): | 1474 | _override_hook_source_branch=None, _stop_revno=None, |
974 | 1475 | tag_selector=None): | ||
975 | 1414 | result = GitBranchPushResult() | 1476 | result = GitBranchPushResult() |
976 | 1415 | result.source_branch = self.source | 1477 | result.source_branch = self.source |
977 | 1416 | result.target_branch = self.target | 1478 | result.target_branch = self.target |
978 | @@ -1421,7 +1483,7 @@ | |||
979 | 1421 | stop_revision, stop_revno=_stop_revno) | 1483 | stop_revision, stop_revno=_stop_revno) |
980 | 1422 | 1484 | ||
981 | 1423 | def update_refs(old_refs): | 1485 | def update_refs(old_refs): |
983 | 1424 | return self._update_refs(result, old_refs, new_refs, overwrite) | 1486 | return self._update_refs(result, old_refs, new_refs, overwrite, tag_selector) |
984 | 1425 | try: | 1487 | try: |
985 | 1426 | result.revidmap, old_refs, new_refs = ( | 1488 | result.revidmap, old_refs, new_refs = ( |
986 | 1427 | self.interrepo.fetch_refs( | 1489 | self.interrepo.fetch_refs( |
987 | 1428 | 1490 | ||
988 | === modified file 'breezy/git/dir.py' | |||
989 | --- breezy/git/dir.py 2020-02-18 01:57:45 +0000 | |||
990 | +++ breezy/git/dir.py 2020-02-21 04:09:29 +0000 | |||
991 | @@ -218,7 +218,8 @@ | |||
992 | 218 | def clone_on_transport(self, transport, revision_id=None, | 218 | def clone_on_transport(self, transport, revision_id=None, |
993 | 219 | force_new_repo=False, preserve_stacking=False, | 219 | force_new_repo=False, preserve_stacking=False, |
994 | 220 | stacked_on=None, create_prefix=False, | 220 | stacked_on=None, create_prefix=False, |
996 | 221 | use_existing_dir=True, no_tree=False): | 221 | use_existing_dir=True, no_tree=False, |
997 | 222 | tag_selector=None): | ||
998 | 222 | """See ControlDir.clone_on_transport.""" | 223 | """See ControlDir.clone_on_transport.""" |
999 | 223 | from ..repository import InterRepository | 224 | from ..repository import InterRepository |
1000 | 224 | from .mapping import default_mapping | 225 | from .mapping import default_mapping |
1001 | @@ -240,7 +241,7 @@ | |||
1002 | 240 | interrepo = InterRepository.get(source_repo, target_repo) | 241 | interrepo = InterRepository.get(source_repo, target_repo) |
1003 | 241 | if revision_id is not None: | 242 | if revision_id is not None: |
1004 | 242 | determine_wants = interrepo.get_determine_wants_revids( | 243 | determine_wants = interrepo.get_determine_wants_revids( |
1006 | 243 | [revision_id], include_tags=True) | 244 | [revision_id], include_tags=True, tag_selector=tag_selector) |
1007 | 244 | else: | 245 | else: |
1008 | 245 | determine_wants = interrepo.determine_wants_all | 246 | determine_wants = interrepo.determine_wants_all |
1009 | 246 | (pack_hint, _, refs) = interrepo.fetch_objects(determine_wants, | 247 | (pack_hint, _, refs) = interrepo.fetch_objects(determine_wants, |
1010 | @@ -312,7 +313,7 @@ | |||
1011 | 312 | 313 | ||
1012 | 313 | def push_branch(self, source, revision_id=None, overwrite=False, | 314 | def push_branch(self, source, revision_id=None, overwrite=False, |
1013 | 314 | remember=False, create_prefix=False, lossy=False, | 315 | remember=False, create_prefix=False, lossy=False, |
1015 | 315 | name=None): | 316 | name=None, tag_selector=None): |
1016 | 316 | """Push the source branch into this ControlDir.""" | 317 | """Push the source branch into this ControlDir.""" |
1017 | 317 | push_result = GitPushResult() | 318 | push_result = GitPushResult() |
1018 | 318 | push_result.workingtree_updated = None | 319 | push_result.workingtree_updated = None |
1019 | @@ -325,7 +326,7 @@ | |||
1020 | 325 | target = self.open_branch(name, nascent_ok=True) | 326 | target = self.open_branch(name, nascent_ok=True) |
1021 | 326 | push_result.branch_push_result = source.push( | 327 | push_result.branch_push_result = source.push( |
1022 | 327 | target, overwrite=overwrite, stop_revision=revision_id, | 328 | target, overwrite=overwrite, stop_revision=revision_id, |
1024 | 328 | lossy=lossy) | 329 | lossy=lossy, tag_selector=tag_selector) |
1025 | 329 | push_result.new_revid = push_result.branch_push_result.new_revid | 330 | push_result.new_revid = push_result.branch_push_result.new_revid |
1026 | 330 | push_result.old_revid = push_result.branch_push_result.old_revid | 331 | push_result.old_revid = push_result.branch_push_result.old_revid |
1027 | 331 | try: | 332 | try: |
1028 | 332 | 333 | ||
1029 | === modified file 'breezy/git/interrepo.py' | |||
1030 | --- breezy/git/interrepo.py 2020-02-18 01:57:45 +0000 | |||
1031 | +++ breezy/git/interrepo.py 2020-02-21 04:09:29 +0000 | |||
1032 | @@ -75,6 +75,7 @@ | |||
1033 | 75 | ) | 75 | ) |
1034 | 76 | from .refs import ( | 76 | from .refs import ( |
1035 | 77 | is_tag, | 77 | is_tag, |
1036 | 78 | ref_to_tag_name, | ||
1037 | 78 | ) | 79 | ) |
1038 | 79 | from .repository import ( | 80 | from .repository import ( |
1039 | 80 | GitRepository, | 81 | GitRepository, |
1040 | @@ -403,7 +404,7 @@ | |||
1041 | 403 | def _target_has_shas(self, shas): | 404 | def _target_has_shas(self, shas): |
1042 | 404 | raise NotImplementedError(self._target_has_shas) | 405 | raise NotImplementedError(self._target_has_shas) |
1043 | 405 | 406 | ||
1045 | 406 | def get_determine_wants_heads(self, wants, include_tags=False): | 407 | def get_determine_wants_heads(self, wants, include_tags=False, tag_selector=None): |
1046 | 407 | wants = set(wants) | 408 | wants = set(wants) |
1047 | 408 | 409 | ||
1048 | 409 | def determine_wants(refs): | 410 | def determine_wants(refs): |
1049 | @@ -416,7 +417,11 @@ | |||
1050 | 416 | for k, sha in refs.items(): | 417 | for k, sha in refs.items(): |
1051 | 417 | if k.endswith(ANNOTATED_TAG_SUFFIX): | 418 | if k.endswith(ANNOTATED_TAG_SUFFIX): |
1052 | 418 | continue | 419 | continue |
1054 | 419 | if not is_tag(k): | 420 | try: |
1055 | 421 | tag_name = ref_to_tag_name(k) | ||
1056 | 422 | except ValueError: | ||
1057 | 423 | continue | ||
1058 | 424 | if tag_selector and not tag_selector(tag_name): | ||
1059 | 420 | continue | 425 | continue |
1060 | 421 | if sha == ZERO_SHA: | 426 | if sha == ZERO_SHA: |
1061 | 422 | continue | 427 | continue |
1062 | @@ -502,14 +507,15 @@ | |||
1063 | 502 | """ | 507 | """ |
1064 | 503 | raise NotImplementedError(self.fetch_objects) | 508 | raise NotImplementedError(self.fetch_objects) |
1065 | 504 | 509 | ||
1067 | 505 | def get_determine_wants_revids(self, revids, include_tags=False): | 510 | def get_determine_wants_revids(self, revids, include_tags=False, tag_selector=None): |
1068 | 506 | wants = set() | 511 | wants = set() |
1069 | 507 | for revid in set(revids): | 512 | for revid in set(revids): |
1070 | 508 | if self.target.has_revision(revid): | 513 | if self.target.has_revision(revid): |
1071 | 509 | continue | 514 | continue |
1072 | 510 | git_sha, mapping = self.source.lookup_bzr_revision_id(revid) | 515 | git_sha, mapping = self.source.lookup_bzr_revision_id(revid) |
1073 | 511 | wants.add(git_sha) | 516 | wants.add(git_sha) |
1075 | 512 | return self.get_determine_wants_heads(wants, include_tags=include_tags) | 517 | return self.get_determine_wants_heads( |
1076 | 518 | wants, include_tags=include_tags, tag_selector=tag_selector) | ||
1077 | 513 | 519 | ||
1078 | 514 | def fetch(self, revision_id=None, find_ghosts=False, | 520 | def fetch(self, revision_id=None, find_ghosts=False, |
1079 | 515 | mapping=None, fetch_spec=None, include_tags=False, lossy=False): | 521 | mapping=None, fetch_spec=None, include_tags=False, lossy=False): |
1080 | @@ -682,14 +688,14 @@ | |||
1081 | 682 | result.refs = wants_recorder.remote_refs | 688 | result.refs = wants_recorder.remote_refs |
1082 | 683 | return result | 689 | return result |
1083 | 684 | 690 | ||
1085 | 685 | def get_determine_wants_revids(self, revids, include_tags=False): | 691 | def get_determine_wants_revids(self, revids, include_tags=False, tag_selector=None): |
1086 | 686 | wants = set() | 692 | wants = set() |
1087 | 687 | for revid in set(revids): | 693 | for revid in set(revids): |
1088 | 688 | if revid == NULL_REVISION: | 694 | if revid == NULL_REVISION: |
1089 | 689 | continue | 695 | continue |
1090 | 690 | git_sha, mapping = self.source.lookup_bzr_revision_id(revid) | 696 | git_sha, mapping = self.source.lookup_bzr_revision_id(revid) |
1091 | 691 | wants.add(git_sha) | 697 | wants.add(git_sha) |
1093 | 692 | return self.get_determine_wants_heads(wants, include_tags=include_tags) | 698 | return self.get_determine_wants_heads(wants, include_tags=include_tags, tag_selector=tag_selector) |
1094 | 693 | 699 | ||
1095 | 694 | def get_determine_wants_branches(self, branches, include_tags=False): | 700 | def get_determine_wants_branches(self, branches, include_tags=False): |
1096 | 695 | def determine_wants(refs): | 701 | def determine_wants(refs): |
1097 | 696 | 702 | ||
1098 | === modified file 'breezy/git/workingtree.py' | |||
1099 | --- breezy/git/workingtree.py 2020-02-18 01:57:45 +0000 | |||
1100 | +++ breezy/git/workingtree.py 2020-02-21 04:09:29 +0000 | |||
1101 | @@ -1210,12 +1210,12 @@ | |||
1102 | 1210 | 1210 | ||
1103 | 1211 | def pull(self, source, overwrite=False, stop_revision=None, | 1211 | def pull(self, source, overwrite=False, stop_revision=None, |
1104 | 1212 | change_reporter=None, possible_transports=None, local=False, | 1212 | change_reporter=None, possible_transports=None, local=False, |
1106 | 1213 | show_base=False): | 1213 | show_base=False, tag_selector=None): |
1107 | 1214 | with self.lock_write(), source.lock_read(): | 1214 | with self.lock_write(), source.lock_read(): |
1108 | 1215 | old_revision = self.branch.last_revision() | 1215 | old_revision = self.branch.last_revision() |
1109 | 1216 | count = self.branch.pull(source, overwrite, stop_revision, | 1216 | count = self.branch.pull(source, overwrite, stop_revision, |
1110 | 1217 | possible_transports=possible_transports, | 1217 | possible_transports=possible_transports, |
1112 | 1218 | local=local) | 1218 | local=local, tag_selector=tag_selector) |
1113 | 1219 | self._update_git_tree( | 1219 | self._update_git_tree( |
1114 | 1220 | old_revision=old_revision, | 1220 | old_revision=old_revision, |
1115 | 1221 | new_revision=self.branch.last_revision(), | 1221 | new_revision=self.branch.last_revision(), |
1116 | 1222 | 1222 | ||
1117 | === modified file 'breezy/plugins/propose/github.py' | |||
1118 | --- breezy/plugins/propose/github.py 2020-02-18 01:57:45 +0000 | |||
1119 | +++ breezy/plugins/propose/github.py 2020-02-21 04:09:29 +0000 | |||
1120 | @@ -275,11 +275,12 @@ | |||
1121 | 275 | return json.loads(response.text) | 275 | return json.loads(response.text) |
1122 | 276 | raise InvalidHttpResponse(path, response.text) | 276 | raise InvalidHttpResponse(path, response.text) |
1123 | 277 | 277 | ||
1125 | 278 | def _create_pull(self, path, title, head, base, body=None, labels=None, assignee=None): | 278 | def _create_pull(self, path, title, head, base, body=None, labels=None, assignee=None, draft=False): |
1126 | 279 | data = { | 279 | data = { |
1127 | 280 | 'title': title, | 280 | 'title': title, |
1128 | 281 | 'head': head, | 281 | 'head': head, |
1129 | 282 | 'base': base, | 282 | 'base': base, |
1130 | 283 | 'draft': draft, | ||
1131 | 283 | } | 284 | } |
1132 | 284 | if labels is not None: | 285 | if labels is not None: |
1133 | 285 | data['labels'] = labels | 286 | data['labels'] = labels |
1134 | @@ -373,7 +374,7 @@ | |||
1135 | 373 | 374 | ||
1136 | 374 | def publish_derived(self, local_branch, base_branch, name, project=None, | 375 | def publish_derived(self, local_branch, base_branch, name, project=None, |
1137 | 375 | owner=None, revision_id=None, overwrite=False, | 376 | owner=None, revision_id=None, overwrite=False, |
1139 | 376 | allow_lossy=True): | 377 | allow_lossy=True, tag_selector=None): |
1140 | 377 | base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch) | 378 | base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch) |
1141 | 378 | base_repo = self._get_repo(base_owner, base_project) | 379 | base_repo = self._get_repo(base_owner, base_project) |
1142 | 379 | if owner is None: | 380 | if owner is None: |
1143 | @@ -393,13 +394,14 @@ | |||
1144 | 393 | try: | 394 | try: |
1145 | 394 | push_result = remote_dir.push_branch( | 395 | push_result = remote_dir.push_branch( |
1146 | 395 | local_branch, revision_id=revision_id, overwrite=overwrite, | 396 | local_branch, revision_id=revision_id, overwrite=overwrite, |
1148 | 396 | name=name) | 397 | name=name, tag_selector=tag_selector) |
1149 | 397 | except errors.NoRoundtrippingSupport: | 398 | except errors.NoRoundtrippingSupport: |
1150 | 398 | if not allow_lossy: | 399 | if not allow_lossy: |
1151 | 399 | raise | 400 | raise |
1152 | 400 | push_result = remote_dir.push_branch( | 401 | push_result = remote_dir.push_branch( |
1153 | 401 | local_branch, revision_id=revision_id, | 402 | local_branch, revision_id=revision_id, |
1155 | 402 | overwrite=overwrite, name=name, lossy=True) | 403 | overwrite=overwrite, name=name, lossy=True, |
1156 | 404 | tag_selector=tag_selector) | ||
1157 | 403 | return push_result.target_branch, github_url_to_bzr_url( | 405 | return push_result.target_branch, github_url_to_bzr_url( |
1158 | 404 | remote_repo['html_url'], name) | 406 | remote_repo['html_url'], name) |
1159 | 405 | 407 | ||
1160 | 406 | 408 | ||
1161 | === modified file 'breezy/plugins/propose/gitlabs.py' | |||
1162 | --- breezy/plugins/propose/gitlabs.py 2020-02-18 01:57:45 +0000 | |||
1163 | +++ breezy/plugins/propose/gitlabs.py 2020-02-21 04:09:29 +0000 | |||
1164 | @@ -413,7 +413,7 @@ | |||
1165 | 413 | 413 | ||
1166 | 414 | def publish_derived(self, local_branch, base_branch, name, project=None, | 414 | def publish_derived(self, local_branch, base_branch, name, project=None, |
1167 | 415 | owner=None, revision_id=None, overwrite=False, | 415 | owner=None, revision_id=None, overwrite=False, |
1169 | 416 | allow_lossy=True): | 416 | allow_lossy=True, tag_selector=None): |
1170 | 417 | (host, base_project, base_branch_name) = parse_gitlab_branch_url(base_branch) | 417 | (host, base_project, base_branch_name) = parse_gitlab_branch_url(base_branch) |
1171 | 418 | if owner is None: | 418 | if owner is None: |
1172 | 419 | owner = self._get_logged_in_username() | 419 | owner = self._get_logged_in_username() |
1173 | @@ -428,13 +428,13 @@ | |||
1174 | 428 | try: | 428 | try: |
1175 | 429 | push_result = remote_dir.push_branch( | 429 | push_result = remote_dir.push_branch( |
1176 | 430 | local_branch, revision_id=revision_id, overwrite=overwrite, | 430 | local_branch, revision_id=revision_id, overwrite=overwrite, |
1178 | 431 | name=name) | 431 | name=name, tag_selector=tag_selector) |
1179 | 432 | except errors.NoRoundtrippingSupport: | 432 | except errors.NoRoundtrippingSupport: |
1180 | 433 | if not allow_lossy: | 433 | if not allow_lossy: |
1181 | 434 | raise | 434 | raise |
1182 | 435 | push_result = remote_dir.push_branch( | 435 | push_result = remote_dir.push_branch( |
1183 | 436 | local_branch, revision_id=revision_id, overwrite=overwrite, | 436 | local_branch, revision_id=revision_id, overwrite=overwrite, |
1185 | 437 | name=name, lossy=True) | 437 | name=name, lossy=True, tag_selector=tag_selector) |
1186 | 438 | public_url = gitlab_url_to_bzr_url( | 438 | public_url = gitlab_url_to_bzr_url( |
1187 | 439 | target_project['http_url_to_repo'], name) | 439 | target_project['http_url_to_repo'], name) |
1188 | 440 | return push_result.target_branch, public_url | 440 | return push_result.target_branch, public_url |
1189 | 441 | 441 | ||
1190 | === modified file 'breezy/plugins/propose/launchpad.py' | |||
1191 | --- breezy/plugins/propose/launchpad.py 2020-02-18 01:57:45 +0000 | |||
1192 | +++ breezy/plugins/propose/launchpad.py 2020-02-21 04:09:29 +0000 | |||
1193 | @@ -258,7 +258,8 @@ | |||
1194 | 258 | return "~%s/%s" % (owner, project) | 258 | return "~%s/%s" % (owner, project) |
1195 | 259 | 259 | ||
1196 | 260 | def _publish_git(self, local_branch, base_path, name, owner, project=None, | 260 | def _publish_git(self, local_branch, base_path, name, owner, project=None, |
1198 | 261 | revision_id=None, overwrite=False, allow_lossy=True): | 261 | revision_id=None, overwrite=False, allow_lossy=True, |
1199 | 262 | tag_selector=None): | ||
1200 | 262 | to_path = self._get_derived_git_path(base_path, owner, project) | 263 | to_path = self._get_derived_git_path(base_path, owner, project) |
1201 | 263 | to_transport = get_transport("git+ssh://git.launchpad.net/" + to_path) | 264 | to_transport = get_transport("git+ssh://git.launchpad.net/" + to_path) |
1202 | 264 | try: | 265 | try: |
1203 | @@ -270,21 +271,23 @@ | |||
1204 | 270 | if dir_to is None: | 271 | if dir_to is None: |
1205 | 271 | try: | 272 | try: |
1206 | 272 | br_to = local_branch.create_clone_on_transport( | 273 | br_to = local_branch.create_clone_on_transport( |
1208 | 273 | to_transport, revision_id=revision_id, name=name) | 274 | to_transport, revision_id=revision_id, name=name, |
1209 | 275 | tag_selector=tag_selector) | ||
1210 | 274 | except errors.NoRoundtrippingSupport: | 276 | except errors.NoRoundtrippingSupport: |
1211 | 275 | br_to = local_branch.create_clone_on_transport( | 277 | br_to = local_branch.create_clone_on_transport( |
1212 | 276 | to_transport, revision_id=revision_id, name=name, | 278 | to_transport, revision_id=revision_id, name=name, |
1214 | 277 | lossy=True) | 279 | lossy=True, tag_selector=tag_selector) |
1215 | 278 | else: | 280 | else: |
1216 | 279 | try: | 281 | try: |
1217 | 280 | dir_to = dir_to.push_branch( | 282 | dir_to = dir_to.push_branch( |
1219 | 281 | local_branch, revision_id, overwrite=overwrite, name=name) | 283 | local_branch, revision_id, overwrite=overwrite, name=name, |
1220 | 284 | tag_selector=tag_selector) | ||
1221 | 282 | except errors.NoRoundtrippingSupport: | 285 | except errors.NoRoundtrippingSupport: |
1222 | 283 | if not allow_lossy: | 286 | if not allow_lossy: |
1223 | 284 | raise | 287 | raise |
1224 | 285 | dir_to = dir_to.push_branch( | 288 | dir_to = dir_to.push_branch( |
1225 | 286 | local_branch, revision_id, overwrite=overwrite, name=name, | 289 | local_branch, revision_id, overwrite=overwrite, name=name, |
1227 | 287 | lossy=True) | 290 | lossy=True, tag_selector=tag_selector) |
1228 | 288 | br_to = dir_to.target_branch | 291 | br_to = dir_to.target_branch |
1229 | 289 | return br_to, ( | 292 | return br_to, ( |
1230 | 290 | "https://git.launchpad.net/%s/+ref/%s" % (to_path, name)) | 293 | "https://git.launchpad.net/%s/+ref/%s" % (to_path, name)) |
1231 | @@ -310,7 +313,7 @@ | |||
1232 | 310 | 313 | ||
1233 | 311 | def _publish_bzr(self, local_branch, base_branch, name, owner, | 314 | def _publish_bzr(self, local_branch, base_branch, name, owner, |
1234 | 312 | project=None, revision_id=None, overwrite=False, | 315 | project=None, revision_id=None, overwrite=False, |
1236 | 313 | allow_lossy=True): | 316 | allow_lossy=True, tag_selector=None): |
1237 | 314 | to_path = self._get_derived_bzr_path(base_branch, name, owner, project) | 317 | to_path = self._get_derived_bzr_path(base_branch, name, owner, project) |
1238 | 315 | to_transport = get_transport("lp:" + to_path) | 318 | to_transport = get_transport("lp:" + to_path) |
1239 | 316 | try: | 319 | try: |
1240 | @@ -321,10 +324,11 @@ | |||
1241 | 321 | 324 | ||
1242 | 322 | if dir_to is None: | 325 | if dir_to is None: |
1243 | 323 | br_to = local_branch.create_clone_on_transport( | 326 | br_to = local_branch.create_clone_on_transport( |
1245 | 324 | to_transport, revision_id=revision_id) | 327 | to_transport, revision_id=revision_id, tag_selector=tag_selector) |
1246 | 325 | else: | 328 | else: |
1247 | 326 | br_to = dir_to.push_branch( | 329 | br_to = dir_to.push_branch( |
1249 | 327 | local_branch, revision_id, overwrite=overwrite).target_branch | 330 | local_branch, revision_id, overwrite=overwrite, |
1250 | 331 | tag_selector=tag_selector).target_branch | ||
1251 | 328 | return br_to, ("https://code.launchpad.net/" + to_path) | 332 | return br_to, ("https://code.launchpad.net/" + to_path) |
1252 | 329 | 333 | ||
1253 | 330 | def _split_url(self, url): | 334 | def _split_url(self, url): |
1254 | @@ -341,7 +345,7 @@ | |||
1255 | 341 | 345 | ||
1256 | 342 | def publish_derived(self, local_branch, base_branch, name, project=None, | 346 | def publish_derived(self, local_branch, base_branch, name, project=None, |
1257 | 343 | owner=None, revision_id=None, overwrite=False, | 347 | owner=None, revision_id=None, overwrite=False, |
1259 | 344 | allow_lossy=True): | 348 | allow_lossy=True, tag_selector=None): |
1260 | 345 | """Publish a branch to the site, derived from base_branch. | 349 | """Publish a branch to the site, derived from base_branch. |
1261 | 346 | 350 | ||
1262 | 347 | :param base_branch: branch to derive the new branch from | 351 | :param base_branch: branch to derive the new branch from |
1263 | @@ -360,12 +364,12 @@ | |||
1264 | 360 | return self._publish_bzr( | 364 | return self._publish_bzr( |
1265 | 361 | local_branch, base_branch, name, project=project, owner=owner, | 365 | local_branch, base_branch, name, project=project, owner=owner, |
1266 | 362 | revision_id=revision_id, overwrite=overwrite, | 366 | revision_id=revision_id, overwrite=overwrite, |
1268 | 363 | allow_lossy=allow_lossy) | 367 | allow_lossy=allow_lossy, tag_selector=tag_selector) |
1269 | 364 | elif base_vcs == 'git': | 368 | elif base_vcs == 'git': |
1270 | 365 | return self._publish_git( | 369 | return self._publish_git( |
1271 | 366 | local_branch, base_path, name, project=project, owner=owner, | 370 | local_branch, base_path, name, project=project, owner=owner, |
1272 | 367 | revision_id=revision_id, overwrite=overwrite, | 371 | revision_id=revision_id, overwrite=overwrite, |
1274 | 368 | allow_lossy=allow_lossy) | 372 | allow_lossy=allow_lossy, tag_selector=tag_selector) |
1275 | 369 | else: | 373 | else: |
1276 | 370 | raise AssertionError('not a valid Launchpad URL') | 374 | raise AssertionError('not a valid Launchpad URL') |
1277 | 371 | 375 | ||
1278 | 372 | 376 | ||
1279 | === modified file 'breezy/plugins/weave_fmt/bzrdir.py' | |||
1280 | --- breezy/plugins/weave_fmt/bzrdir.py 2020-02-18 01:57:45 +0000 | |||
1281 | +++ breezy/plugins/weave_fmt/bzrdir.py 2020-02-21 04:09:29 +0000 | |||
1282 | @@ -745,7 +745,7 @@ | |||
1283 | 745 | return self._format.__class__() | 745 | return self._format.__class__() |
1284 | 746 | 746 | ||
1285 | 747 | def clone(self, url, revision_id=None, force_new_repo=False, | 747 | def clone(self, url, revision_id=None, force_new_repo=False, |
1287 | 748 | preserve_stacking=False): | 748 | preserve_stacking=False, tag_selector=None): |
1288 | 749 | """See ControlDir.clone(). | 749 | """See ControlDir.clone(). |
1289 | 750 | 750 | ||
1290 | 751 | force_new_repo has no effect, since this family of formats always | 751 | force_new_repo has no effect, since this family of formats always |
1291 | @@ -757,7 +757,7 @@ | |||
1292 | 757 | result = self._format._initialize_for_clone(url) | 757 | result = self._format._initialize_for_clone(url) |
1293 | 758 | self.open_repository().clone(result, revision_id=revision_id) | 758 | self.open_repository().clone(result, revision_id=revision_id) |
1294 | 759 | from_branch = self.open_branch() | 759 | from_branch = self.open_branch() |
1296 | 760 | from_branch.clone(result, revision_id=revision_id) | 760 | from_branch.clone(result, revision_id=revision_id, tag_selector=tag_selector) |
1297 | 761 | try: | 761 | try: |
1298 | 762 | tree = self.open_workingtree() | 762 | tree = self.open_workingtree() |
1299 | 763 | except errors.NotLocalUrl: | 763 | except errors.NotLocalUrl: |
1300 | 764 | 764 | ||
1301 | === modified file 'breezy/propose.py' | |||
1302 | --- breezy/propose.py 2020-02-18 01:57:45 +0000 | |||
1303 | +++ breezy/propose.py 2020-02-21 04:09:29 +0000 | |||
1304 | @@ -237,7 +237,7 @@ | |||
1305 | 237 | 237 | ||
1306 | 238 | def publish_derived(self, new_branch, base_branch, name, project=None, | 238 | def publish_derived(self, new_branch, base_branch, name, project=None, |
1307 | 239 | owner=None, revision_id=None, overwrite=False, | 239 | owner=None, revision_id=None, overwrite=False, |
1309 | 240 | allow_lossy=True): | 240 | allow_lossy=True, tag_selector=None): |
1310 | 241 | """Publish a branch to the site, derived from base_branch. | 241 | """Publish a branch to the site, derived from base_branch. |
1311 | 242 | 242 | ||
1312 | 243 | :param base_branch: branch to derive the new branch from | 243 | :param base_branch: branch to derive the new branch from |
1313 | 244 | 244 | ||
1314 | === modified file 'breezy/tag.py' | |||
1315 | --- breezy/tag.py 2020-02-18 01:57:45 +0000 | |||
1316 | +++ breezy/tag.py 2020-02-21 04:09:29 +0000 | |||
1317 | @@ -24,29 +24,22 @@ | |||
1318 | 24 | 24 | ||
1319 | 25 | from collections import defaultdict | 25 | from collections import defaultdict |
1320 | 26 | import contextlib | 26 | import contextlib |
1321 | 27 | import itertools | ||
1322 | 28 | import re | ||
1323 | 29 | import sys | ||
1324 | 27 | 30 | ||
1325 | 28 | # NOTE: I was going to call this tags.py, but vim seems to think all files | 31 | # NOTE: I was going to call this tags.py, but vim seems to think all files |
1326 | 29 | # called tags* are ctags files... mbp 20070220. | 32 | # called tags* are ctags files... mbp 20070220. |
1327 | 30 | 33 | ||
1328 | 34 | from .inter import InterObject | ||
1329 | 31 | from .registry import Registry | 35 | from .registry import Registry |
1330 | 32 | from .lazy_import import lazy_import | ||
1331 | 33 | lazy_import(globals(), """ | ||
1332 | 34 | import itertools | ||
1333 | 35 | import re | ||
1334 | 36 | import sys | ||
1335 | 37 | |||
1336 | 38 | from breezy import ( | ||
1337 | 39 | bencode, | ||
1338 | 40 | trace, | ||
1339 | 41 | ) | ||
1340 | 42 | """) | ||
1341 | 43 | 36 | ||
1342 | 44 | from . import ( | 37 | from . import ( |
1343 | 45 | errors, | 38 | errors, |
1344 | 46 | ) | 39 | ) |
1345 | 47 | 40 | ||
1346 | 48 | 41 | ||
1348 | 49 | def _reconcile_tags(source_dict, dest_dict, overwrite): | 42 | def _reconcile_tags(source_dict, dest_dict, overwrite, selector): |
1349 | 50 | """Do a two-way merge of two tag dictionaries. | 43 | """Do a two-way merge of two tag dictionaries. |
1350 | 51 | 44 | ||
1351 | 52 | * only in source => source value | 45 | * only in source => source value |
1352 | @@ -62,6 +55,8 @@ | |||
1353 | 62 | updates = {} | 55 | updates = {} |
1354 | 63 | result = dict(dest_dict) # copy | 56 | result = dict(dest_dict) # copy |
1355 | 64 | for name, target in source_dict.items(): | 57 | for name, target in source_dict.items(): |
1356 | 58 | if selector and not selector(name): | ||
1357 | 59 | continue | ||
1358 | 65 | if result.get(name) == target: | 60 | if result.get(name) == target: |
1359 | 66 | pass | 61 | pass |
1360 | 67 | elif name not in result or overwrite: | 62 | elif name not in result or overwrite: |
1361 | @@ -72,7 +67,7 @@ | |||
1362 | 72 | return result, updates, conflicts | 67 | return result, updates, conflicts |
1363 | 73 | 68 | ||
1364 | 74 | 69 | ||
1366 | 75 | class _Tags(object): | 70 | class Tags(object): |
1367 | 76 | 71 | ||
1368 | 77 | def __init__(self, branch): | 72 | def __init__(self, branch): |
1369 | 78 | self.branch = branch | 73 | self.branch = branch |
1370 | @@ -83,21 +78,39 @@ | |||
1371 | 83 | raise NotImplementedError(self.get_tag_dict) | 78 | raise NotImplementedError(self.get_tag_dict) |
1372 | 84 | 79 | ||
1373 | 85 | def get_reverse_tag_dict(self): | 80 | def get_reverse_tag_dict(self): |
1383 | 86 | """Return a dictionary mapping revision ids to list of tags. | 81 | """Returns a dict with revisions as keys |
1384 | 87 | """ | 82 | and a list of tags for that revision as value""" |
1385 | 88 | raise NotImplementedError(self.get_reverse_tag_dict) | 83 | d = self.get_tag_dict() |
1386 | 89 | 84 | rev = defaultdict(set) | |
1387 | 90 | def merge_to(self, to_tags, overwrite=False, ignore_master=False): | 85 | for key in d: |
1388 | 91 | """Merge new tags from this tags container into another. | 86 | rev[d[key]].add(key) |
1389 | 92 | 87 | return rev | |
1390 | 93 | :param to_tags: Tags container to merge into | 88 | |
1391 | 94 | :param overwrite: Whether to overwrite existing, divergent, tags. | 89 | def merge_to(self, to_tags, overwrite=False, ignore_master=False, selector=None): |
1392 | 90 | """Copy tags between repositories if necessary and possible. | ||
1393 | 91 | |||
1394 | 92 | This method has common command-line behaviour about handling | ||
1395 | 93 | error cases. | ||
1396 | 94 | |||
1397 | 95 | All new definitions are copied across, except that tags that already | ||
1398 | 96 | exist keep their existing definitions. | ||
1399 | 97 | |||
1400 | 98 | :param to_tags: Branch to receive these tags | ||
1401 | 99 | :param overwrite: Overwrite conflicting tags in the target branch | ||
1402 | 95 | :param ignore_master: Do not modify the tags in the target's master | 100 | :param ignore_master: Do not modify the tags in the target's master |
1403 | 96 | branch (if any). Default is false (so the master will be updated). | 101 | branch (if any). Default is false (so the master will be updated). |
1406 | 97 | New in bzr 2.3. | 102 | |
1407 | 98 | :return: Tuple with tag updates as dictionary and tag conflicts | 103 | :returns: Tuple with tag_updates and tag_conflicts. |
1408 | 104 | tag_updates is a dictionary with new tags, None is used for | ||
1409 | 105 | removed tags | ||
1410 | 106 | tag_conflicts is a set of tags that conflicted, each of which is | ||
1411 | 107 | (tagname, source_target, dest_target), or None if no copying was | ||
1412 | 108 | done. | ||
1413 | 99 | """ | 109 | """ |
1415 | 100 | raise NotImplementedError(self.merge_to) | 110 | intertags = InterTags.get(self, to_tags) |
1416 | 111 | return intertags.merge( | ||
1417 | 112 | overwrite=overwrite, ignore_master=ignore_master, | ||
1418 | 113 | selector=selector) | ||
1419 | 101 | 114 | ||
1420 | 102 | def set_tag(self, tag_name, revision): | 115 | def set_tag(self, tag_name, revision): |
1421 | 103 | """Set a tag. | 116 | """Set a tag. |
1422 | @@ -127,18 +140,21 @@ | |||
1423 | 127 | raise NotImplementedError(self.delete_tag) | 140 | raise NotImplementedError(self.delete_tag) |
1424 | 128 | 141 | ||
1425 | 129 | def rename_revisions(self, rename_map): | 142 | def rename_revisions(self, rename_map): |
1427 | 130 | """Replace revision ids according to a rename map. | 143 | """Rename revisions in this tags dictionary. |
1428 | 131 | 144 | ||
1431 | 132 | :param rename_map: Dictionary mapping old revision ids to | 145 | :param rename_map: Dictionary mapping old revids to new revids |
1430 | 133 | new revision ids. | ||
1432 | 134 | """ | 146 | """ |
1434 | 135 | raise NotImplementedError(self.rename_revisions) | 147 | reverse_tags = self.get_reverse_tag_dict() |
1435 | 148 | for revid, names in reverse_tags.items(): | ||
1436 | 149 | if revid in rename_map: | ||
1437 | 150 | for name in names: | ||
1438 | 151 | self.set_tag(name, rename_map[revid]) | ||
1439 | 136 | 152 | ||
1440 | 137 | def has_tag(self, tag_name): | 153 | def has_tag(self, tag_name): |
1441 | 138 | return tag_name in self.get_tag_dict() | 154 | return tag_name in self.get_tag_dict() |
1442 | 139 | 155 | ||
1443 | 140 | 156 | ||
1445 | 141 | class DisabledTags(_Tags): | 157 | class DisabledTags(Tags): |
1446 | 142 | """Tag storage that refuses to store anything. | 158 | """Tag storage that refuses to store anything. |
1447 | 143 | 159 | ||
1448 | 144 | This is used by older formats that can't store tags. | 160 | This is used by older formats that can't store tags. |
1449 | @@ -153,7 +169,7 @@ | |||
1450 | 153 | lookup_tag = _not_supported | 169 | lookup_tag = _not_supported |
1451 | 154 | delete_tag = _not_supported | 170 | delete_tag = _not_supported |
1452 | 155 | 171 | ||
1454 | 156 | def merge_to(self, to_tags, overwrite=False, ignore_master=False): | 172 | def merge_to(self, to_tags, overwrite=False, ignore_master=False, selector=None): |
1455 | 157 | # we never have anything to copy | 173 | # we never have anything to copy |
1456 | 158 | return {}, [] | 174 | return {}, [] |
1457 | 159 | 175 | ||
1458 | @@ -166,102 +182,19 @@ | |||
1459 | 166 | return {} | 182 | return {} |
1460 | 167 | 183 | ||
1461 | 168 | 184 | ||
1464 | 169 | class BasicTags(_Tags): | 185 | class InterTags(InterObject): |
1465 | 170 | """Tag storage in an unversioned branch control file. | 186 | """Operations between sets of tags. |
1466 | 171 | """ | 187 | """ |
1467 | 172 | 188 | ||
1560 | 173 | def set_tag(self, tag_name, tag_target): | 189 | _optimisers = [] |
1561 | 174 | """Add a tag definition to the branch. | 190 | """The available optimised InterTags types.""" |
1562 | 175 | 191 | ||
1563 | 176 | Behaviour if the tag is already present is not defined (yet). | 192 | @classmethod |
1564 | 177 | """ | 193 | def is_compatible(klass, source, target): |
1565 | 178 | # all done with a write lock held, so this looks atomic | 194 | # This is the default implementation |
1566 | 179 | with self.branch.lock_write(): | 195 | return True |
1567 | 180 | master = self.branch.get_master_branch() | 196 | |
1568 | 181 | if master is not None: | 197 | def merge(self, overwrite=False, ignore_master=False, selector=None): |
1477 | 182 | master.tags.set_tag(tag_name, tag_target) | ||
1478 | 183 | td = self.get_tag_dict() | ||
1479 | 184 | td[tag_name] = tag_target | ||
1480 | 185 | self._set_tag_dict(td) | ||
1481 | 186 | |||
1482 | 187 | def lookup_tag(self, tag_name): | ||
1483 | 188 | """Return the referent string of a tag""" | ||
1484 | 189 | td = self.get_tag_dict() | ||
1485 | 190 | try: | ||
1486 | 191 | return td[tag_name] | ||
1487 | 192 | except KeyError: | ||
1488 | 193 | raise errors.NoSuchTag(tag_name) | ||
1489 | 194 | |||
1490 | 195 | def get_tag_dict(self): | ||
1491 | 196 | with self.branch.lock_read(): | ||
1492 | 197 | try: | ||
1493 | 198 | tag_content = self.branch._get_tags_bytes() | ||
1494 | 199 | except errors.NoSuchFile: | ||
1495 | 200 | # ugly, but only abentley should see this :) | ||
1496 | 201 | trace.warning('No branch/tags file in %s. ' | ||
1497 | 202 | 'This branch was probably created by bzr 0.15pre. ' | ||
1498 | 203 | 'Create an empty file to silence this message.' | ||
1499 | 204 | % (self.branch, )) | ||
1500 | 205 | return {} | ||
1501 | 206 | return self._deserialize_tag_dict(tag_content) | ||
1502 | 207 | |||
1503 | 208 | def get_reverse_tag_dict(self): | ||
1504 | 209 | """Returns a dict with revisions as keys | ||
1505 | 210 | and a list of tags for that revision as value""" | ||
1506 | 211 | d = self.get_tag_dict() | ||
1507 | 212 | rev = defaultdict(set) | ||
1508 | 213 | for key in d: | ||
1509 | 214 | rev[d[key]].add(key) | ||
1510 | 215 | return rev | ||
1511 | 216 | |||
1512 | 217 | def delete_tag(self, tag_name): | ||
1513 | 218 | """Delete a tag definition. | ||
1514 | 219 | """ | ||
1515 | 220 | with self.branch.lock_write(): | ||
1516 | 221 | d = self.get_tag_dict() | ||
1517 | 222 | try: | ||
1518 | 223 | del d[tag_name] | ||
1519 | 224 | except KeyError: | ||
1520 | 225 | raise errors.NoSuchTag(tag_name) | ||
1521 | 226 | master = self.branch.get_master_branch() | ||
1522 | 227 | if master is not None: | ||
1523 | 228 | try: | ||
1524 | 229 | master.tags.delete_tag(tag_name) | ||
1525 | 230 | except errors.NoSuchTag: | ||
1526 | 231 | pass | ||
1527 | 232 | self._set_tag_dict(d) | ||
1528 | 233 | |||
1529 | 234 | def _set_tag_dict(self, new_dict): | ||
1530 | 235 | """Replace all tag definitions | ||
1531 | 236 | |||
1532 | 237 | WARNING: Calling this on an unlocked branch will lock it, and will | ||
1533 | 238 | replace the tags without warning on conflicts. | ||
1534 | 239 | |||
1535 | 240 | :param new_dict: Dictionary from tag name to target. | ||
1536 | 241 | """ | ||
1537 | 242 | return self.branch._set_tags_bytes(self._serialize_tag_dict(new_dict)) | ||
1538 | 243 | |||
1539 | 244 | def _serialize_tag_dict(self, tag_dict): | ||
1540 | 245 | td = dict((k.encode('utf-8'), v) | ||
1541 | 246 | for k, v in tag_dict.items()) | ||
1542 | 247 | return bencode.bencode(td) | ||
1543 | 248 | |||
1544 | 249 | def _deserialize_tag_dict(self, tag_content): | ||
1545 | 250 | """Convert the tag file into a dictionary of tags""" | ||
1546 | 251 | # was a special case to make initialization easy, an empty definition | ||
1547 | 252 | # is an empty dictionary | ||
1548 | 253 | if tag_content == b'': | ||
1549 | 254 | return {} | ||
1550 | 255 | try: | ||
1551 | 256 | r = {} | ||
1552 | 257 | for k, v in bencode.bdecode(tag_content).items(): | ||
1553 | 258 | r[k.decode('utf-8')] = v | ||
1554 | 259 | return r | ||
1555 | 260 | except ValueError as e: | ||
1556 | 261 | raise ValueError("failed to deserialize tag dictionary %r: %s" | ||
1557 | 262 | % (tag_content, e)) | ||
1558 | 263 | |||
1559 | 264 | def merge_to(self, to_tags, overwrite=False, ignore_master=False): | ||
1569 | 265 | """Copy tags between repositories if necessary and possible. | 198 | """Copy tags between repositories if necessary and possible. |
1570 | 266 | 199 | ||
1571 | 267 | This method has common command-line behaviour about handling | 200 | This method has common command-line behaviour about handling |
1572 | @@ -274,7 +207,9 @@ | |||
1573 | 274 | :param overwrite: Overwrite conflicting tags in the target branch | 207 | :param overwrite: Overwrite conflicting tags in the target branch |
1574 | 275 | :param ignore_master: Do not modify the tags in the target's master | 208 | :param ignore_master: Do not modify the tags in the target's master |
1575 | 276 | branch (if any). Default is false (so the master will be updated). | 209 | branch (if any). Default is false (so the master will be updated). |
1577 | 277 | New in bzr 2.3. | 210 | :param selector: Callback that determines whether a tag should be |
1578 | 211 | copied. It should take a tag name and as argument and return a | ||
1579 | 212 | boolean. | ||
1580 | 278 | 213 | ||
1581 | 279 | :returns: Tuple with tag_updates and tag_conflicts. | 214 | :returns: Tuple with tag_updates and tag_conflicts. |
1582 | 280 | tag_updates is a dictionary with new tags, None is used for | 215 | tag_updates is a dictionary with new tags, None is used for |
1583 | @@ -284,12 +219,12 @@ | |||
1584 | 284 | done. | 219 | done. |
1585 | 285 | """ | 220 | """ |
1586 | 286 | with contextlib.ExitStack() as stack: | 221 | with contextlib.ExitStack() as stack: |
1588 | 287 | if self.branch == to_tags.branch: | 222 | if self.source.branch == self.target.branch: |
1589 | 288 | return {}, [] | 223 | return {}, [] |
1591 | 289 | if not self.branch.supports_tags(): | 224 | if not self.source.branch.supports_tags(): |
1592 | 290 | # obviously nothing to copy | 225 | # obviously nothing to copy |
1593 | 291 | return {}, [] | 226 | return {}, [] |
1595 | 292 | source_dict = self.get_tag_dict() | 227 | source_dict = self.source.get_tag_dict() |
1596 | 293 | if not source_dict: | 228 | if not source_dict: |
1597 | 294 | # no tags in the source, and we don't want to clobber anything | 229 | # no tags in the source, and we don't want to clobber anything |
1598 | 295 | # that's in the destination | 230 | # that's in the destination |
1599 | @@ -306,44 +241,35 @@ | |||
1600 | 306 | # Ideally we'd improve this API to report the different conflicts | 241 | # Ideally we'd improve this API to report the different conflicts |
1601 | 307 | # more clearly to the caller, but we don't want to break plugins | 242 | # more clearly to the caller, but we don't want to break plugins |
1602 | 308 | # such as bzr-builddeb that use this API. | 243 | # such as bzr-builddeb that use this API. |
1604 | 309 | stack.enter_context(to_tags.branch.lock_write()) | 244 | stack.enter_context(self.target.branch.lock_write()) |
1605 | 310 | if ignore_master: | 245 | if ignore_master: |
1606 | 311 | master = None | 246 | master = None |
1607 | 312 | else: | 247 | else: |
1609 | 313 | master = to_tags.branch.get_master_branch() | 248 | master = self.target.branch.get_master_branch() |
1610 | 314 | if master is not None: | 249 | if master is not None: |
1611 | 315 | stack.enter_context(master.lock_write()) | 250 | stack.enter_context(master.lock_write()) |
1613 | 316 | updates, conflicts = self._merge_to(to_tags, source_dict, overwrite) | 251 | updates, conflicts = self._merge_to( |
1614 | 252 | self.target, source_dict, overwrite, selector=selector) | ||
1615 | 317 | if master is not None: | 253 | if master is not None: |
1618 | 318 | extra_updates, extra_conflicts = self._merge_to(master.tags, | 254 | extra_updates, extra_conflicts = self._merge_to( |
1619 | 319 | source_dict, overwrite) | 255 | master.tags, source_dict, overwrite, selector=selector) |
1620 | 320 | updates.update(extra_updates) | 256 | updates.update(extra_updates) |
1621 | 321 | conflicts += extra_conflicts | 257 | conflicts += extra_conflicts |
1622 | 322 | # We use set() to remove any duplicate conflicts from the master | 258 | # We use set() to remove any duplicate conflicts from the master |
1623 | 323 | # branch. | 259 | # branch. |
1624 | 324 | return updates, set(conflicts) | 260 | return updates, set(conflicts) |
1625 | 325 | 261 | ||
1627 | 326 | def _merge_to(self, to_tags, source_dict, overwrite): | 262 | @classmethod |
1628 | 263 | def _merge_to(cls, to_tags, source_dict, overwrite, selector): | ||
1629 | 327 | dest_dict = to_tags.get_tag_dict() | 264 | dest_dict = to_tags.get_tag_dict() |
1630 | 328 | result, updates, conflicts = _reconcile_tags( | 265 | result, updates, conflicts = _reconcile_tags( |
1632 | 329 | source_dict, dest_dict, overwrite) | 266 | source_dict, dest_dict, overwrite, selector) |
1633 | 330 | if result != dest_dict: | 267 | if result != dest_dict: |
1634 | 331 | to_tags._set_tag_dict(result) | 268 | to_tags._set_tag_dict(result) |
1635 | 332 | return updates, conflicts | 269 | return updates, conflicts |
1636 | 333 | 270 | ||
1650 | 334 | def rename_revisions(self, rename_map): | 271 | |
1651 | 335 | """Rename revisions in this tags dictionary. | 272 | class MemoryTags(Tags): |
1639 | 336 | |||
1640 | 337 | :param rename_map: Dictionary mapping old revids to new revids | ||
1641 | 338 | """ | ||
1642 | 339 | reverse_tags = self.get_reverse_tag_dict() | ||
1643 | 340 | for revid, names in reverse_tags.items(): | ||
1644 | 341 | if revid in rename_map: | ||
1645 | 342 | for name in names: | ||
1646 | 343 | self.set_tag(name, rename_map[revid]) | ||
1647 | 344 | |||
1648 | 345 | |||
1649 | 346 | class MemoryTags(_Tags): | ||
1652 | 347 | 273 | ||
1653 | 348 | def __init__(self, tag_dict): | 274 | def __init__(self, tag_dict): |
1654 | 349 | self._tag_dict = tag_dict | 275 | self._tag_dict = tag_dict |
1655 | @@ -359,15 +285,6 @@ | |||
1656 | 359 | except KeyError: | 285 | except KeyError: |
1657 | 360 | raise errors.NoSuchTag(tag_name) | 286 | raise errors.NoSuchTag(tag_name) |
1658 | 361 | 287 | ||
1659 | 362 | def get_reverse_tag_dict(self): | ||
1660 | 363 | """Returns a dict with revisions as keys | ||
1661 | 364 | and a list of tags for that revision as value""" | ||
1662 | 365 | d = self.get_tag_dict() | ||
1663 | 366 | rev = defaultdict(set) | ||
1664 | 367 | for key in d: | ||
1665 | 368 | rev[d[key]].add(key) | ||
1666 | 369 | return rev | ||
1667 | 370 | |||
1668 | 371 | def set_tag(self, name, revid): | 288 | def set_tag(self, name, revid): |
1669 | 372 | self._tag_dict[name] = revid | 289 | self._tag_dict[name] = revid |
1670 | 373 | 290 | ||
1671 | @@ -385,11 +302,11 @@ | |||
1672 | 385 | def _set_tag_dict(self, result): | 302 | def _set_tag_dict(self, result): |
1673 | 386 | self._tag_dict = dict(result.items()) | 303 | self._tag_dict = dict(result.items()) |
1674 | 387 | 304 | ||
1676 | 388 | def merge_to(self, to_tags, overwrite=False, ignore_master=False): | 305 | def merge_to(self, to_tags, overwrite=False, ignore_master=False, selector=None): |
1677 | 389 | source_dict = self.get_tag_dict() | 306 | source_dict = self.get_tag_dict() |
1678 | 390 | dest_dict = to_tags.get_tag_dict() | 307 | dest_dict = to_tags.get_tag_dict() |
1679 | 391 | result, updates, conflicts = _reconcile_tags( | 308 | result, updates, conflicts = _reconcile_tags( |
1681 | 392 | source_dict, dest_dict, overwrite) | 309 | source_dict, dest_dict, overwrite, selector) |
1682 | 393 | if result != dest_dict: | 310 | if result != dest_dict: |
1683 | 394 | to_tags._set_tag_dict(result) | 311 | to_tags._set_tag_dict(result) |
1684 | 395 | return updates, conflicts | 312 | return updates, conflicts |
1685 | @@ -441,3 +358,6 @@ | |||
1686 | 441 | tag_sort_methods.register("alpha", sort_alpha, 'Sort tags lexicographically.') | 358 | tag_sort_methods.register("alpha", sort_alpha, 'Sort tags lexicographically.') |
1687 | 442 | tag_sort_methods.register("time", sort_time, 'Sort tags chronologically.') | 359 | tag_sort_methods.register("time", sort_time, 'Sort tags chronologically.') |
1688 | 443 | tag_sort_methods.default_key = "natural" | 360 | tag_sort_methods.default_key = "natural" |
1689 | 361 | |||
1690 | 362 | |||
1691 | 363 | InterTags.register_optimiser(InterTags) | ||
1692 | 444 | 364 | ||
1693 | === modified file 'breezy/tests/per_branch/test_pull.py' | |||
1694 | --- breezy/tests/per_branch/test_pull.py 2018-11-17 20:50:40 +0000 | |||
1695 | +++ breezy/tests/per_branch/test_pull.py 2020-02-21 04:09:29 +0000 | |||
1696 | @@ -118,7 +118,7 @@ | |||
1697 | 118 | self.assertEqual(p1, result.old_revid) | 118 | self.assertEqual(p1, result.old_revid) |
1698 | 119 | self.assertEqual(2, result.new_revno) | 119 | self.assertEqual(2, result.new_revno) |
1699 | 120 | self.assertEqual(m1, result.new_revid) | 120 | self.assertEqual(m1, result.new_revid) |
1701 | 121 | self.assertEqual([], result.tag_conflicts) | 121 | self.assertEqual([], list(result.tag_conflicts)) |
1702 | 122 | 122 | ||
1703 | 123 | def test_pull_overwrite(self): | 123 | def test_pull_overwrite(self): |
1704 | 124 | tree_a = self.make_branch_and_tree('tree_a') | 124 | tree_a = self.make_branch_and_tree('tree_a') |
1705 | 125 | 125 | ||
1706 | === modified file 'breezy/tests/per_branch/test_tags.py' | |||
1707 | --- breezy/tests/per_branch/test_tags.py 2019-02-02 21:58:23 +0000 | |||
1708 | +++ breezy/tests/per_branch/test_tags.py 2020-02-21 04:09:29 +0000 | |||
1709 | @@ -142,6 +142,21 @@ | |||
1710 | 142 | self.assertEqual(updates, {}) | 142 | self.assertEqual(updates, {}) |
1711 | 143 | self.assertEqual(b2.tags.lookup_tag('conflicts'), revid2) | 143 | self.assertEqual(b2.tags.lookup_tag('conflicts'), revid2) |
1712 | 144 | 144 | ||
1713 | 145 | def test_merge_tags_selector(self): | ||
1714 | 146 | b1, [revid, revid1] = self.make_branch_with_revision_tuple('b1', 2) | ||
1715 | 147 | w2 = b1.controldir.sprout('b2', revision_id=revid).open_workingtree() | ||
1716 | 148 | revid2 = w2.commit('revision 2') | ||
1717 | 149 | b2 = w2.branch | ||
1718 | 150 | # if there are tags in the source and not the destination, then they | ||
1719 | 151 | # just go across | ||
1720 | 152 | b1.tags.set_tag('tag1', revid) | ||
1721 | 153 | b1.tags.set_tag('tag2', revid2) | ||
1722 | 154 | updates, conflicts = b1.tags.merge_to(b2.tags, selector=lambda x: x == 'tag1') | ||
1723 | 155 | self.assertEqual({'tag1': revid}, updates) | ||
1724 | 156 | self.assertEqual(set(), set(conflicts)) | ||
1725 | 157 | self.assertEqual(b2.tags.lookup_tag('tag1'), revid) | ||
1726 | 158 | self.assertRaises(errors.NoSuchTag, b2.tags.lookup_tag, 'tag2') | ||
1727 | 159 | |||
1728 | 145 | def test_unicode_tag(self): | 160 | def test_unicode_tag(self): |
1729 | 146 | tag_name = u'\u3070' | 161 | tag_name = u'\u3070' |
1730 | 147 | b1, [revid] = self.make_branch_with_revision_tuple('b', 1) | 162 | b1, [revid] = self.make_branch_with_revision_tuple('b', 1) |
1731 | 148 | 163 | ||
1732 | === modified file 'breezy/tests/per_controldir/test_push.py' | |||
1733 | --- breezy/tests/per_controldir/test_push.py 2019-02-15 03:42:06 +0000 | |||
1734 | +++ breezy/tests/per_controldir/test_push.py 2020-02-21 04:09:29 +0000 | |||
1735 | @@ -99,3 +99,14 @@ | |||
1736 | 99 | self.assertEqual(2, result.branch_push_result.new_revno) | 99 | self.assertEqual(2, result.branch_push_result.new_revno) |
1737 | 100 | self.assertEqual(tree.branch.base, result.source_branch.base) | 100 | self.assertEqual(tree.branch.base, result.source_branch.base) |
1738 | 101 | self.assertEqual(dir.open_branch().base, result.target_branch.base) | 101 | self.assertEqual(dir.open_branch().base, result.target_branch.base) |
1739 | 102 | |||
1740 | 103 | def test_push_tag_selector(self): | ||
1741 | 104 | tree, rev1 = self.create_simple_tree() | ||
1742 | 105 | try: | ||
1743 | 106 | tree.branch.tags.set_tag('tag1', rev1) | ||
1744 | 107 | except TagsNotSupported: | ||
1745 | 108 | raise TestNotApplicable('tags not supported') | ||
1746 | 109 | tree.branch.tags.set_tag('tag2', rev1) | ||
1747 | 110 | dir = self.make_repository('dir').controldir | ||
1748 | 111 | dir.push_branch(tree.branch, tag_selector=lambda x: x == 'tag1') | ||
1749 | 112 | self.assertEqual({'tag1': rev1}, dir.open_branch().tags.get_tag_dict()) | ||
1750 | 102 | 113 | ||
1751 | === modified file 'breezy/tests/per_interbranch/test_pull.py' | |||
1752 | --- breezy/tests/per_interbranch/test_pull.py 2018-11-11 04:08:32 +0000 | |||
1753 | +++ breezy/tests/per_interbranch/test_pull.py 2020-02-21 04:09:29 +0000 | |||
1754 | @@ -144,7 +144,7 @@ | |||
1755 | 144 | self.assertEqual(p1, result.old_revid) | 144 | self.assertEqual(p1, result.old_revid) |
1756 | 145 | self.assertEqual(2, result.new_revno) | 145 | self.assertEqual(2, result.new_revno) |
1757 | 146 | self.assertEqual(m1, result.new_revid) | 146 | self.assertEqual(m1, result.new_revid) |
1759 | 147 | self.assertEqual([], result.tag_conflicts) | 147 | self.assertEqual([], list(result.tag_conflicts)) |
1760 | 148 | 148 | ||
1761 | 149 | def test_pull_overwrite(self): | 149 | def test_pull_overwrite(self): |
1762 | 150 | tree_a = self.make_from_branch_and_tree('tree_a') | 150 | tree_a = self.make_from_branch_and_tree('tree_a') |
1763 | @@ -180,6 +180,26 @@ | |||
1764 | 180 | self.assertEqual(tree_b.branch.last_revision(), | 180 | self.assertEqual(tree_b.branch.last_revision(), |
1765 | 181 | tree_a.branch.last_revision()) | 181 | tree_a.branch.last_revision()) |
1766 | 182 | 182 | ||
1767 | 183 | def test_pull_tag_selector(self): | ||
1768 | 184 | if not self.branch_format_from.supports_tags(): | ||
1769 | 185 | raise TestNotApplicable('from format does not support tags') | ||
1770 | 186 | if not self.branch_format_to.supports_tags(): | ||
1771 | 187 | raise TestNotApplicable('to format does not support tags') | ||
1772 | 188 | tree_a = self.make_from_branch_and_tree('tree_a') | ||
1773 | 189 | revid1 = tree_a.commit('message 1') | ||
1774 | 190 | try: | ||
1775 | 191 | tree_b = self.sprout_to( | ||
1776 | 192 | tree_a.controldir, 'tree_b').open_workingtree() | ||
1777 | 193 | except errors.NoRoundtrippingSupport: | ||
1778 | 194 | raise TestNotApplicable( | ||
1779 | 195 | 'lossless push between %r and %r not supported' % | ||
1780 | 196 | (self.branch_format_from, self.branch_format_to)) | ||
1781 | 197 | tree_b.branch.tags.set_tag('tag1', revid1) | ||
1782 | 198 | tree_b.branch.tags.set_tag('tag2', revid1) | ||
1783 | 199 | tree_b.branch.get_config_stack().set('branch.fetch_tags', True) | ||
1784 | 200 | tree_a.pull(tree_b.branch, tag_selector=lambda x: x == 'tag1') | ||
1785 | 201 | self.assertEqual({'tag1': revid1}, tree_a.branch.tags.get_tag_dict()) | ||
1786 | 202 | |||
1787 | 183 | 203 | ||
1788 | 184 | class TestPullHook(TestCaseWithInterBranch): | 204 | class TestPullHook(TestCaseWithInterBranch): |
1789 | 185 | 205 | ||
1790 | 186 | 206 | ||
1791 | === modified file 'breezy/tests/per_interbranch/test_push.py' | |||
1792 | --- breezy/tests/per_interbranch/test_push.py 2020-02-07 02:14:30 +0000 | |||
1793 | +++ breezy/tests/per_interbranch/test_push.py 2020-02-21 04:09:29 +0000 | |||
1794 | @@ -374,6 +374,26 @@ | |||
1795 | 374 | self.overrideAttr(SmartServerRepositoryGetParentMap, | 374 | self.overrideAttr(SmartServerRepositoryGetParentMap, |
1796 | 375 | 'no_extra_results', True) | 375 | 'no_extra_results', True) |
1797 | 376 | 376 | ||
1798 | 377 | def test_push_tag_selector(self): | ||
1799 | 378 | if not self.branch_format_from.supports_tags(): | ||
1800 | 379 | raise tests.TestNotApplicable('from format does not support tags') | ||
1801 | 380 | if not self.branch_format_to.supports_tags(): | ||
1802 | 381 | raise tests.TestNotApplicable('to format does not support tags') | ||
1803 | 382 | tree_a = self.make_from_branch_and_tree('tree_a') | ||
1804 | 383 | revid1 = tree_a.commit('message 1') | ||
1805 | 384 | try: | ||
1806 | 385 | tree_b = self.sprout_to( | ||
1807 | 386 | tree_a.controldir, 'tree_b').open_workingtree() | ||
1808 | 387 | except errors.NoRoundtrippingSupport: | ||
1809 | 388 | raise tests.TestNotApplicable( | ||
1810 | 389 | 'lossless push between %r and %r not supported' % | ||
1811 | 390 | (self.branch_format_from, self.branch_format_to)) | ||
1812 | 391 | tree_b.branch.tags.set_tag('tag1', revid1) | ||
1813 | 392 | tree_b.branch.tags.set_tag('tag2', revid1) | ||
1814 | 393 | tree_b.branch.get_config_stack().set('branch.fetch_tags', True) | ||
1815 | 394 | tree_b.branch.push(tree_a.branch, tag_selector=lambda x: x == 'tag1') | ||
1816 | 395 | self.assertEqual({'tag1': revid1}, tree_a.branch.tags.get_tag_dict()) | ||
1817 | 396 | |||
1818 | 377 | 397 | ||
1819 | 378 | class TestPushHook(TestCaseWithInterBranch): | 398 | class TestPushHook(TestCaseWithInterBranch): |
1820 | 379 | 399 | ||
1821 | 380 | 400 | ||
1822 | === modified file 'breezy/tests/test_foreign.py' | |||
1823 | --- breezy/tests/test_foreign.py 2018-11-25 20:44:56 +0000 | |||
1824 | +++ breezy/tests/test_foreign.py 2020-02-21 04:09:29 +0000 | |||
1825 | @@ -171,7 +171,7 @@ | |||
1826 | 171 | def is_compatible(source, target): | 171 | def is_compatible(source, target): |
1827 | 172 | return isinstance(target, DummyForeignVcsBranch) | 172 | return isinstance(target, DummyForeignVcsBranch) |
1828 | 173 | 173 | ||
1830 | 174 | def push(self, overwrite=False, stop_revision=None, lossy=False): | 174 | def push(self, overwrite=False, stop_revision=None, lossy=False, tag_selector=None): |
1831 | 175 | if not lossy: | 175 | if not lossy: |
1832 | 176 | raise errors.NoRoundtrippingSupport(self.source, self.target) | 176 | raise errors.NoRoundtrippingSupport(self.source, self.target) |
1833 | 177 | result = branch.BranchPushResult() | 177 | result = branch.BranchPushResult() |
1834 | 178 | 178 | ||
1835 | === modified file 'breezy/tests/test_tag.py' | |||
1836 | --- breezy/tests/test_tag.py 2019-02-01 16:29:45 +0000 | |||
1837 | +++ breezy/tests/test_tag.py 2020-02-21 04:09:29 +0000 | |||
1838 | @@ -22,10 +22,12 @@ | |||
1839 | 22 | errors, | 22 | errors, |
1840 | 23 | ) | 23 | ) |
1841 | 24 | from breezy.tag import ( | 24 | from breezy.tag import ( |
1842 | 25 | BasicTags, | ||
1843 | 26 | DisabledTags, | 25 | DisabledTags, |
1844 | 27 | MemoryTags, | 26 | MemoryTags, |
1845 | 28 | ) | 27 | ) |
1846 | 28 | from breezy.bzr.tag import ( | ||
1847 | 29 | BasicTags, | ||
1848 | 30 | ) | ||
1849 | 29 | from breezy.tests import ( | 31 | from breezy.tests import ( |
1850 | 30 | TestCase, | 32 | TestCase, |
1851 | 31 | TestCaseWithTransport, | 33 | TestCaseWithTransport, |
1852 | @@ -120,6 +122,17 @@ | |||
1853 | 120 | self.assertEqual({u'tag-2': b'z'}, updates) | 122 | self.assertEqual({u'tag-2': b'z'}, updates) |
1854 | 121 | self.assertEqual(b'z', b.tags.lookup_tag('tag-2')) | 123 | self.assertEqual(b'z', b.tags.lookup_tag('tag-2')) |
1855 | 122 | 124 | ||
1856 | 125 | def test_merge_to_with_selector(self): | ||
1857 | 126 | a = self.make_branch_supporting_tags('a') | ||
1858 | 127 | b = self.make_branch_supporting_tags('b') | ||
1859 | 128 | # simple merge | ||
1860 | 129 | a.tags.set_tag('tag-1', b'x') | ||
1861 | 130 | a.tags.set_tag('tag-2', b'y') | ||
1862 | 131 | updates, conflicts = a.tags.merge_to(b.tags, selector=lambda x: x == 'tag-1') | ||
1863 | 132 | self.assertEqual(list(conflicts), []) | ||
1864 | 133 | self.assertEqual({u'tag-1': b'x'}, updates) | ||
1865 | 134 | self.assertRaises(errors.NoSuchTag, b.tags.lookup_tag, 'tag-2') | ||
1866 | 135 | |||
1867 | 123 | 136 | ||
1868 | 124 | class TestTagsInCheckouts(TestCaseWithTransport): | 137 | class TestTagsInCheckouts(TestCaseWithTransport): |
1869 | 125 | """Tests for how tags are synchronised between the master and child branch | 138 | """Tests for how tags are synchronised between the master and child branch |
1870 | 126 | 139 | ||
1871 | === modified file 'breezy/workingtree.py' | |||
1872 | --- breezy/workingtree.py 2020-02-18 01:57:45 +0000 | |||
1873 | +++ breezy/workingtree.py 2020-02-21 04:09:29 +0000 | |||
1874 | @@ -819,13 +819,13 @@ | |||
1875 | 819 | 819 | ||
1876 | 820 | def pull(self, source, overwrite=False, stop_revision=None, | 820 | def pull(self, source, overwrite=False, stop_revision=None, |
1877 | 821 | change_reporter=None, possible_transports=None, local=False, | 821 | change_reporter=None, possible_transports=None, local=False, |
1879 | 822 | show_base=False): | 822 | show_base=False, tag_selector=None): |
1880 | 823 | with self.lock_write(), source.lock_read(): | 823 | with self.lock_write(), source.lock_read(): |
1881 | 824 | old_revision_info = self.branch.last_revision_info() | 824 | old_revision_info = self.branch.last_revision_info() |
1882 | 825 | basis_tree = self.basis_tree() | 825 | basis_tree = self.basis_tree() |
1883 | 826 | count = self.branch.pull(source, overwrite, stop_revision, | 826 | count = self.branch.pull(source, overwrite, stop_revision, |
1884 | 827 | possible_transports=possible_transports, | 827 | possible_transports=possible_transports, |
1886 | 828 | local=local) | 828 | local=local, tag_selector=tag_selector) |
1887 | 829 | new_revision_info = self.branch.last_revision_info() | 829 | new_revision_info = self.branch.last_revision_info() |
1888 | 830 | if new_revision_info != old_revision_info: | 830 | if new_revision_info != old_revision_info: |
1889 | 831 | repository = self.branch.repository | 831 | repository = self.branch.repository |
Running landing tests failed /ci.breezy- vcs.org/ job/brz/ job/brz- land/722/
https:/