Show More
@@ -0,0 +1,259 b'' | |||||
|
1 | $ cat >> $HGRCPATH <<EOF | |||
|
2 | > [experimental] | |||
|
3 | > bundle-phases=yes | |||
|
4 | > [extensions] | |||
|
5 | > strip= | |||
|
6 | > drawdag=$TESTDIR/drawdag.py | |||
|
7 | > EOF | |||
|
8 | ||||
|
9 | Set up repo with linear history | |||
|
10 | $ hg init linear | |||
|
11 | $ cd linear | |||
|
12 | $ hg debugdrawdag <<'EOF' | |||
|
13 | > E | |||
|
14 | > | | |||
|
15 | > D | |||
|
16 | > | | |||
|
17 | > C | |||
|
18 | > | | |||
|
19 | > B | |||
|
20 | > | | |||
|
21 | > A | |||
|
22 | > EOF | |||
|
23 | $ hg phase --public A | |||
|
24 | $ hg phase --force --secret D | |||
|
25 | $ hg log -G -T '{desc} {phase}\n' | |||
|
26 | o E secret | |||
|
27 | | | |||
|
28 | o D secret | |||
|
29 | | | |||
|
30 | o C draft | |||
|
31 | | | |||
|
32 | o B draft | |||
|
33 | | | |||
|
34 | o A public | |||
|
35 | ||||
|
36 | Phases are restored when unbundling | |||
|
37 | $ hg bundle --base B -r E bundle | |||
|
38 | 3 changesets found | |||
|
39 | $ hg debugbundle bundle | |||
|
40 | Stream params: sortdict([('Compression', 'BZ')]) | |||
|
41 | changegroup -- "sortdict([('version', '02'), ('nbchanges', '3')])" | |||
|
42 | 26805aba1e600a82e93661149f2313866a221a7b | |||
|
43 | f585351a92f85104bff7c284233c338b10eb1df7 | |||
|
44 | 9bc730a19041f9ec7cb33c626e811aa233efb18c | |||
|
45 | phase-heads -- 'sortdict()' | |||
|
46 | 26805aba1e600a82e93661149f2313866a221a7b draft | |||
|
47 | $ hg strip --no-backup C | |||
|
48 | $ hg unbundle -q bundle | |||
|
49 | $ rm bundle | |||
|
50 | $ hg log -G -T '{desc} {phase}\n' | |||
|
51 | o E secret | |||
|
52 | | | |||
|
53 | o D secret | |||
|
54 | | | |||
|
55 | o C draft | |||
|
56 | | | |||
|
57 | o B draft | |||
|
58 | | | |||
|
59 | o A public | |||
|
60 | ||||
|
61 | Root revision's phase is preserved | |||
|
62 | $ hg bundle -a bundle | |||
|
63 | 5 changesets found | |||
|
64 | $ hg strip --no-backup A | |||
|
65 | $ hg unbundle -q bundle | |||
|
66 | $ rm bundle | |||
|
67 | $ hg log -G -T '{desc} {phase}\n' | |||
|
68 | o E secret | |||
|
69 | | | |||
|
70 | o D secret | |||
|
71 | | | |||
|
72 | o C draft | |||
|
73 | | | |||
|
74 | o B draft | |||
|
75 | | | |||
|
76 | o A public | |||
|
77 | ||||
|
78 | Completely public history can be restored | |||
|
79 | $ hg phase --public E | |||
|
80 | $ hg bundle -a bundle | |||
|
81 | 5 changesets found | |||
|
82 | $ hg strip --no-backup A | |||
|
83 | $ hg unbundle -q bundle | |||
|
84 | $ rm bundle | |||
|
85 | $ hg log -G -T '{desc} {phase}\n' | |||
|
86 | o E public | |||
|
87 | | | |||
|
88 | o D public | |||
|
89 | | | |||
|
90 | o C public | |||
|
91 | | | |||
|
92 | o B public | |||
|
93 | | | |||
|
94 | o A public | |||
|
95 | ||||
|
96 | Direct transition from public to secret can be restored | |||
|
97 | $ hg phase --secret --force D | |||
|
98 | $ hg bundle -a bundle | |||
|
99 | 5 changesets found | |||
|
100 | $ hg strip --no-backup A | |||
|
101 | $ hg unbundle -q bundle | |||
|
102 | $ rm bundle | |||
|
103 | $ hg log -G -T '{desc} {phase}\n' | |||
|
104 | o E secret | |||
|
105 | | | |||
|
106 | o D secret | |||
|
107 | | | |||
|
108 | o C public | |||
|
109 | | | |||
|
110 | o B public | |||
|
111 | | | |||
|
112 | o A public | |||
|
113 | ||||
|
114 | Revisions within bundle preserve their phase even if parent changes its phase | |||
|
115 | $ hg phase --draft --force B | |||
|
116 | $ hg bundle --base B -r E bundle | |||
|
117 | 3 changesets found | |||
|
118 | $ hg strip --no-backup C | |||
|
119 | $ hg phase --public B | |||
|
120 | $ hg unbundle -q bundle | |||
|
121 | $ rm bundle | |||
|
122 | $ hg log -G -T '{desc} {phase}\n' | |||
|
123 | o E secret | |||
|
124 | | | |||
|
125 | o D secret | |||
|
126 | | | |||
|
127 | o C draft | |||
|
128 | | | |||
|
129 | o B public | |||
|
130 | | | |||
|
131 | o A public | |||
|
132 | ||||
|
133 | Phase of ancestors of stripped node get advanced to accommodate child | |||
|
134 | $ hg bundle --base B -r E bundle | |||
|
135 | 3 changesets found | |||
|
136 | $ hg strip --no-backup C | |||
|
137 | $ hg phase --force --secret B | |||
|
138 | $ hg unbundle -q bundle | |||
|
139 | $ rm bundle | |||
|
140 | $ hg log -G -T '{desc} {phase}\n' | |||
|
141 | o E secret | |||
|
142 | | | |||
|
143 | o D secret | |||
|
144 | | | |||
|
145 | o C draft | |||
|
146 | | | |||
|
147 | o B draft | |||
|
148 | | | |||
|
149 | o A public | |||
|
150 | ||||
|
151 | Unbundling advances phases of changesets even if they were already in the repo. | |||
|
152 | To test that, create a bundle of everything in draft phase and then unbundle | |||
|
153 | to see that secret becomes draft, but public remains public. | |||
|
154 | $ hg phase --draft --force A | |||
|
155 | $ hg phase --draft E | |||
|
156 | $ hg bundle -a bundle | |||
|
157 | 5 changesets found | |||
|
158 | $ hg phase --public A | |||
|
159 | $ hg phase --secret --force E | |||
|
160 | $ hg unbundle -q bundle | |||
|
161 | $ rm bundle | |||
|
162 | $ hg log -G -T '{desc} {phase}\n' | |||
|
163 | o E draft | |||
|
164 | | | |||
|
165 | o D draft | |||
|
166 | | | |||
|
167 | o C draft | |||
|
168 | | | |||
|
169 | o B draft | |||
|
170 | | | |||
|
171 | o A public | |||
|
172 | ||||
|
173 | $ cd .. | |||
|
174 | ||||
|
175 | Set up repo with non-linear history | |||
|
176 | $ hg init non-linear | |||
|
177 | $ cd non-linear | |||
|
178 | $ hg debugdrawdag <<'EOF' | |||
|
179 | > D E | |||
|
180 | > |\| | |||
|
181 | > B C | |||
|
182 | > |/ | |||
|
183 | > A | |||
|
184 | > EOF | |||
|
185 | $ hg phase --public C | |||
|
186 | $ hg phase --force --secret B | |||
|
187 | $ hg log -G -T '{node|short} {desc} {phase}\n' | |||
|
188 | o 03ca77807e91 E draft | |||
|
189 | | | |||
|
190 | | o 215e7b0814e1 D secret | |||
|
191 | |/| | |||
|
192 | o | dc0947a82db8 C public | |||
|
193 | | | | |||
|
194 | | o 112478962961 B secret | |||
|
195 | |/ | |||
|
196 | o 426bada5c675 A public | |||
|
197 | ||||
|
198 | ||||
|
199 | Restore bundle of entire repo | |||
|
200 | $ hg bundle -a bundle | |||
|
201 | 5 changesets found | |||
|
202 | $ hg debugbundle bundle | |||
|
203 | Stream params: sortdict([('Compression', 'BZ')]) | |||
|
204 | changegroup -- "sortdict([('version', '02'), ('nbchanges', '5')])" | |||
|
205 | 426bada5c67598ca65036d57d9e4b64b0c1ce7a0 | |||
|
206 | 112478962961147124edd43549aedd1a335e44bf | |||
|
207 | dc0947a82db884575bb76ea10ac97b08536bfa03 | |||
|
208 | 215e7b0814e1cac8e2614e7284f2a5dc266b4323 | |||
|
209 | 03ca77807e919db8807c3749086dc36fb478cac0 | |||
|
210 | phase-heads -- 'sortdict()' | |||
|
211 | dc0947a82db884575bb76ea10ac97b08536bfa03 public | |||
|
212 | 03ca77807e919db8807c3749086dc36fb478cac0 draft | |||
|
213 | $ hg strip --no-backup A | |||
|
214 | $ hg unbundle -q bundle | |||
|
215 | $ rm bundle | |||
|
216 | $ hg log -G -T '{node|short} {desc} {phase}\n' | |||
|
217 | o 03ca77807e91 E draft | |||
|
218 | | | |||
|
219 | | o 215e7b0814e1 D secret | |||
|
220 | |/| | |||
|
221 | o | dc0947a82db8 C public | |||
|
222 | | | | |||
|
223 | | o 112478962961 B secret | |||
|
224 | |/ | |||
|
225 | o 426bada5c675 A public | |||
|
226 | ||||
|
227 | ||||
|
228 | $ hg bundle --base 'A + C' -r D bundle | |||
|
229 | 2 changesets found | |||
|
230 | $ hg debugbundle bundle | |||
|
231 | Stream params: sortdict([('Compression', 'BZ')]) | |||
|
232 | changegroup -- "sortdict([('version', '02'), ('nbchanges', '2')])" | |||
|
233 | 112478962961147124edd43549aedd1a335e44bf | |||
|
234 | 215e7b0814e1cac8e2614e7284f2a5dc266b4323 | |||
|
235 | phase-heads -- 'sortdict()' | |||
|
236 | $ rm bundle | |||
|
237 | ||||
|
238 | $ hg bundle --base A -r D bundle | |||
|
239 | 3 changesets found | |||
|
240 | $ hg debugbundle bundle | |||
|
241 | Stream params: sortdict([('Compression', 'BZ')]) | |||
|
242 | changegroup -- "sortdict([('version', '02'), ('nbchanges', '3')])" | |||
|
243 | 112478962961147124edd43549aedd1a335e44bf | |||
|
244 | dc0947a82db884575bb76ea10ac97b08536bfa03 | |||
|
245 | 215e7b0814e1cac8e2614e7284f2a5dc266b4323 | |||
|
246 | phase-heads -- 'sortdict()' | |||
|
247 | dc0947a82db884575bb76ea10ac97b08536bfa03 public | |||
|
248 | $ rm bundle | |||
|
249 | ||||
|
250 | $ hg bundle --base 'B + C' -r 'D + E' bundle | |||
|
251 | 2 changesets found | |||
|
252 | $ hg debugbundle bundle | |||
|
253 | Stream params: sortdict([('Compression', 'BZ')]) | |||
|
254 | changegroup -- "sortdict([('version', '02'), ('nbchanges', '2')])" | |||
|
255 | 215e7b0814e1cac8e2614e7284f2a5dc266b4323 | |||
|
256 | 03ca77807e919db8807c3749086dc36fb478cac0 | |||
|
257 | phase-heads -- 'sortdict()' | |||
|
258 | 03ca77807e919db8807c3749086dc36fb478cac0 draft | |||
|
259 | $ rm bundle |
@@ -158,6 +158,7 b' from . import (' | |||||
158 | changegroup, |
|
158 | changegroup, | |
159 | error, |
|
159 | error, | |
160 | obsolete, |
|
160 | obsolete, | |
|
161 | phases, | |||
161 | pushkey, |
|
162 | pushkey, | |
162 | pycompat, |
|
163 | pycompat, | |
163 | tags, |
|
164 | tags, | |
@@ -178,6 +179,8 b' urlreq = util.urlreq' | |||||
178 | _fpayloadsize = '>i' |
|
179 | _fpayloadsize = '>i' | |
179 | _fpartparamcount = '>BB' |
|
180 | _fpartparamcount = '>BB' | |
180 |
|
181 | |||
|
182 | _fphasesentry = '>i20s' | |||
|
183 | ||||
181 | preferedchunksize = 4096 |
|
184 | preferedchunksize = 4096 | |
182 |
|
185 | |||
183 | _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]') |
|
186 | _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]') | |
@@ -1387,6 +1390,14 b' def _addpartsfromopts(ui, repo, bundler,' | |||||
1387 | obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing) |
|
1390 | obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing) | |
1388 | buildobsmarkerspart(bundler, obsmarkers) |
|
1391 | buildobsmarkerspart(bundler, obsmarkers) | |
1389 |
|
1392 | |||
|
1393 | if opts.get('phases', False): | |||
|
1394 | headsbyphase = phases.subsetphaseheads(repo, outgoing.missing) | |||
|
1395 | phasedata = [] | |||
|
1396 | for phase in phases.allphases: | |||
|
1397 | for head in headsbyphase[phase]: | |||
|
1398 | phasedata.append(_pack(_fphasesentry, phase, head)) | |||
|
1399 | bundler.newpart('phase-heads', data=''.join(phasedata)) | |||
|
1400 | ||||
1390 | def addparttagsfnodescache(repo, bundler, outgoing): |
|
1401 | def addparttagsfnodescache(repo, bundler, outgoing): | |
1391 | # we include the tags fnode cache for the bundle changeset |
|
1402 | # we include the tags fnode cache for the bundle changeset | |
1392 | # (as an optional parts) |
|
1403 | # (as an optional parts) | |
@@ -1721,6 +1732,29 b' def handlepushkey(op, inpart):' | |||||
1721 | kwargs[key] = inpart.params[key] |
|
1732 | kwargs[key] = inpart.params[key] | |
1722 | raise error.PushkeyFailed(partid=str(inpart.id), **kwargs) |
|
1733 | raise error.PushkeyFailed(partid=str(inpart.id), **kwargs) | |
1723 |
|
1734 | |||
|
1735 | def _readphaseheads(inpart): | |||
|
1736 | headsbyphase = [[] for i in phases.allphases] | |||
|
1737 | entrysize = struct.calcsize(_fphasesentry) | |||
|
1738 | while True: | |||
|
1739 | entry = inpart.read(entrysize) | |||
|
1740 | if len(entry) < entrysize: | |||
|
1741 | if entry: | |||
|
1742 | raise error.Abort(_('bad phase-heads bundle part')) | |||
|
1743 | break | |||
|
1744 | phase, node = struct.unpack(_fphasesentry, entry) | |||
|
1745 | headsbyphase[phase].append(node) | |||
|
1746 | return headsbyphase | |||
|
1747 | ||||
|
1748 | @parthandler('phase-heads') | |||
|
1749 | def handlephases(op, inpart): | |||
|
1750 | """apply phases from bundle part to repo""" | |||
|
1751 | headsbyphase = _readphaseheads(inpart) | |||
|
1752 | addednodes = [] | |||
|
1753 | for entry in op.records['changegroup']: | |||
|
1754 | addednodes.extend(entry['addednodes']) | |||
|
1755 | phases.updatephases(op.repo.unfiltered(), op.gettransaction(), headsbyphase, | |||
|
1756 | addednodes) | |||
|
1757 | ||||
1724 | @parthandler('reply:pushkey', ('return', 'in-reply-to')) |
|
1758 | @parthandler('reply:pushkey', ('return', 'in-reply-to')) | |
1725 | def handlepushkeyreply(op, inpart): |
|
1759 | def handlepushkeyreply(op, inpart): | |
1726 | """retrieve the result of a pushkey request""" |
|
1760 | """retrieve the result of a pushkey request""" |
@@ -1230,6 +1230,8 b' def bundle(ui, repo, fname, dest=None, *' | |||||
1230 | contentopts = {'cg.version': cgversion} |
|
1230 | contentopts = {'cg.version': cgversion} | |
1231 | if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker', False): |
|
1231 | if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker', False): | |
1232 | contentopts['obsolescence'] = True |
|
1232 | contentopts['obsolescence'] = True | |
|
1233 | if repo.ui.configbool('experimental', 'bundle-phases', False): | |||
|
1234 | contentopts['phases'] = True | |||
1233 | bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing, |
|
1235 | bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing, | |
1234 | contentopts, compression=bcompression, |
|
1236 | contentopts, compression=bcompression, | |
1235 | compopts=compopts) |
|
1237 | compopts=compopts) |
@@ -311,6 +311,15 b' def _debugobsmarkers(ui, part, indent=0,' | |||||
311 | cmdutil.showmarker(fm, m) |
|
311 | cmdutil.showmarker(fm, m) | |
312 | fm.end() |
|
312 | fm.end() | |
313 |
|
313 | |||
|
314 | def _debugphaseheads(ui, data, indent=0): | |||
|
315 | """display version and markers contained in 'data'""" | |||
|
316 | indent_string = ' ' * indent | |||
|
317 | headsbyphase = bundle2._readphaseheads(data) | |||
|
318 | for phase in phases.allphases: | |||
|
319 | for head in headsbyphase[phase]: | |||
|
320 | ui.write(indent_string) | |||
|
321 | ui.write('%s %s\n' % (hex(head), phases.phasenames[phase])) | |||
|
322 | ||||
314 | def _debugbundle2(ui, gen, all=None, **opts): |
|
323 | def _debugbundle2(ui, gen, all=None, **opts): | |
315 | """lists the contents of a bundle2""" |
|
324 | """lists the contents of a bundle2""" | |
316 | if not isinstance(gen, bundle2.unbundle20): |
|
325 | if not isinstance(gen, bundle2.unbundle20): | |
@@ -327,6 +336,8 b' def _debugbundle2(ui, gen, all=None, **o' | |||||
327 | _debugchangegroup(ui, cg, all=all, indent=4, **opts) |
|
336 | _debugchangegroup(ui, cg, all=all, indent=4, **opts) | |
328 | if part.type == 'obsmarkers': |
|
337 | if part.type == 'obsmarkers': | |
329 | _debugobsmarkers(ui, part, indent=4, **opts) |
|
338 | _debugobsmarkers(ui, part, indent=4, **opts) | |
|
339 | if part.type == 'phase-heads': | |||
|
340 | _debugphaseheads(ui, part, indent=4) | |||
330 |
|
341 | |||
331 | @command('debugbundle', |
|
342 | @command('debugbundle', | |
332 | [('a', 'all', None, _('show all details')), |
|
343 | [('a', 'all', None, _('show all details')), |
@@ -430,6 +430,32 b' def pushphase(repo, nhex, oldphasestr, n' | |||||
430 | else: |
|
430 | else: | |
431 | return False |
|
431 | return False | |
432 |
|
432 | |||
|
433 | def subsetphaseheads(repo, subset): | |||
|
434 | """Finds the phase heads for a subset of a history | |||
|
435 | ||||
|
436 | Returns a list indexed by phase number where each item is a list of phase | |||
|
437 | head nodes. | |||
|
438 | """ | |||
|
439 | cl = repo.changelog | |||
|
440 | ||||
|
441 | headsbyphase = [[] for i in allphases] | |||
|
442 | # No need to keep track of secret phase; any heads in the subset that | |||
|
443 | # are not mentioned are implicitly secret. | |||
|
444 | for phase in allphases[:-1]: | |||
|
445 | revset = "heads(%%ln & %s())" % phasenames[phase] | |||
|
446 | headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)] | |||
|
447 | return headsbyphase | |||
|
448 | ||||
|
449 | def updatephases(repo, tr, headsbyphase, addednodes): | |||
|
450 | """Updates the repo with the given phase heads""" | |||
|
451 | # First make all the added revisions secret because changegroup.apply() | |||
|
452 | # currently sets the phase to draft. | |||
|
453 | retractboundary(repo, tr, secret, addednodes) | |||
|
454 | ||||
|
455 | # Now advance phase boundaries of all but secret phase | |||
|
456 | for phase in allphases[:-1]: | |||
|
457 | advanceboundary(repo, tr, phase, headsbyphase[phase]) | |||
|
458 | ||||
433 | def analyzeremotephases(repo, subset, roots): |
|
459 | def analyzeremotephases(repo, subset, roots): | |
434 | """Compute phases heads and root in a subset of node from root dict |
|
460 | """Compute phases heads and root in a subset of node from root dict | |
435 |
|
461 |
General Comments 0
You need to be logged in to leave comments.
Login now