##// END OF EJS Templates
clfilter: introduces a hidden filter...
Pierre-Yves David -
r18242:e4687ede default
parent child Browse files
Show More
@@ -1,115 +1,127
1 1 # repoview.py - Filtered view of a localrepo object
2 2 #
3 3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 4 # Logilab SA <contact@logilab.fr>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 import copy
10 10 import phases
11 11
12
13 def computehidden(repo):
14 """compute the set of hidden revision to filter
15
16 During most operation hidden should be filtered."""
17 assert not repo.changelog.filteredrevs
18 if repo.obsstore:
19 return frozenset(repo.revs('hidden()'))
20 return frozenset()
21
12 22 def computeunserved(repo):
13 23 """compute the set of revision that should be filtered when used a server
14 24
15 25 Secret and hidden changeset should not pretend to be here."""
16 26 assert not repo.changelog.filteredrevs
17 27 # fast path in simple case to avoid impact of non optimised code
18 28 if phases.hassecret(repo) or repo.obsstore:
19 29 return frozenset(repo.revs('hidden() + secret()'))
20 30 return frozenset()
21 31
22 32 # function to compute filtered set
23 filtertable = {'unserved': computeunserved}
33 filtertable = {'hidden': computehidden,
34 'unserved': computeunserved}
24 35 ### Nearest subset relation
25 36 # Nearest subset of filter X is a filter Y so that:
26 37 # * Y is included in X,
27 38 # * X - Y is as small as possible.
28 39 # This create and ordering used for branchmap purpose.
29 40 # the ordering may be partial
30 subsettable = {None: 'unserved'}
41 subsettable = {None: 'hidden',
42 'hidden': 'unserved'}
31 43
32 44 def filteredrevs(repo, filtername):
33 45 """returns set of filtered revision for this filter name"""
34 46 if filtername not in repo.filteredrevcache:
35 47 func = filtertable[filtername]
36 48 repo.filteredrevcache[filtername] = func(repo.unfiltered())
37 49 return repo.filteredrevcache[filtername]
38 50
39 51 class repoview(object):
40 52 """Provide a read/write view of a repo through a filtered changelog
41 53
42 54 This object is used to access a filtered version of a repository without
43 55 altering the original repository object itself. We can not alter the
44 56 original object for two main reasons:
45 57 - It prevents the use of a repo with multiple filters at the same time. In
46 58 particular when multiple threads are involved.
47 59 - It makes scope of the filtering harder to control.
48 60
49 61 This object behaves very closely to the original repository. All attribute
50 62 operations are done on the original repository:
51 63 - An access to `repoview.someattr` actually returns `repo.someattr`,
52 64 - A write to `repoview.someattr` actually sets value of `repo.someattr`,
53 65 - A deletion of `repoview.someattr` actually drops `someattr`
54 66 from `repo.__dict__`.
55 67
56 68 The only exception is the `changelog` property. It is overridden to return
57 69 a (surface) copy of `repo.changelog` with some revisions filtered. The
58 70 `filtername` attribute of the view control the revisions that need to be
59 71 filtered. (the fact the changelog is copied is an implementation detail).
60 72
61 73 Unlike attributes, this object intercepts all method calls. This means that
62 74 all methods are run on the `repoview` object with the filtered `changelog`
63 75 property. For this purpose the simple `repoview` class must be mixed with
64 76 the actual class of the repository. This ensures that the resulting
65 77 `repoview` object have the very same methods than the repo object. This
66 78 leads to the property below.
67 79
68 80 repoview.method() --> repo.__class__.method(repoview)
69 81
70 82 The inheritance has to be done dynamically because `repo` can be of any
71 83 subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`.
72 84 """
73 85
74 86 def __init__(self, repo, filtername):
75 87 object.__setattr__(self, '_unfilteredrepo', repo)
76 88 object.__setattr__(self, 'filtername', filtername)
77 89
78 90 # not a cacheproperty on purpose we shall implement a proper cache later
79 91 @property
80 92 def changelog(self):
81 93 """return a filtered version of the changeset
82 94
83 95 this changelog must not be used for writing"""
84 96 # some cache may be implemented later
85 97 cl = copy.copy(self._unfilteredrepo.changelog)
86 98 cl.filteredrevs = filteredrevs(self._unfilteredrepo, self.filtername)
87 99 return cl
88 100
89 101 def unfiltered(self):
90 102 """Return an unfiltered version of a repo"""
91 103 return self._unfilteredrepo
92 104
93 105 def filtered(self, name):
94 106 """Return a filtered version of a repository"""
95 107 if name == self.filtername:
96 108 return self
97 109 return self.unfiltered().filtered(name)
98 110
99 111 # everything access are forwarded to the proxied repo
100 112 def __getattr__(self, attr):
101 113 return getattr(self._unfilteredrepo, attr)
102 114
103 115 def __setattr__(self, attr, value):
104 116 return setattr(self._unfilteredrepo, attr, value)
105 117
106 118 def __delattr__(self, attr):
107 119 return delattr(self._unfilteredrepo, attr)
108 120
109 121 # The `requirement` attribut is initialiazed during __init__. But
110 122 # __getattr__ won't be called as it also exists on the class. We need
111 123 # explicit forwarding to main repo here
112 124 @property
113 125 def requirements(self):
114 126 return self._unfilteredrepo.requirements
115 127
@@ -1,503 +1,503
1 1 $ hglog() { hg log --template "{rev} {phaseidx} {desc}\n" $*; }
2 2 $ mkcommit() {
3 3 > echo "$1" > "$1"
4 4 > hg add "$1"
5 5 > message="$1"
6 6 > shift
7 7 > hg ci -m "$message" $*
8 8 > }
9 9
10 10 $ hg init initialrepo
11 11 $ cd initialrepo
12 12
13 13 Cannot change null revision phase
14 14
15 15 $ hg phase --force --secret null
16 16 abort: cannot change null revision phase
17 17 [255]
18 18 $ hg phase null
19 19 -1: public
20 20
21 21 $ mkcommit A
22 22
23 23 New commit are draft by default
24 24
25 25 $ hglog
26 26 0 1 A
27 27
28 28 Following commit are draft too
29 29
30 30 $ mkcommit B
31 31
32 32 $ hglog
33 33 1 1 B
34 34 0 1 A
35 35
36 36 Draft commit are properly created over public one:
37 37
38 38 $ hg phase --public .
39 39 $ hglog
40 40 1 0 B
41 41 0 0 A
42 42
43 43 $ mkcommit C
44 44 $ mkcommit D
45 45
46 46 $ hglog
47 47 3 1 D
48 48 2 1 C
49 49 1 0 B
50 50 0 0 A
51 51
52 52 Test creating changeset as secret
53 53
54 54 $ mkcommit E --config phases.new-commit='secret'
55 55 $ hglog
56 56 4 2 E
57 57 3 1 D
58 58 2 1 C
59 59 1 0 B
60 60 0 0 A
61 61
62 62 Test the secret property is inherited
63 63
64 64 $ mkcommit H
65 65 $ hglog
66 66 5 2 H
67 67 4 2 E
68 68 3 1 D
69 69 2 1 C
70 70 1 0 B
71 71 0 0 A
72 72
73 73 Even on merge
74 74
75 75 $ hg up -q 1
76 76 $ mkcommit "B'"
77 77 created new head
78 78 $ hglog
79 79 6 1 B'
80 80 5 2 H
81 81 4 2 E
82 82 3 1 D
83 83 2 1 C
84 84 1 0 B
85 85 0 0 A
86 86 $ hg merge 4 # E
87 87 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 88 (branch merge, don't forget to commit)
89 89 $ hg ci -m "merge B' and E"
90 90 $ hglog
91 91 7 2 merge B' and E
92 92 6 1 B'
93 93 5 2 H
94 94 4 2 E
95 95 3 1 D
96 96 2 1 C
97 97 1 0 B
98 98 0 0 A
99 99
100 100 Test secret changeset are not pushed
101 101
102 102 $ hg init ../push-dest
103 103 $ cat > ../push-dest/.hg/hgrc << EOF
104 104 > [phases]
105 105 > publish=False
106 106 > EOF
107 107 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
108 108 comparing with ../push-dest
109 109 searching for changes
110 110 0 public A
111 111 1 public B
112 112 2 draft C
113 113 3 draft D
114 114 6 draft B'
115 115 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
116 116 comparing with ../push-dest
117 117 searching for changes
118 118 0 public A
119 119 1 public B
120 120 2 draft C
121 121 3 draft D
122 122 6 draft B'
123 123
124 124 $ hg push ../push-dest -f # force because we push multiple heads
125 125 pushing to ../push-dest
126 126 searching for changes
127 127 adding changesets
128 128 adding manifests
129 129 adding file changes
130 130 added 5 changesets with 5 changes to 5 files (+1 heads)
131 131 $ hglog
132 132 7 2 merge B' and E
133 133 6 1 B'
134 134 5 2 H
135 135 4 2 E
136 136 3 1 D
137 137 2 1 C
138 138 1 0 B
139 139 0 0 A
140 140 $ cd ../push-dest
141 141 $ hglog
142 142 4 1 B'
143 143 3 1 D
144 144 2 1 C
145 145 1 0 B
146 146 0 0 A
147 147
148 148 (Issue3303)
149 149 Check that remote secret changeset are ignore when checking creation of remote heads
150 150
151 151 We add a secret head into the push destination. This secreat head shadow a
152 152 visible shared between the initial repo and the push destination.
153 153
154 154 $ hg up -q 4 # B'
155 155 $ mkcommit Z --config phases.new-commit=secret
156 156 $ hg phase .
157 157 5: secret
158 158
159 159 # We now try to push a new public changeset that descend from the common public
160 160 # head shadowed by the remote secret head.
161 161
162 162 $ cd ../initialrepo
163 163 $ hg up -q 6 #B'
164 164 $ mkcommit I
165 165 created new head
166 166 $ hg push ../push-dest
167 167 pushing to ../push-dest
168 168 searching for changes
169 169 adding changesets
170 170 adding manifests
171 171 adding file changes
172 172 added 1 changesets with 1 changes to 1 files (+1 heads)
173 173
174 174 :note: The "(+1 heads)" is wrong as we do not had any visible head
175 175
176 176 check that branch cache with "unserved" filter are properly computed and stored
177 177
178 178 $ ls ../push-dest/.hg/cache/branchheads*
179 ../push-dest/.hg/cache/branchheads
179 ../push-dest/.hg/cache/branchheads-hidden
180 180 ../push-dest/.hg/cache/branchheads-unserved
181 $ cat ../push-dest/.hg/cache/branchheads
181 $ cat ../push-dest/.hg/cache/branchheads-hidden
182 182 6d6770faffce199f1fddd1cf87f6f026138cf061 6
183 183 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
184 184 2713879da13d6eea1ff22b442a5a87cb31a7ce6a default
185 185 6d6770faffce199f1fddd1cf87f6f026138cf061 default
186 186 $ cat ../push-dest/.hg/cache/branchheads-unserved
187 187 cf9fe039dfd67e829edf6522a45de057b5c86519 4
188 188 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
189 189 cf9fe039dfd67e829edf6522a45de057b5c86519 default
190 190
191 191
192 192 Restore condition prior extra insertion.
193 193 $ hg -q --config extensions.mq= strip .
194 194 $ hg up -q 7
195 195 $ cd ..
196 196
197 197 Test secret changeset are not pull
198 198
199 199 $ hg init pull-dest
200 200 $ cd pull-dest
201 201 $ hg pull ../initialrepo
202 202 pulling from ../initialrepo
203 203 requesting all changes
204 204 adding changesets
205 205 adding manifests
206 206 adding file changes
207 207 added 5 changesets with 5 changes to 5 files (+1 heads)
208 208 (run 'hg heads' to see heads, 'hg merge' to merge)
209 209 $ hglog
210 210 4 0 B'
211 211 3 0 D
212 212 2 0 C
213 213 1 0 B
214 214 0 0 A
215 215 $ cd ..
216 216
217 217 But secret can still be bundled explicitly
218 218
219 219 $ cd initialrepo
220 220 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
221 221 4 changesets found
222 222 $ cd ..
223 223
224 224 Test secret changeset are not cloned
225 225 (during local clone)
226 226
227 227 $ hg clone -qU initialrepo clone-dest
228 228 $ hglog -R clone-dest
229 229 4 0 B'
230 230 3 0 D
231 231 2 0 C
232 232 1 0 B
233 233 0 0 A
234 234
235 235 Test revset
236 236
237 237 $ cd initialrepo
238 238 $ hglog -r 'public()'
239 239 0 0 A
240 240 1 0 B
241 241 $ hglog -r 'draft()'
242 242 2 1 C
243 243 3 1 D
244 244 6 1 B'
245 245 $ hglog -r 'secret()'
246 246 4 2 E
247 247 5 2 H
248 248 7 2 merge B' and E
249 249
250 250 test that phase are displayed in log at debug level
251 251
252 252 $ hg log --debug
253 253 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
254 254 tag: tip
255 255 phase: secret
256 256 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
257 257 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
258 258 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
259 259 user: test
260 260 date: Thu Jan 01 00:00:00 1970 +0000
261 261 files+: C D E
262 262 extra: branch=default
263 263 description:
264 264 merge B' and E
265 265
266 266
267 267 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
268 268 phase: draft
269 269 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
270 270 parent: -1:0000000000000000000000000000000000000000
271 271 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
272 272 user: test
273 273 date: Thu Jan 01 00:00:00 1970 +0000
274 274 files+: B'
275 275 extra: branch=default
276 276 description:
277 277 B'
278 278
279 279
280 280 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
281 281 phase: secret
282 282 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
283 283 parent: -1:0000000000000000000000000000000000000000
284 284 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
285 285 user: test
286 286 date: Thu Jan 01 00:00:00 1970 +0000
287 287 files+: H
288 288 extra: branch=default
289 289 description:
290 290 H
291 291
292 292
293 293 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
294 294 phase: secret
295 295 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
296 296 parent: -1:0000000000000000000000000000000000000000
297 297 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
298 298 user: test
299 299 date: Thu Jan 01 00:00:00 1970 +0000
300 300 files+: E
301 301 extra: branch=default
302 302 description:
303 303 E
304 304
305 305
306 306 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
307 307 phase: draft
308 308 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
309 309 parent: -1:0000000000000000000000000000000000000000
310 310 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
311 311 user: test
312 312 date: Thu Jan 01 00:00:00 1970 +0000
313 313 files+: D
314 314 extra: branch=default
315 315 description:
316 316 D
317 317
318 318
319 319 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
320 320 phase: draft
321 321 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
322 322 parent: -1:0000000000000000000000000000000000000000
323 323 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
324 324 user: test
325 325 date: Thu Jan 01 00:00:00 1970 +0000
326 326 files+: C
327 327 extra: branch=default
328 328 description:
329 329 C
330 330
331 331
332 332 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
333 333 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
334 334 parent: -1:0000000000000000000000000000000000000000
335 335 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
336 336 user: test
337 337 date: Thu Jan 01 00:00:00 1970 +0000
338 338 files+: B
339 339 extra: branch=default
340 340 description:
341 341 B
342 342
343 343
344 344 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
345 345 parent: -1:0000000000000000000000000000000000000000
346 346 parent: -1:0000000000000000000000000000000000000000
347 347 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
348 348 user: test
349 349 date: Thu Jan 01 00:00:00 1970 +0000
350 350 files+: A
351 351 extra: branch=default
352 352 description:
353 353 A
354 354
355 355
356 356
357 357
358 358 (Issue3707)
359 359 test invalid phase name
360 360
361 361 $ mkcommit I --config phases.new-commit='babar'
362 362 transaction abort!
363 363 rollback completed
364 364 abort: phases.new-commit: not a valid phase name ('babar')
365 365 [255]
366 366 Test phase command
367 367 ===================
368 368
369 369 initial picture
370 370
371 371 $ cat >> $HGRCPATH << EOF
372 372 > [extensions]
373 373 > hgext.graphlog=
374 374 > EOF
375 375 $ hg log -G --template "{rev} {phase} {desc}\n"
376 376 @ 7 secret merge B' and E
377 377 |\
378 378 | o 6 draft B'
379 379 | |
380 380 +---o 5 secret H
381 381 | |
382 382 o | 4 secret E
383 383 | |
384 384 o | 3 draft D
385 385 | |
386 386 o | 2 draft C
387 387 |/
388 388 o 1 public B
389 389 |
390 390 o 0 public A
391 391
392 392
393 393 display changesets phase
394 394
395 395 (mixing -r and plain rev specification)
396 396
397 397 $ hg phase 1::4 -r 7
398 398 1: public
399 399 2: draft
400 400 3: draft
401 401 4: secret
402 402 7: secret
403 403
404 404
405 405 move changeset forward
406 406
407 407 (with -r option)
408 408
409 409 $ hg phase --public -r 2
410 410 $ hg log -G --template "{rev} {phase} {desc}\n"
411 411 @ 7 secret merge B' and E
412 412 |\
413 413 | o 6 draft B'
414 414 | |
415 415 +---o 5 secret H
416 416 | |
417 417 o | 4 secret E
418 418 | |
419 419 o | 3 draft D
420 420 | |
421 421 o | 2 public C
422 422 |/
423 423 o 1 public B
424 424 |
425 425 o 0 public A
426 426
427 427
428 428 move changeset backward
429 429
430 430 (without -r option)
431 431
432 432 $ hg phase --draft --force 2
433 433 $ hg log -G --template "{rev} {phase} {desc}\n"
434 434 @ 7 secret merge B' and E
435 435 |\
436 436 | o 6 draft B'
437 437 | |
438 438 +---o 5 secret H
439 439 | |
440 440 o | 4 secret E
441 441 | |
442 442 o | 3 draft D
443 443 | |
444 444 o | 2 draft C
445 445 |/
446 446 o 1 public B
447 447 |
448 448 o 0 public A
449 449
450 450
451 451 move changeset forward and backward
452 452
453 453 $ hg phase --draft --force 1::4
454 454 $ hg log -G --template "{rev} {phase} {desc}\n"
455 455 @ 7 secret merge B' and E
456 456 |\
457 457 | o 6 draft B'
458 458 | |
459 459 +---o 5 secret H
460 460 | |
461 461 o | 4 draft E
462 462 | |
463 463 o | 3 draft D
464 464 | |
465 465 o | 2 draft C
466 466 |/
467 467 o 1 draft B
468 468 |
469 469 o 0 public A
470 470
471 471 test partial failure
472 472
473 473 $ hg phase --public 7
474 474 $ hg phase --draft '5 or 7'
475 475 cannot move 1 changesets to a more permissive phase, use --force
476 476 phase changed for 1 changesets
477 477 [1]
478 478 $ hg log -G --template "{rev} {phase} {desc}\n"
479 479 @ 7 public merge B' and E
480 480 |\
481 481 | o 6 public B'
482 482 | |
483 483 +---o 5 draft H
484 484 | |
485 485 o | 4 public E
486 486 | |
487 487 o | 3 public D
488 488 | |
489 489 o | 2 public C
490 490 |/
491 491 o 1 public B
492 492 |
493 493 o 0 public A
494 494
495 495
496 496 test complete failure
497 497
498 498 $ hg phase --draft 7
499 499 cannot move 1 changesets to a more permissive phase, use --force
500 500 no phases changed
501 501 [1]
502 502
503 503 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now