##// END OF EJS Templates
repair: preserve phase also when not using generaldelta (issue5678)...
Martin von Zweigbergk -
r34179:91f0677d stable
parent child Browse files
Show More
@@ -1,1009 +1,1014 b''
1 # changegroup.py - Mercurial changegroup manipulation functions
1 # changegroup.py - Mercurial changegroup manipulation functions
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import struct
11 import struct
12 import tempfile
12 import tempfile
13 import weakref
13 import weakref
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullrev,
18 nullrev,
19 short,
19 short,
20 )
20 )
21
21
22 from . import (
22 from . import (
23 dagutil,
23 dagutil,
24 discovery,
24 discovery,
25 error,
25 error,
26 mdiff,
26 mdiff,
27 phases,
27 phases,
28 pycompat,
28 pycompat,
29 util,
29 util,
30 )
30 )
31
31
32 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
32 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
33 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
33 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
34 _CHANGEGROUPV3_DELTA_HEADER = ">20s20s20s20s20sH"
34 _CHANGEGROUPV3_DELTA_HEADER = ">20s20s20s20s20sH"
35
35
36 def readexactly(stream, n):
36 def readexactly(stream, n):
37 '''read n bytes from stream.read and abort if less was available'''
37 '''read n bytes from stream.read and abort if less was available'''
38 s = stream.read(n)
38 s = stream.read(n)
39 if len(s) < n:
39 if len(s) < n:
40 raise error.Abort(_("stream ended unexpectedly"
40 raise error.Abort(_("stream ended unexpectedly"
41 " (got %d bytes, expected %d)")
41 " (got %d bytes, expected %d)")
42 % (len(s), n))
42 % (len(s), n))
43 return s
43 return s
44
44
45 def getchunk(stream):
45 def getchunk(stream):
46 """return the next chunk from stream as a string"""
46 """return the next chunk from stream as a string"""
47 d = readexactly(stream, 4)
47 d = readexactly(stream, 4)
48 l = struct.unpack(">l", d)[0]
48 l = struct.unpack(">l", d)[0]
49 if l <= 4:
49 if l <= 4:
50 if l:
50 if l:
51 raise error.Abort(_("invalid chunk length %d") % l)
51 raise error.Abort(_("invalid chunk length %d") % l)
52 return ""
52 return ""
53 return readexactly(stream, l - 4)
53 return readexactly(stream, l - 4)
54
54
55 def chunkheader(length):
55 def chunkheader(length):
56 """return a changegroup chunk header (string)"""
56 """return a changegroup chunk header (string)"""
57 return struct.pack(">l", length + 4)
57 return struct.pack(">l", length + 4)
58
58
59 def closechunk():
59 def closechunk():
60 """return a changegroup chunk header (string) for a zero-length chunk"""
60 """return a changegroup chunk header (string) for a zero-length chunk"""
61 return struct.pack(">l", 0)
61 return struct.pack(">l", 0)
62
62
63 def writechunks(ui, chunks, filename, vfs=None):
63 def writechunks(ui, chunks, filename, vfs=None):
64 """Write chunks to a file and return its filename.
64 """Write chunks to a file and return its filename.
65
65
66 The stream is assumed to be a bundle file.
66 The stream is assumed to be a bundle file.
67 Existing files will not be overwritten.
67 Existing files will not be overwritten.
68 If no filename is specified, a temporary file is created.
68 If no filename is specified, a temporary file is created.
69 """
69 """
70 fh = None
70 fh = None
71 cleanup = None
71 cleanup = None
72 try:
72 try:
73 if filename:
73 if filename:
74 if vfs:
74 if vfs:
75 fh = vfs.open(filename, "wb")
75 fh = vfs.open(filename, "wb")
76 else:
76 else:
77 # Increase default buffer size because default is usually
77 # Increase default buffer size because default is usually
78 # small (4k is common on Linux).
78 # small (4k is common on Linux).
79 fh = open(filename, "wb", 131072)
79 fh = open(filename, "wb", 131072)
80 else:
80 else:
81 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
81 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
82 fh = os.fdopen(fd, pycompat.sysstr("wb"))
82 fh = os.fdopen(fd, pycompat.sysstr("wb"))
83 cleanup = filename
83 cleanup = filename
84 for c in chunks:
84 for c in chunks:
85 fh.write(c)
85 fh.write(c)
86 cleanup = None
86 cleanup = None
87 return filename
87 return filename
88 finally:
88 finally:
89 if fh is not None:
89 if fh is not None:
90 fh.close()
90 fh.close()
91 if cleanup is not None:
91 if cleanup is not None:
92 if filename and vfs:
92 if filename and vfs:
93 vfs.unlink(cleanup)
93 vfs.unlink(cleanup)
94 else:
94 else:
95 os.unlink(cleanup)
95 os.unlink(cleanup)
96
96
97 class cg1unpacker(object):
97 class cg1unpacker(object):
98 """Unpacker for cg1 changegroup streams.
98 """Unpacker for cg1 changegroup streams.
99
99
100 A changegroup unpacker handles the framing of the revision data in
100 A changegroup unpacker handles the framing of the revision data in
101 the wire format. Most consumers will want to use the apply()
101 the wire format. Most consumers will want to use the apply()
102 method to add the changes from the changegroup to a repository.
102 method to add the changes from the changegroup to a repository.
103
103
104 If you're forwarding a changegroup unmodified to another consumer,
104 If you're forwarding a changegroup unmodified to another consumer,
105 use getchunks(), which returns an iterator of changegroup
105 use getchunks(), which returns an iterator of changegroup
106 chunks. This is mostly useful for cases where you need to know the
106 chunks. This is mostly useful for cases where you need to know the
107 data stream has ended by observing the end of the changegroup.
107 data stream has ended by observing the end of the changegroup.
108
108
109 deltachunk() is useful only if you're applying delta data. Most
109 deltachunk() is useful only if you're applying delta data. Most
110 consumers should prefer apply() instead.
110 consumers should prefer apply() instead.
111
111
112 A few other public methods exist. Those are used only for
112 A few other public methods exist. Those are used only for
113 bundlerepo and some debug commands - their use is discouraged.
113 bundlerepo and some debug commands - their use is discouraged.
114 """
114 """
115 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
115 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
116 deltaheadersize = struct.calcsize(deltaheader)
116 deltaheadersize = struct.calcsize(deltaheader)
117 version = '01'
117 version = '01'
118 _grouplistcount = 1 # One list of files after the manifests
118 _grouplistcount = 1 # One list of files after the manifests
119
119
120 def __init__(self, fh, alg, extras=None):
120 def __init__(self, fh, alg, extras=None):
121 if alg is None:
121 if alg is None:
122 alg = 'UN'
122 alg = 'UN'
123 if alg not in util.compengines.supportedbundletypes:
123 if alg not in util.compengines.supportedbundletypes:
124 raise error.Abort(_('unknown stream compression type: %s')
124 raise error.Abort(_('unknown stream compression type: %s')
125 % alg)
125 % alg)
126 if alg == 'BZ':
126 if alg == 'BZ':
127 alg = '_truncatedBZ'
127 alg = '_truncatedBZ'
128
128
129 compengine = util.compengines.forbundletype(alg)
129 compengine = util.compengines.forbundletype(alg)
130 self._stream = compengine.decompressorreader(fh)
130 self._stream = compengine.decompressorreader(fh)
131 self._type = alg
131 self._type = alg
132 self.extras = extras or {}
132 self.extras = extras or {}
133 self.callback = None
133 self.callback = None
134
134
135 # These methods (compressed, read, seek, tell) all appear to only
135 # These methods (compressed, read, seek, tell) all appear to only
136 # be used by bundlerepo, but it's a little hard to tell.
136 # be used by bundlerepo, but it's a little hard to tell.
137 def compressed(self):
137 def compressed(self):
138 return self._type is not None and self._type != 'UN'
138 return self._type is not None and self._type != 'UN'
139 def read(self, l):
139 def read(self, l):
140 return self._stream.read(l)
140 return self._stream.read(l)
141 def seek(self, pos):
141 def seek(self, pos):
142 return self._stream.seek(pos)
142 return self._stream.seek(pos)
143 def tell(self):
143 def tell(self):
144 return self._stream.tell()
144 return self._stream.tell()
145 def close(self):
145 def close(self):
146 return self._stream.close()
146 return self._stream.close()
147
147
148 def _chunklength(self):
148 def _chunklength(self):
149 d = readexactly(self._stream, 4)
149 d = readexactly(self._stream, 4)
150 l = struct.unpack(">l", d)[0]
150 l = struct.unpack(">l", d)[0]
151 if l <= 4:
151 if l <= 4:
152 if l:
152 if l:
153 raise error.Abort(_("invalid chunk length %d") % l)
153 raise error.Abort(_("invalid chunk length %d") % l)
154 return 0
154 return 0
155 if self.callback:
155 if self.callback:
156 self.callback()
156 self.callback()
157 return l - 4
157 return l - 4
158
158
159 def changelogheader(self):
159 def changelogheader(self):
160 """v10 does not have a changelog header chunk"""
160 """v10 does not have a changelog header chunk"""
161 return {}
161 return {}
162
162
163 def manifestheader(self):
163 def manifestheader(self):
164 """v10 does not have a manifest header chunk"""
164 """v10 does not have a manifest header chunk"""
165 return {}
165 return {}
166
166
167 def filelogheader(self):
167 def filelogheader(self):
168 """return the header of the filelogs chunk, v10 only has the filename"""
168 """return the header of the filelogs chunk, v10 only has the filename"""
169 l = self._chunklength()
169 l = self._chunklength()
170 if not l:
170 if not l:
171 return {}
171 return {}
172 fname = readexactly(self._stream, l)
172 fname = readexactly(self._stream, l)
173 return {'filename': fname}
173 return {'filename': fname}
174
174
175 def _deltaheader(self, headertuple, prevnode):
175 def _deltaheader(self, headertuple, prevnode):
176 node, p1, p2, cs = headertuple
176 node, p1, p2, cs = headertuple
177 if prevnode is None:
177 if prevnode is None:
178 deltabase = p1
178 deltabase = p1
179 else:
179 else:
180 deltabase = prevnode
180 deltabase = prevnode
181 flags = 0
181 flags = 0
182 return node, p1, p2, deltabase, cs, flags
182 return node, p1, p2, deltabase, cs, flags
183
183
184 def deltachunk(self, prevnode):
184 def deltachunk(self, prevnode):
185 l = self._chunklength()
185 l = self._chunklength()
186 if not l:
186 if not l:
187 return {}
187 return {}
188 headerdata = readexactly(self._stream, self.deltaheadersize)
188 headerdata = readexactly(self._stream, self.deltaheadersize)
189 header = struct.unpack(self.deltaheader, headerdata)
189 header = struct.unpack(self.deltaheader, headerdata)
190 delta = readexactly(self._stream, l - self.deltaheadersize)
190 delta = readexactly(self._stream, l - self.deltaheadersize)
191 node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode)
191 node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode)
192 return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs,
192 return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs,
193 'deltabase': deltabase, 'delta': delta, 'flags': flags}
193 'deltabase': deltabase, 'delta': delta, 'flags': flags}
194
194
195 def getchunks(self):
195 def getchunks(self):
196 """returns all the chunks contains in the bundle
196 """returns all the chunks contains in the bundle
197
197
198 Used when you need to forward the binary stream to a file or another
198 Used when you need to forward the binary stream to a file or another
199 network API. To do so, it parse the changegroup data, otherwise it will
199 network API. To do so, it parse the changegroup data, otherwise it will
200 block in case of sshrepo because it don't know the end of the stream.
200 block in case of sshrepo because it don't know the end of the stream.
201 """
201 """
202 # an empty chunkgroup is the end of the changegroup
202 # an empty chunkgroup is the end of the changegroup
203 # a changegroup has at least 2 chunkgroups (changelog and manifest).
203 # a changegroup has at least 2 chunkgroups (changelog and manifest).
204 # after that, changegroup versions 1 and 2 have a series of groups
204 # after that, changegroup versions 1 and 2 have a series of groups
205 # with one group per file. changegroup 3 has a series of directory
205 # with one group per file. changegroup 3 has a series of directory
206 # manifests before the files.
206 # manifests before the files.
207 count = 0
207 count = 0
208 emptycount = 0
208 emptycount = 0
209 while emptycount < self._grouplistcount:
209 while emptycount < self._grouplistcount:
210 empty = True
210 empty = True
211 count += 1
211 count += 1
212 while True:
212 while True:
213 chunk = getchunk(self)
213 chunk = getchunk(self)
214 if not chunk:
214 if not chunk:
215 if empty and count > 2:
215 if empty and count > 2:
216 emptycount += 1
216 emptycount += 1
217 break
217 break
218 empty = False
218 empty = False
219 yield chunkheader(len(chunk))
219 yield chunkheader(len(chunk))
220 pos = 0
220 pos = 0
221 while pos < len(chunk):
221 while pos < len(chunk):
222 next = pos + 2**20
222 next = pos + 2**20
223 yield chunk[pos:next]
223 yield chunk[pos:next]
224 pos = next
224 pos = next
225 yield closechunk()
225 yield closechunk()
226
226
227 def _unpackmanifests(self, repo, revmap, trp, prog, numchanges):
227 def _unpackmanifests(self, repo, revmap, trp, prog, numchanges):
228 # We know that we'll never have more manifests than we had
228 # We know that we'll never have more manifests than we had
229 # changesets.
229 # changesets.
230 self.callback = prog(_('manifests'), numchanges)
230 self.callback = prog(_('manifests'), numchanges)
231 # no need to check for empty manifest group here:
231 # no need to check for empty manifest group here:
232 # if the result of the merge of 1 and 2 is the same in 3 and 4,
232 # if the result of the merge of 1 and 2 is the same in 3 and 4,
233 # no new manifest will be created and the manifest group will
233 # no new manifest will be created and the manifest group will
234 # be empty during the pull
234 # be empty during the pull
235 self.manifestheader()
235 self.manifestheader()
236 repo.manifestlog._revlog.addgroup(self, revmap, trp)
236 repo.manifestlog._revlog.addgroup(self, revmap, trp)
237 repo.ui.progress(_('manifests'), None)
237 repo.ui.progress(_('manifests'), None)
238 self.callback = None
238 self.callback = None
239
239
240 def apply(self, repo, tr, srctype, url, targetphase=phases.draft,
240 def apply(self, repo, tr, srctype, url, targetphase=phases.draft,
241 expectedtotal=None):
241 expectedtotal=None):
242 """Add the changegroup returned by source.read() to this repo.
242 """Add the changegroup returned by source.read() to this repo.
243 srctype is a string like 'push', 'pull', or 'unbundle'. url is
243 srctype is a string like 'push', 'pull', or 'unbundle'. url is
244 the URL of the repo where this changegroup is coming from.
244 the URL of the repo where this changegroup is coming from.
245
245
246 Return an integer summarizing the change to this repo:
246 Return an integer summarizing the change to this repo:
247 - nothing changed or no source: 0
247 - nothing changed or no source: 0
248 - more heads than before: 1+added heads (2..n)
248 - more heads than before: 1+added heads (2..n)
249 - fewer heads than before: -1-removed heads (-2..-n)
249 - fewer heads than before: -1-removed heads (-2..-n)
250 - number of heads stays the same: 1
250 - number of heads stays the same: 1
251 """
251 """
252 repo = repo.unfiltered()
252 repo = repo.unfiltered()
253 def csmap(x):
253 def csmap(x):
254 repo.ui.debug("add changeset %s\n" % short(x))
254 repo.ui.debug("add changeset %s\n" % short(x))
255 return len(cl)
255 return len(cl)
256
256
257 def revmap(x):
257 def revmap(x):
258 return cl.rev(x)
258 return cl.rev(x)
259
259
260 changesets = files = revisions = 0
260 changesets = files = revisions = 0
261
261
262 try:
262 try:
263 # The transaction may already carry source information. In this
263 # The transaction may already carry source information. In this
264 # case we use the top level data. We overwrite the argument
264 # case we use the top level data. We overwrite the argument
265 # because we need to use the top level value (if they exist)
265 # because we need to use the top level value (if they exist)
266 # in this function.
266 # in this function.
267 srctype = tr.hookargs.setdefault('source', srctype)
267 srctype = tr.hookargs.setdefault('source', srctype)
268 url = tr.hookargs.setdefault('url', url)
268 url = tr.hookargs.setdefault('url', url)
269 repo.hook('prechangegroup', throw=True, **tr.hookargs)
269 repo.hook('prechangegroup', throw=True, **tr.hookargs)
270
270
271 # write changelog data to temp files so concurrent readers
271 # write changelog data to temp files so concurrent readers
272 # will not see an inconsistent view
272 # will not see an inconsistent view
273 cl = repo.changelog
273 cl = repo.changelog
274 cl.delayupdate(tr)
274 cl.delayupdate(tr)
275 oldheads = set(cl.heads())
275 oldheads = set(cl.heads())
276
276
277 trp = weakref.proxy(tr)
277 trp = weakref.proxy(tr)
278 # pull off the changeset group
278 # pull off the changeset group
279 repo.ui.status(_("adding changesets\n"))
279 repo.ui.status(_("adding changesets\n"))
280 clstart = len(cl)
280 clstart = len(cl)
281 class prog(object):
281 class prog(object):
282 def __init__(self, step, total):
282 def __init__(self, step, total):
283 self._step = step
283 self._step = step
284 self._total = total
284 self._total = total
285 self._count = 1
285 self._count = 1
286 def __call__(self):
286 def __call__(self):
287 repo.ui.progress(self._step, self._count, unit=_('chunks'),
287 repo.ui.progress(self._step, self._count, unit=_('chunks'),
288 total=self._total)
288 total=self._total)
289 self._count += 1
289 self._count += 1
290 self.callback = prog(_('changesets'), expectedtotal)
290 self.callback = prog(_('changesets'), expectedtotal)
291
291
292 efiles = set()
292 efiles = set()
293 def onchangelog(cl, node):
293 def onchangelog(cl, node):
294 efiles.update(cl.readfiles(node))
294 efiles.update(cl.readfiles(node))
295
295
296 self.changelogheader()
296 self.changelogheader()
297 cgnodes = cl.addgroup(self, csmap, trp, addrevisioncb=onchangelog)
297 cgnodes = cl.addgroup(self, csmap, trp, addrevisioncb=onchangelog)
298 efiles = len(efiles)
298 efiles = len(efiles)
299
299
300 if not cgnodes:
300 if not cgnodes:
301 repo.ui.develwarn('applied empty changegroup',
301 repo.ui.develwarn('applied empty changegroup',
302 config='empty-changegroup')
302 config='empty-changegroup')
303 clend = len(cl)
303 clend = len(cl)
304 changesets = clend - clstart
304 changesets = clend - clstart
305 repo.ui.progress(_('changesets'), None)
305 repo.ui.progress(_('changesets'), None)
306 self.callback = None
306 self.callback = None
307
307
308 # pull off the manifest group
308 # pull off the manifest group
309 repo.ui.status(_("adding manifests\n"))
309 repo.ui.status(_("adding manifests\n"))
310 self._unpackmanifests(repo, revmap, trp, prog, changesets)
310 self._unpackmanifests(repo, revmap, trp, prog, changesets)
311
311
312 needfiles = {}
312 needfiles = {}
313 if repo.ui.configbool('server', 'validate'):
313 if repo.ui.configbool('server', 'validate'):
314 cl = repo.changelog
314 cl = repo.changelog
315 ml = repo.manifestlog
315 ml = repo.manifestlog
316 # validate incoming csets have their manifests
316 # validate incoming csets have their manifests
317 for cset in xrange(clstart, clend):
317 for cset in xrange(clstart, clend):
318 mfnode = cl.changelogrevision(cset).manifest
318 mfnode = cl.changelogrevision(cset).manifest
319 mfest = ml[mfnode].readdelta()
319 mfest = ml[mfnode].readdelta()
320 # store file cgnodes we must see
320 # store file cgnodes we must see
321 for f, n in mfest.iteritems():
321 for f, n in mfest.iteritems():
322 needfiles.setdefault(f, set()).add(n)
322 needfiles.setdefault(f, set()).add(n)
323
323
324 # process the files
324 # process the files
325 repo.ui.status(_("adding file changes\n"))
325 repo.ui.status(_("adding file changes\n"))
326 newrevs, newfiles = _addchangegroupfiles(
326 newrevs, newfiles = _addchangegroupfiles(
327 repo, self, revmap, trp, efiles, needfiles)
327 repo, self, revmap, trp, efiles, needfiles)
328 revisions += newrevs
328 revisions += newrevs
329 files += newfiles
329 files += newfiles
330
330
331 deltaheads = 0
331 deltaheads = 0
332 if oldheads:
332 if oldheads:
333 heads = cl.heads()
333 heads = cl.heads()
334 deltaheads = len(heads) - len(oldheads)
334 deltaheads = len(heads) - len(oldheads)
335 for h in heads:
335 for h in heads:
336 if h not in oldheads and repo[h].closesbranch():
336 if h not in oldheads and repo[h].closesbranch():
337 deltaheads -= 1
337 deltaheads -= 1
338 htext = ""
338 htext = ""
339 if deltaheads:
339 if deltaheads:
340 htext = _(" (%+d heads)") % deltaheads
340 htext = _(" (%+d heads)") % deltaheads
341
341
342 repo.ui.status(_("added %d changesets"
342 repo.ui.status(_("added %d changesets"
343 " with %d changes to %d files%s\n")
343 " with %d changes to %d files%s\n")
344 % (changesets, revisions, files, htext))
344 % (changesets, revisions, files, htext))
345 repo.invalidatevolatilesets()
345 repo.invalidatevolatilesets()
346
346
347 if changesets > 0:
347 if changesets > 0:
348 if 'node' not in tr.hookargs:
348 if 'node' not in tr.hookargs:
349 tr.hookargs['node'] = hex(cl.node(clstart))
349 tr.hookargs['node'] = hex(cl.node(clstart))
350 tr.hookargs['node_last'] = hex(cl.node(clend - 1))
350 tr.hookargs['node_last'] = hex(cl.node(clend - 1))
351 hookargs = dict(tr.hookargs)
351 hookargs = dict(tr.hookargs)
352 else:
352 else:
353 hookargs = dict(tr.hookargs)
353 hookargs = dict(tr.hookargs)
354 hookargs['node'] = hex(cl.node(clstart))
354 hookargs['node'] = hex(cl.node(clstart))
355 hookargs['node_last'] = hex(cl.node(clend - 1))
355 hookargs['node_last'] = hex(cl.node(clend - 1))
356 repo.hook('pretxnchangegroup', throw=True, **hookargs)
356 repo.hook('pretxnchangegroup', throw=True, **hookargs)
357
357
358 added = [cl.node(r) for r in xrange(clstart, clend)]
358 added = [cl.node(r) for r in xrange(clstart, clend)]
359 phaseall = None
359 phaseall = None
360 if srctype in ('push', 'serve'):
360 if srctype in ('push', 'serve'):
361 # Old servers can not push the boundary themselves.
361 # Old servers can not push the boundary themselves.
362 # New servers won't push the boundary if changeset already
362 # New servers won't push the boundary if changeset already
363 # exists locally as secret
363 # exists locally as secret
364 #
364 #
365 # We should not use added here but the list of all change in
365 # We should not use added here but the list of all change in
366 # the bundle
366 # the bundle
367 if repo.publishing():
367 if repo.publishing():
368 targetphase = phaseall = phases.public
368 targetphase = phaseall = phases.public
369 else:
369 else:
370 # closer target phase computation
370 # closer target phase computation
371
371
372 # Those changesets have been pushed from the
372 # Those changesets have been pushed from the
373 # outside, their phases are going to be pushed
373 # outside, their phases are going to be pushed
374 # alongside. Therefor `targetphase` is
374 # alongside. Therefor `targetphase` is
375 # ignored.
375 # ignored.
376 targetphase = phaseall = phases.draft
376 targetphase = phaseall = phases.draft
377 if added:
377 if added:
378 phases.registernew(repo, tr, targetphase, added)
378 phases.registernew(repo, tr, targetphase, added)
379 if phaseall is not None:
379 if phaseall is not None:
380 phases.advanceboundary(repo, tr, phaseall, cgnodes)
380 phases.advanceboundary(repo, tr, phaseall, cgnodes)
381
381
382 if changesets > 0:
382 if changesets > 0:
383
383
384 def runhooks():
384 def runhooks():
385 # These hooks run when the lock releases, not when the
385 # These hooks run when the lock releases, not when the
386 # transaction closes. So it's possible for the changelog
386 # transaction closes. So it's possible for the changelog
387 # to have changed since we last saw it.
387 # to have changed since we last saw it.
388 if clstart >= len(repo):
388 if clstart >= len(repo):
389 return
389 return
390
390
391 repo.hook("changegroup", **hookargs)
391 repo.hook("changegroup", **hookargs)
392
392
393 for n in added:
393 for n in added:
394 args = hookargs.copy()
394 args = hookargs.copy()
395 args['node'] = hex(n)
395 args['node'] = hex(n)
396 del args['node_last']
396 del args['node_last']
397 repo.hook("incoming", **args)
397 repo.hook("incoming", **args)
398
398
399 newheads = [h for h in repo.heads()
399 newheads = [h for h in repo.heads()
400 if h not in oldheads]
400 if h not in oldheads]
401 repo.ui.log("incoming",
401 repo.ui.log("incoming",
402 "%s incoming changes - new heads: %s\n",
402 "%s incoming changes - new heads: %s\n",
403 len(added),
403 len(added),
404 ', '.join([hex(c[:6]) for c in newheads]))
404 ', '.join([hex(c[:6]) for c in newheads]))
405
405
406 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
406 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
407 lambda tr: repo._afterlock(runhooks))
407 lambda tr: repo._afterlock(runhooks))
408 finally:
408 finally:
409 repo.ui.flush()
409 repo.ui.flush()
410 # never return 0 here:
410 # never return 0 here:
411 if deltaheads < 0:
411 if deltaheads < 0:
412 ret = deltaheads - 1
412 ret = deltaheads - 1
413 else:
413 else:
414 ret = deltaheads + 1
414 ret = deltaheads + 1
415 return ret
415 return ret
416
416
417 class cg2unpacker(cg1unpacker):
417 class cg2unpacker(cg1unpacker):
418 """Unpacker for cg2 streams.
418 """Unpacker for cg2 streams.
419
419
420 cg2 streams add support for generaldelta, so the delta header
420 cg2 streams add support for generaldelta, so the delta header
421 format is slightly different. All other features about the data
421 format is slightly different. All other features about the data
422 remain the same.
422 remain the same.
423 """
423 """
424 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
424 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
425 deltaheadersize = struct.calcsize(deltaheader)
425 deltaheadersize = struct.calcsize(deltaheader)
426 version = '02'
426 version = '02'
427
427
428 def _deltaheader(self, headertuple, prevnode):
428 def _deltaheader(self, headertuple, prevnode):
429 node, p1, p2, deltabase, cs = headertuple
429 node, p1, p2, deltabase, cs = headertuple
430 flags = 0
430 flags = 0
431 return node, p1, p2, deltabase, cs, flags
431 return node, p1, p2, deltabase, cs, flags
432
432
433 class cg3unpacker(cg2unpacker):
433 class cg3unpacker(cg2unpacker):
434 """Unpacker for cg3 streams.
434 """Unpacker for cg3 streams.
435
435
436 cg3 streams add support for exchanging treemanifests and revlog
436 cg3 streams add support for exchanging treemanifests and revlog
437 flags. It adds the revlog flags to the delta header and an empty chunk
437 flags. It adds the revlog flags to the delta header and an empty chunk
438 separating manifests and files.
438 separating manifests and files.
439 """
439 """
440 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
440 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
441 deltaheadersize = struct.calcsize(deltaheader)
441 deltaheadersize = struct.calcsize(deltaheader)
442 version = '03'
442 version = '03'
443 _grouplistcount = 2 # One list of manifests and one list of files
443 _grouplistcount = 2 # One list of manifests and one list of files
444
444
445 def _deltaheader(self, headertuple, prevnode):
445 def _deltaheader(self, headertuple, prevnode):
446 node, p1, p2, deltabase, cs, flags = headertuple
446 node, p1, p2, deltabase, cs, flags = headertuple
447 return node, p1, p2, deltabase, cs, flags
447 return node, p1, p2, deltabase, cs, flags
448
448
449 def _unpackmanifests(self, repo, revmap, trp, prog, numchanges):
449 def _unpackmanifests(self, repo, revmap, trp, prog, numchanges):
450 super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog,
450 super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog,
451 numchanges)
451 numchanges)
452 for chunkdata in iter(self.filelogheader, {}):
452 for chunkdata in iter(self.filelogheader, {}):
453 # If we get here, there are directory manifests in the changegroup
453 # If we get here, there are directory manifests in the changegroup
454 d = chunkdata["filename"]
454 d = chunkdata["filename"]
455 repo.ui.debug("adding %s revisions\n" % d)
455 repo.ui.debug("adding %s revisions\n" % d)
456 dirlog = repo.manifestlog._revlog.dirlog(d)
456 dirlog = repo.manifestlog._revlog.dirlog(d)
457 if not dirlog.addgroup(self, revmap, trp):
457 if not dirlog.addgroup(self, revmap, trp):
458 raise error.Abort(_("received dir revlog group is empty"))
458 raise error.Abort(_("received dir revlog group is empty"))
459
459
460 class headerlessfixup(object):
460 class headerlessfixup(object):
461 def __init__(self, fh, h):
461 def __init__(self, fh, h):
462 self._h = h
462 self._h = h
463 self._fh = fh
463 self._fh = fh
464 def read(self, n):
464 def read(self, n):
465 if self._h:
465 if self._h:
466 d, self._h = self._h[:n], self._h[n:]
466 d, self._h = self._h[:n], self._h[n:]
467 if len(d) < n:
467 if len(d) < n:
468 d += readexactly(self._fh, n - len(d))
468 d += readexactly(self._fh, n - len(d))
469 return d
469 return d
470 return readexactly(self._fh, n)
470 return readexactly(self._fh, n)
471
471
472 class cg1packer(object):
472 class cg1packer(object):
473 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
473 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
474 version = '01'
474 version = '01'
475 def __init__(self, repo, bundlecaps=None):
475 def __init__(self, repo, bundlecaps=None):
476 """Given a source repo, construct a bundler.
476 """Given a source repo, construct a bundler.
477
477
478 bundlecaps is optional and can be used to specify the set of
478 bundlecaps is optional and can be used to specify the set of
479 capabilities which can be used to build the bundle. While bundlecaps is
479 capabilities which can be used to build the bundle. While bundlecaps is
480 unused in core Mercurial, extensions rely on this feature to communicate
480 unused in core Mercurial, extensions rely on this feature to communicate
481 capabilities to customize the changegroup packer.
481 capabilities to customize the changegroup packer.
482 """
482 """
483 # Set of capabilities we can use to build the bundle.
483 # Set of capabilities we can use to build the bundle.
484 if bundlecaps is None:
484 if bundlecaps is None:
485 bundlecaps = set()
485 bundlecaps = set()
486 self._bundlecaps = bundlecaps
486 self._bundlecaps = bundlecaps
487 # experimental config: bundle.reorder
487 # experimental config: bundle.reorder
488 reorder = repo.ui.config('bundle', 'reorder')
488 reorder = repo.ui.config('bundle', 'reorder')
489 if reorder == 'auto':
489 if reorder == 'auto':
490 reorder = None
490 reorder = None
491 else:
491 else:
492 reorder = util.parsebool(reorder)
492 reorder = util.parsebool(reorder)
493 self._repo = repo
493 self._repo = repo
494 self._reorder = reorder
494 self._reorder = reorder
495 self._progress = repo.ui.progress
495 self._progress = repo.ui.progress
496 if self._repo.ui.verbose and not self._repo.ui.debugflag:
496 if self._repo.ui.verbose and not self._repo.ui.debugflag:
497 self._verbosenote = self._repo.ui.note
497 self._verbosenote = self._repo.ui.note
498 else:
498 else:
499 self._verbosenote = lambda s: None
499 self._verbosenote = lambda s: None
500
500
501 def close(self):
501 def close(self):
502 return closechunk()
502 return closechunk()
503
503
504 def fileheader(self, fname):
504 def fileheader(self, fname):
505 return chunkheader(len(fname)) + fname
505 return chunkheader(len(fname)) + fname
506
506
507 # Extracted both for clarity and for overriding in extensions.
507 # Extracted both for clarity and for overriding in extensions.
508 def _sortgroup(self, revlog, nodelist, lookup):
508 def _sortgroup(self, revlog, nodelist, lookup):
509 """Sort nodes for change group and turn them into revnums."""
509 """Sort nodes for change group and turn them into revnums."""
510 # for generaldelta revlogs, we linearize the revs; this will both be
510 # for generaldelta revlogs, we linearize the revs; this will both be
511 # much quicker and generate a much smaller bundle
511 # much quicker and generate a much smaller bundle
512 if (revlog._generaldelta and self._reorder is None) or self._reorder:
512 if (revlog._generaldelta and self._reorder is None) or self._reorder:
513 dag = dagutil.revlogdag(revlog)
513 dag = dagutil.revlogdag(revlog)
514 return dag.linearize(set(revlog.rev(n) for n in nodelist))
514 return dag.linearize(set(revlog.rev(n) for n in nodelist))
515 else:
515 else:
516 return sorted([revlog.rev(n) for n in nodelist])
516 return sorted([revlog.rev(n) for n in nodelist])
517
517
518 def group(self, nodelist, revlog, lookup, units=None):
518 def group(self, nodelist, revlog, lookup, units=None):
519 """Calculate a delta group, yielding a sequence of changegroup chunks
519 """Calculate a delta group, yielding a sequence of changegroup chunks
520 (strings).
520 (strings).
521
521
522 Given a list of changeset revs, return a set of deltas and
522 Given a list of changeset revs, return a set of deltas and
523 metadata corresponding to nodes. The first delta is
523 metadata corresponding to nodes. The first delta is
524 first parent(nodelist[0]) -> nodelist[0], the receiver is
524 first parent(nodelist[0]) -> nodelist[0], the receiver is
525 guaranteed to have this parent as it has all history before
525 guaranteed to have this parent as it has all history before
526 these changesets. In the case firstparent is nullrev the
526 these changesets. In the case firstparent is nullrev the
527 changegroup starts with a full revision.
527 changegroup starts with a full revision.
528
528
529 If units is not None, progress detail will be generated, units specifies
529 If units is not None, progress detail will be generated, units specifies
530 the type of revlog that is touched (changelog, manifest, etc.).
530 the type of revlog that is touched (changelog, manifest, etc.).
531 """
531 """
532 # if we don't have any revisions touched by these changesets, bail
532 # if we don't have any revisions touched by these changesets, bail
533 if len(nodelist) == 0:
533 if len(nodelist) == 0:
534 yield self.close()
534 yield self.close()
535 return
535 return
536
536
537 revs = self._sortgroup(revlog, nodelist, lookup)
537 revs = self._sortgroup(revlog, nodelist, lookup)
538
538
539 # add the parent of the first rev
539 # add the parent of the first rev
540 p = revlog.parentrevs(revs[0])[0]
540 p = revlog.parentrevs(revs[0])[0]
541 revs.insert(0, p)
541 revs.insert(0, p)
542
542
543 # build deltas
543 # build deltas
544 total = len(revs) - 1
544 total = len(revs) - 1
545 msgbundling = _('bundling')
545 msgbundling = _('bundling')
546 for r in xrange(len(revs) - 1):
546 for r in xrange(len(revs) - 1):
547 if units is not None:
547 if units is not None:
548 self._progress(msgbundling, r + 1, unit=units, total=total)
548 self._progress(msgbundling, r + 1, unit=units, total=total)
549 prev, curr = revs[r], revs[r + 1]
549 prev, curr = revs[r], revs[r + 1]
550 linknode = lookup(revlog.node(curr))
550 linknode = lookup(revlog.node(curr))
551 for c in self.revchunk(revlog, curr, prev, linknode):
551 for c in self.revchunk(revlog, curr, prev, linknode):
552 yield c
552 yield c
553
553
554 if units is not None:
554 if units is not None:
555 self._progress(msgbundling, None)
555 self._progress(msgbundling, None)
556 yield self.close()
556 yield self.close()
557
557
558 # filter any nodes that claim to be part of the known set
558 # filter any nodes that claim to be part of the known set
559 def prune(self, revlog, missing, commonrevs):
559 def prune(self, revlog, missing, commonrevs):
560 rr, rl = revlog.rev, revlog.linkrev
560 rr, rl = revlog.rev, revlog.linkrev
561 return [n for n in missing if rl(rr(n)) not in commonrevs]
561 return [n for n in missing if rl(rr(n)) not in commonrevs]
562
562
563 def _packmanifests(self, dir, mfnodes, lookuplinknode):
563 def _packmanifests(self, dir, mfnodes, lookuplinknode):
564 """Pack flat manifests into a changegroup stream."""
564 """Pack flat manifests into a changegroup stream."""
565 assert not dir
565 assert not dir
566 for chunk in self.group(mfnodes, self._repo.manifestlog._revlog,
566 for chunk in self.group(mfnodes, self._repo.manifestlog._revlog,
567 lookuplinknode, units=_('manifests')):
567 lookuplinknode, units=_('manifests')):
568 yield chunk
568 yield chunk
569
569
570 def _manifestsdone(self):
570 def _manifestsdone(self):
571 return ''
571 return ''
572
572
573 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
573 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
574 '''yield a sequence of changegroup chunks (strings)'''
574 '''yield a sequence of changegroup chunks (strings)'''
575 repo = self._repo
575 repo = self._repo
576 cl = repo.changelog
576 cl = repo.changelog
577
577
578 clrevorder = {}
578 clrevorder = {}
579 mfs = {} # needed manifests
579 mfs = {} # needed manifests
580 fnodes = {} # needed file nodes
580 fnodes = {} # needed file nodes
581 changedfiles = set()
581 changedfiles = set()
582
582
583 # Callback for the changelog, used to collect changed files and manifest
583 # Callback for the changelog, used to collect changed files and manifest
584 # nodes.
584 # nodes.
585 # Returns the linkrev node (identity in the changelog case).
585 # Returns the linkrev node (identity in the changelog case).
586 def lookupcl(x):
586 def lookupcl(x):
587 c = cl.read(x)
587 c = cl.read(x)
588 clrevorder[x] = len(clrevorder)
588 clrevorder[x] = len(clrevorder)
589 n = c[0]
589 n = c[0]
590 # record the first changeset introducing this manifest version
590 # record the first changeset introducing this manifest version
591 mfs.setdefault(n, x)
591 mfs.setdefault(n, x)
592 # Record a complete list of potentially-changed files in
592 # Record a complete list of potentially-changed files in
593 # this manifest.
593 # this manifest.
594 changedfiles.update(c[3])
594 changedfiles.update(c[3])
595 return x
595 return x
596
596
597 self._verbosenote(_('uncompressed size of bundle content:\n'))
597 self._verbosenote(_('uncompressed size of bundle content:\n'))
598 size = 0
598 size = 0
599 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
599 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
600 size += len(chunk)
600 size += len(chunk)
601 yield chunk
601 yield chunk
602 self._verbosenote(_('%8.i (changelog)\n') % size)
602 self._verbosenote(_('%8.i (changelog)\n') % size)
603
603
604 # We need to make sure that the linkrev in the changegroup refers to
604 # We need to make sure that the linkrev in the changegroup refers to
605 # the first changeset that introduced the manifest or file revision.
605 # the first changeset that introduced the manifest or file revision.
606 # The fastpath is usually safer than the slowpath, because the filelogs
606 # The fastpath is usually safer than the slowpath, because the filelogs
607 # are walked in revlog order.
607 # are walked in revlog order.
608 #
608 #
609 # When taking the slowpath with reorder=None and the manifest revlog
609 # When taking the slowpath with reorder=None and the manifest revlog
610 # uses generaldelta, the manifest may be walked in the "wrong" order.
610 # uses generaldelta, the manifest may be walked in the "wrong" order.
611 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
611 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
612 # cc0ff93d0c0c).
612 # cc0ff93d0c0c).
613 #
613 #
614 # When taking the fastpath, we are only vulnerable to reordering
614 # When taking the fastpath, we are only vulnerable to reordering
615 # of the changelog itself. The changelog never uses generaldelta, so
615 # of the changelog itself. The changelog never uses generaldelta, so
616 # it is only reordered when reorder=True. To handle this case, we
616 # it is only reordered when reorder=True. To handle this case, we
617 # simply take the slowpath, which already has the 'clrevorder' logic.
617 # simply take the slowpath, which already has the 'clrevorder' logic.
618 # This was also fixed in cc0ff93d0c0c.
618 # This was also fixed in cc0ff93d0c0c.
619 fastpathlinkrev = fastpathlinkrev and not self._reorder
619 fastpathlinkrev = fastpathlinkrev and not self._reorder
620 # Treemanifests don't work correctly with fastpathlinkrev
620 # Treemanifests don't work correctly with fastpathlinkrev
621 # either, because we don't discover which directory nodes to
621 # either, because we don't discover which directory nodes to
622 # send along with files. This could probably be fixed.
622 # send along with files. This could probably be fixed.
623 fastpathlinkrev = fastpathlinkrev and (
623 fastpathlinkrev = fastpathlinkrev and (
624 'treemanifest' not in repo.requirements)
624 'treemanifest' not in repo.requirements)
625
625
626 for chunk in self.generatemanifests(commonrevs, clrevorder,
626 for chunk in self.generatemanifests(commonrevs, clrevorder,
627 fastpathlinkrev, mfs, fnodes):
627 fastpathlinkrev, mfs, fnodes):
628 yield chunk
628 yield chunk
629 mfs.clear()
629 mfs.clear()
630 clrevs = set(cl.rev(x) for x in clnodes)
630 clrevs = set(cl.rev(x) for x in clnodes)
631
631
632 if not fastpathlinkrev:
632 if not fastpathlinkrev:
633 def linknodes(unused, fname):
633 def linknodes(unused, fname):
634 return fnodes.get(fname, {})
634 return fnodes.get(fname, {})
635 else:
635 else:
636 cln = cl.node
636 cln = cl.node
637 def linknodes(filerevlog, fname):
637 def linknodes(filerevlog, fname):
638 llr = filerevlog.linkrev
638 llr = filerevlog.linkrev
639 fln = filerevlog.node
639 fln = filerevlog.node
640 revs = ((r, llr(r)) for r in filerevlog)
640 revs = ((r, llr(r)) for r in filerevlog)
641 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
641 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
642
642
643 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
643 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
644 source):
644 source):
645 yield chunk
645 yield chunk
646
646
647 yield self.close()
647 yield self.close()
648
648
649 if clnodes:
649 if clnodes:
650 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
650 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
651
651
652 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
652 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
653 fnodes):
653 fnodes):
654 repo = self._repo
654 repo = self._repo
655 mfl = repo.manifestlog
655 mfl = repo.manifestlog
656 dirlog = mfl._revlog.dirlog
656 dirlog = mfl._revlog.dirlog
657 tmfnodes = {'': mfs}
657 tmfnodes = {'': mfs}
658
658
659 # Callback for the manifest, used to collect linkrevs for filelog
659 # Callback for the manifest, used to collect linkrevs for filelog
660 # revisions.
660 # revisions.
661 # Returns the linkrev node (collected in lookupcl).
661 # Returns the linkrev node (collected in lookupcl).
662 def makelookupmflinknode(dir):
662 def makelookupmflinknode(dir):
663 if fastpathlinkrev:
663 if fastpathlinkrev:
664 assert not dir
664 assert not dir
665 return mfs.__getitem__
665 return mfs.__getitem__
666
666
667 def lookupmflinknode(x):
667 def lookupmflinknode(x):
668 """Callback for looking up the linknode for manifests.
668 """Callback for looking up the linknode for manifests.
669
669
670 Returns the linkrev node for the specified manifest.
670 Returns the linkrev node for the specified manifest.
671
671
672 SIDE EFFECT:
672 SIDE EFFECT:
673
673
674 1) fclnodes gets populated with the list of relevant
674 1) fclnodes gets populated with the list of relevant
675 file nodes if we're not using fastpathlinkrev
675 file nodes if we're not using fastpathlinkrev
676 2) When treemanifests are in use, collects treemanifest nodes
676 2) When treemanifests are in use, collects treemanifest nodes
677 to send
677 to send
678
678
679 Note that this means manifests must be completely sent to
679 Note that this means manifests must be completely sent to
680 the client before you can trust the list of files and
680 the client before you can trust the list of files and
681 treemanifests to send.
681 treemanifests to send.
682 """
682 """
683 clnode = tmfnodes[dir][x]
683 clnode = tmfnodes[dir][x]
684 mdata = mfl.get(dir, x).readfast(shallow=True)
684 mdata = mfl.get(dir, x).readfast(shallow=True)
685 for p, n, fl in mdata.iterentries():
685 for p, n, fl in mdata.iterentries():
686 if fl == 't': # subdirectory manifest
686 if fl == 't': # subdirectory manifest
687 subdir = dir + p + '/'
687 subdir = dir + p + '/'
688 tmfclnodes = tmfnodes.setdefault(subdir, {})
688 tmfclnodes = tmfnodes.setdefault(subdir, {})
689 tmfclnode = tmfclnodes.setdefault(n, clnode)
689 tmfclnode = tmfclnodes.setdefault(n, clnode)
690 if clrevorder[clnode] < clrevorder[tmfclnode]:
690 if clrevorder[clnode] < clrevorder[tmfclnode]:
691 tmfclnodes[n] = clnode
691 tmfclnodes[n] = clnode
692 else:
692 else:
693 f = dir + p
693 f = dir + p
694 fclnodes = fnodes.setdefault(f, {})
694 fclnodes = fnodes.setdefault(f, {})
695 fclnode = fclnodes.setdefault(n, clnode)
695 fclnode = fclnodes.setdefault(n, clnode)
696 if clrevorder[clnode] < clrevorder[fclnode]:
696 if clrevorder[clnode] < clrevorder[fclnode]:
697 fclnodes[n] = clnode
697 fclnodes[n] = clnode
698 return clnode
698 return clnode
699 return lookupmflinknode
699 return lookupmflinknode
700
700
701 size = 0
701 size = 0
702 while tmfnodes:
702 while tmfnodes:
703 dir = min(tmfnodes)
703 dir = min(tmfnodes)
704 nodes = tmfnodes[dir]
704 nodes = tmfnodes[dir]
705 prunednodes = self.prune(dirlog(dir), nodes, commonrevs)
705 prunednodes = self.prune(dirlog(dir), nodes, commonrevs)
706 if not dir or prunednodes:
706 if not dir or prunednodes:
707 for x in self._packmanifests(dir, prunednodes,
707 for x in self._packmanifests(dir, prunednodes,
708 makelookupmflinknode(dir)):
708 makelookupmflinknode(dir)):
709 size += len(x)
709 size += len(x)
710 yield x
710 yield x
711 del tmfnodes[dir]
711 del tmfnodes[dir]
712 self._verbosenote(_('%8.i (manifests)\n') % size)
712 self._verbosenote(_('%8.i (manifests)\n') % size)
713 yield self._manifestsdone()
713 yield self._manifestsdone()
714
714
715 # The 'source' parameter is useful for extensions
715 # The 'source' parameter is useful for extensions
716 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
716 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
717 repo = self._repo
717 repo = self._repo
718 progress = self._progress
718 progress = self._progress
719 msgbundling = _('bundling')
719 msgbundling = _('bundling')
720
720
721 total = len(changedfiles)
721 total = len(changedfiles)
722 # for progress output
722 # for progress output
723 msgfiles = _('files')
723 msgfiles = _('files')
724 for i, fname in enumerate(sorted(changedfiles)):
724 for i, fname in enumerate(sorted(changedfiles)):
725 filerevlog = repo.file(fname)
725 filerevlog = repo.file(fname)
726 if not filerevlog:
726 if not filerevlog:
727 raise error.Abort(_("empty or missing revlog for %s") % fname)
727 raise error.Abort(_("empty or missing revlog for %s") % fname)
728
728
729 linkrevnodes = linknodes(filerevlog, fname)
729 linkrevnodes = linknodes(filerevlog, fname)
730 # Lookup for filenodes, we collected the linkrev nodes above in the
730 # Lookup for filenodes, we collected the linkrev nodes above in the
731 # fastpath case and with lookupmf in the slowpath case.
731 # fastpath case and with lookupmf in the slowpath case.
732 def lookupfilelog(x):
732 def lookupfilelog(x):
733 return linkrevnodes[x]
733 return linkrevnodes[x]
734
734
735 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs)
735 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs)
736 if filenodes:
736 if filenodes:
737 progress(msgbundling, i + 1, item=fname, unit=msgfiles,
737 progress(msgbundling, i + 1, item=fname, unit=msgfiles,
738 total=total)
738 total=total)
739 h = self.fileheader(fname)
739 h = self.fileheader(fname)
740 size = len(h)
740 size = len(h)
741 yield h
741 yield h
742 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
742 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
743 size += len(chunk)
743 size += len(chunk)
744 yield chunk
744 yield chunk
745 self._verbosenote(_('%8.i %s\n') % (size, fname))
745 self._verbosenote(_('%8.i %s\n') % (size, fname))
746 progress(msgbundling, None)
746 progress(msgbundling, None)
747
747
748 def deltaparent(self, revlog, rev, p1, p2, prev):
748 def deltaparent(self, revlog, rev, p1, p2, prev):
749 return prev
749 return prev
750
750
751 def revchunk(self, revlog, rev, prev, linknode):
751 def revchunk(self, revlog, rev, prev, linknode):
752 node = revlog.node(rev)
752 node = revlog.node(rev)
753 p1, p2 = revlog.parentrevs(rev)
753 p1, p2 = revlog.parentrevs(rev)
754 base = self.deltaparent(revlog, rev, p1, p2, prev)
754 base = self.deltaparent(revlog, rev, p1, p2, prev)
755
755
756 prefix = ''
756 prefix = ''
757 if revlog.iscensored(base) or revlog.iscensored(rev):
757 if revlog.iscensored(base) or revlog.iscensored(rev):
758 try:
758 try:
759 delta = revlog.revision(node, raw=True)
759 delta = revlog.revision(node, raw=True)
760 except error.CensoredNodeError as e:
760 except error.CensoredNodeError as e:
761 delta = e.tombstone
761 delta = e.tombstone
762 if base == nullrev:
762 if base == nullrev:
763 prefix = mdiff.trivialdiffheader(len(delta))
763 prefix = mdiff.trivialdiffheader(len(delta))
764 else:
764 else:
765 baselen = revlog.rawsize(base)
765 baselen = revlog.rawsize(base)
766 prefix = mdiff.replacediffheader(baselen, len(delta))
766 prefix = mdiff.replacediffheader(baselen, len(delta))
767 elif base == nullrev:
767 elif base == nullrev:
768 delta = revlog.revision(node, raw=True)
768 delta = revlog.revision(node, raw=True)
769 prefix = mdiff.trivialdiffheader(len(delta))
769 prefix = mdiff.trivialdiffheader(len(delta))
770 else:
770 else:
771 delta = revlog.revdiff(base, rev)
771 delta = revlog.revdiff(base, rev)
772 p1n, p2n = revlog.parents(node)
772 p1n, p2n = revlog.parents(node)
773 basenode = revlog.node(base)
773 basenode = revlog.node(base)
774 flags = revlog.flags(rev)
774 flags = revlog.flags(rev)
775 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode, flags)
775 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode, flags)
776 meta += prefix
776 meta += prefix
777 l = len(meta) + len(delta)
777 l = len(meta) + len(delta)
778 yield chunkheader(l)
778 yield chunkheader(l)
779 yield meta
779 yield meta
780 yield delta
780 yield delta
781 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
781 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
782 # do nothing with basenode, it is implicitly the previous one in HG10
782 # do nothing with basenode, it is implicitly the previous one in HG10
783 # do nothing with flags, it is implicitly 0 for cg1 and cg2
783 # do nothing with flags, it is implicitly 0 for cg1 and cg2
784 return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
784 return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
785
785
786 class cg2packer(cg1packer):
786 class cg2packer(cg1packer):
787 version = '02'
787 version = '02'
788 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
788 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
789
789
790 def __init__(self, repo, bundlecaps=None):
790 def __init__(self, repo, bundlecaps=None):
791 super(cg2packer, self).__init__(repo, bundlecaps)
791 super(cg2packer, self).__init__(repo, bundlecaps)
792 if self._reorder is None:
792 if self._reorder is None:
793 # Since generaldelta is directly supported by cg2, reordering
793 # Since generaldelta is directly supported by cg2, reordering
794 # generally doesn't help, so we disable it by default (treating
794 # generally doesn't help, so we disable it by default (treating
795 # bundle.reorder=auto just like bundle.reorder=False).
795 # bundle.reorder=auto just like bundle.reorder=False).
796 self._reorder = False
796 self._reorder = False
797
797
798 def deltaparent(self, revlog, rev, p1, p2, prev):
798 def deltaparent(self, revlog, rev, p1, p2, prev):
799 dp = revlog.deltaparent(rev)
799 dp = revlog.deltaparent(rev)
800 if dp == nullrev and revlog.storedeltachains:
800 if dp == nullrev and revlog.storedeltachains:
801 # Avoid sending full revisions when delta parent is null. Pick prev
801 # Avoid sending full revisions when delta parent is null. Pick prev
802 # in that case. It's tempting to pick p1 in this case, as p1 will
802 # in that case. It's tempting to pick p1 in this case, as p1 will
803 # be smaller in the common case. However, computing a delta against
803 # be smaller in the common case. However, computing a delta against
804 # p1 may require resolving the raw text of p1, which could be
804 # p1 may require resolving the raw text of p1, which could be
805 # expensive. The revlog caches should have prev cached, meaning
805 # expensive. The revlog caches should have prev cached, meaning
806 # less CPU for changegroup generation. There is likely room to add
806 # less CPU for changegroup generation. There is likely room to add
807 # a flag and/or config option to control this behavior.
807 # a flag and/or config option to control this behavior.
808 return prev
808 return prev
809 elif dp == nullrev:
809 elif dp == nullrev:
810 # revlog is configured to use full snapshot for a reason,
810 # revlog is configured to use full snapshot for a reason,
811 # stick to full snapshot.
811 # stick to full snapshot.
812 return nullrev
812 return nullrev
813 elif dp not in (p1, p2, prev):
813 elif dp not in (p1, p2, prev):
814 # Pick prev when we can't be sure remote has the base revision.
814 # Pick prev when we can't be sure remote has the base revision.
815 return prev
815 return prev
816 else:
816 else:
817 return dp
817 return dp
818
818
819 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
819 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
820 # Do nothing with flags, it is implicitly 0 in cg1 and cg2
820 # Do nothing with flags, it is implicitly 0 in cg1 and cg2
821 return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
821 return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
822
822
823 class cg3packer(cg2packer):
823 class cg3packer(cg2packer):
824 version = '03'
824 version = '03'
825 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
825 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
826
826
827 def _packmanifests(self, dir, mfnodes, lookuplinknode):
827 def _packmanifests(self, dir, mfnodes, lookuplinknode):
828 if dir:
828 if dir:
829 yield self.fileheader(dir)
829 yield self.fileheader(dir)
830
830
831 dirlog = self._repo.manifestlog._revlog.dirlog(dir)
831 dirlog = self._repo.manifestlog._revlog.dirlog(dir)
832 for chunk in self.group(mfnodes, dirlog, lookuplinknode,
832 for chunk in self.group(mfnodes, dirlog, lookuplinknode,
833 units=_('manifests')):
833 units=_('manifests')):
834 yield chunk
834 yield chunk
835
835
836 def _manifestsdone(self):
836 def _manifestsdone(self):
837 return self.close()
837 return self.close()
838
838
839 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
839 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
840 return struct.pack(
840 return struct.pack(
841 self.deltaheader, node, p1n, p2n, basenode, linknode, flags)
841 self.deltaheader, node, p1n, p2n, basenode, linknode, flags)
842
842
843 _packermap = {'01': (cg1packer, cg1unpacker),
843 _packermap = {'01': (cg1packer, cg1unpacker),
844 # cg2 adds support for exchanging generaldelta
844 # cg2 adds support for exchanging generaldelta
845 '02': (cg2packer, cg2unpacker),
845 '02': (cg2packer, cg2unpacker),
846 # cg3 adds support for exchanging revlog flags and treemanifests
846 # cg3 adds support for exchanging revlog flags and treemanifests
847 '03': (cg3packer, cg3unpacker),
847 '03': (cg3packer, cg3unpacker),
848 }
848 }
849
849
850 def allsupportedversions(repo):
850 def allsupportedversions(repo):
851 versions = set(_packermap.keys())
851 versions = set(_packermap.keys())
852 if not (repo.ui.configbool('experimental', 'changegroup3') or
852 if not (repo.ui.configbool('experimental', 'changegroup3') or
853 repo.ui.configbool('experimental', 'treemanifest') or
853 repo.ui.configbool('experimental', 'treemanifest') or
854 'treemanifest' in repo.requirements):
854 'treemanifest' in repo.requirements):
855 versions.discard('03')
855 versions.discard('03')
856 return versions
856 return versions
857
857
858 # Changegroup versions that can be applied to the repo
858 # Changegroup versions that can be applied to the repo
859 def supportedincomingversions(repo):
859 def supportedincomingversions(repo):
860 return allsupportedversions(repo)
860 return allsupportedversions(repo)
861
861
862 # Changegroup versions that can be created from the repo
862 # Changegroup versions that can be created from the repo
863 def supportedoutgoingversions(repo):
863 def supportedoutgoingversions(repo):
864 versions = allsupportedversions(repo)
864 versions = allsupportedversions(repo)
865 if 'treemanifest' in repo.requirements:
865 if 'treemanifest' in repo.requirements:
866 # Versions 01 and 02 support only flat manifests and it's just too
866 # Versions 01 and 02 support only flat manifests and it's just too
867 # expensive to convert between the flat manifest and tree manifest on
867 # expensive to convert between the flat manifest and tree manifest on
868 # the fly. Since tree manifests are hashed differently, all of history
868 # the fly. Since tree manifests are hashed differently, all of history
869 # would have to be converted. Instead, we simply don't even pretend to
869 # would have to be converted. Instead, we simply don't even pretend to
870 # support versions 01 and 02.
870 # support versions 01 and 02.
871 versions.discard('01')
871 versions.discard('01')
872 versions.discard('02')
872 versions.discard('02')
873 return versions
873 return versions
874
874
875 def localversion(repo):
876 # Finds the best version to use for bundles that are meant to be used
877 # locally, such as those from strip and shelve, and temporary bundles.
878 return max(supportedoutgoingversions(repo))
879
875 def safeversion(repo):
880 def safeversion(repo):
876 # Finds the smallest version that it's safe to assume clients of the repo
881 # Finds the smallest version that it's safe to assume clients of the repo
877 # will support. For example, all hg versions that support generaldelta also
882 # will support. For example, all hg versions that support generaldelta also
878 # support changegroup 02.
883 # support changegroup 02.
879 versions = supportedoutgoingversions(repo)
884 versions = supportedoutgoingversions(repo)
880 if 'generaldelta' in repo.requirements:
885 if 'generaldelta' in repo.requirements:
881 versions.discard('01')
886 versions.discard('01')
882 assert versions
887 assert versions
883 return min(versions)
888 return min(versions)
884
889
885 def getbundler(version, repo, bundlecaps=None):
890 def getbundler(version, repo, bundlecaps=None):
886 assert version in supportedoutgoingversions(repo)
891 assert version in supportedoutgoingversions(repo)
887 return _packermap[version][0](repo, bundlecaps)
892 return _packermap[version][0](repo, bundlecaps)
888
893
889 def getunbundler(version, fh, alg, extras=None):
894 def getunbundler(version, fh, alg, extras=None):
890 return _packermap[version][1](fh, alg, extras=extras)
895 return _packermap[version][1](fh, alg, extras=extras)
891
896
892 def _changegroupinfo(repo, nodes, source):
897 def _changegroupinfo(repo, nodes, source):
893 if repo.ui.verbose or source == 'bundle':
898 if repo.ui.verbose or source == 'bundle':
894 repo.ui.status(_("%d changesets found\n") % len(nodes))
899 repo.ui.status(_("%d changesets found\n") % len(nodes))
895 if repo.ui.debugflag:
900 if repo.ui.debugflag:
896 repo.ui.debug("list of changesets:\n")
901 repo.ui.debug("list of changesets:\n")
897 for node in nodes:
902 for node in nodes:
898 repo.ui.debug("%s\n" % hex(node))
903 repo.ui.debug("%s\n" % hex(node))
899
904
900 def getsubsetraw(repo, outgoing, bundler, source, fastpath=False):
905 def getsubsetraw(repo, outgoing, bundler, source, fastpath=False):
901 repo = repo.unfiltered()
906 repo = repo.unfiltered()
902 commonrevs = outgoing.common
907 commonrevs = outgoing.common
903 csets = outgoing.missing
908 csets = outgoing.missing
904 heads = outgoing.missingheads
909 heads = outgoing.missingheads
905 # We go through the fast path if we get told to, or if all (unfiltered
910 # We go through the fast path if we get told to, or if all (unfiltered
906 # heads have been requested (since we then know there all linkrevs will
911 # heads have been requested (since we then know there all linkrevs will
907 # be pulled by the client).
912 # be pulled by the client).
908 heads.sort()
913 heads.sort()
909 fastpathlinkrev = fastpath or (
914 fastpathlinkrev = fastpath or (
910 repo.filtername is None and heads == sorted(repo.heads()))
915 repo.filtername is None and heads == sorted(repo.heads()))
911
916
912 repo.hook('preoutgoing', throw=True, source=source)
917 repo.hook('preoutgoing', throw=True, source=source)
913 _changegroupinfo(repo, csets, source)
918 _changegroupinfo(repo, csets, source)
914 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
919 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
915
920
916 def getsubset(repo, outgoing, bundler, source, fastpath=False):
921 def getsubset(repo, outgoing, bundler, source, fastpath=False):
917 gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath)
922 gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath)
918 return getunbundler(bundler.version, util.chunkbuffer(gengroup), None,
923 return getunbundler(bundler.version, util.chunkbuffer(gengroup), None,
919 {'clcount': len(outgoing.missing)})
924 {'clcount': len(outgoing.missing)})
920
925
921 def changegroupsubset(repo, roots, heads, source, version='01'):
926 def changegroupsubset(repo, roots, heads, source, version='01'):
922 """Compute a changegroup consisting of all the nodes that are
927 """Compute a changegroup consisting of all the nodes that are
923 descendants of any of the roots and ancestors of any of the heads.
928 descendants of any of the roots and ancestors of any of the heads.
924 Return a chunkbuffer object whose read() method will return
929 Return a chunkbuffer object whose read() method will return
925 successive changegroup chunks.
930 successive changegroup chunks.
926
931
927 It is fairly complex as determining which filenodes and which
932 It is fairly complex as determining which filenodes and which
928 manifest nodes need to be included for the changeset to be complete
933 manifest nodes need to be included for the changeset to be complete
929 is non-trivial.
934 is non-trivial.
930
935
931 Another wrinkle is doing the reverse, figuring out which changeset in
936 Another wrinkle is doing the reverse, figuring out which changeset in
932 the changegroup a particular filenode or manifestnode belongs to.
937 the changegroup a particular filenode or manifestnode belongs to.
933 """
938 """
934 outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads)
939 outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads)
935 bundler = getbundler(version, repo)
940 bundler = getbundler(version, repo)
936 return getsubset(repo, outgoing, bundler, source)
941 return getsubset(repo, outgoing, bundler, source)
937
942
938 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
943 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
939 version='01'):
944 version='01'):
940 """Like getbundle, but taking a discovery.outgoing as an argument.
945 """Like getbundle, but taking a discovery.outgoing as an argument.
941
946
942 This is only implemented for local repos and reuses potentially
947 This is only implemented for local repos and reuses potentially
943 precomputed sets in outgoing. Returns a raw changegroup generator."""
948 precomputed sets in outgoing. Returns a raw changegroup generator."""
944 if not outgoing.missing:
949 if not outgoing.missing:
945 return None
950 return None
946 bundler = getbundler(version, repo, bundlecaps)
951 bundler = getbundler(version, repo, bundlecaps)
947 return getsubsetraw(repo, outgoing, bundler, source)
952 return getsubsetraw(repo, outgoing, bundler, source)
948
953
949 def getchangegroup(repo, source, outgoing, bundlecaps=None,
954 def getchangegroup(repo, source, outgoing, bundlecaps=None,
950 version='01'):
955 version='01'):
951 """Like getbundle, but taking a discovery.outgoing as an argument.
956 """Like getbundle, but taking a discovery.outgoing as an argument.
952
957
953 This is only implemented for local repos and reuses potentially
958 This is only implemented for local repos and reuses potentially
954 precomputed sets in outgoing."""
959 precomputed sets in outgoing."""
955 if not outgoing.missing:
960 if not outgoing.missing:
956 return None
961 return None
957 bundler = getbundler(version, repo, bundlecaps)
962 bundler = getbundler(version, repo, bundlecaps)
958 return getsubset(repo, outgoing, bundler, source)
963 return getsubset(repo, outgoing, bundler, source)
959
964
960 def getlocalchangegroup(repo, *args, **kwargs):
965 def getlocalchangegroup(repo, *args, **kwargs):
961 repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup',
966 repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup',
962 '4.3')
967 '4.3')
963 return getchangegroup(repo, *args, **kwargs)
968 return getchangegroup(repo, *args, **kwargs)
964
969
965 def changegroup(repo, basenodes, source):
970 def changegroup(repo, basenodes, source):
966 # to avoid a race we use changegroupsubset() (issue1320)
971 # to avoid a race we use changegroupsubset() (issue1320)
967 return changegroupsubset(repo, basenodes, repo.heads(), source)
972 return changegroupsubset(repo, basenodes, repo.heads(), source)
968
973
969 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
974 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
970 revisions = 0
975 revisions = 0
971 files = 0
976 files = 0
972 for chunkdata in iter(source.filelogheader, {}):
977 for chunkdata in iter(source.filelogheader, {}):
973 files += 1
978 files += 1
974 f = chunkdata["filename"]
979 f = chunkdata["filename"]
975 repo.ui.debug("adding %s revisions\n" % f)
980 repo.ui.debug("adding %s revisions\n" % f)
976 repo.ui.progress(_('files'), files, unit=_('files'),
981 repo.ui.progress(_('files'), files, unit=_('files'),
977 total=expectedfiles)
982 total=expectedfiles)
978 fl = repo.file(f)
983 fl = repo.file(f)
979 o = len(fl)
984 o = len(fl)
980 try:
985 try:
981 if not fl.addgroup(source, revmap, trp):
986 if not fl.addgroup(source, revmap, trp):
982 raise error.Abort(_("received file revlog group is empty"))
987 raise error.Abort(_("received file revlog group is empty"))
983 except error.CensoredBaseError as e:
988 except error.CensoredBaseError as e:
984 raise error.Abort(_("received delta base is censored: %s") % e)
989 raise error.Abort(_("received delta base is censored: %s") % e)
985 revisions += len(fl) - o
990 revisions += len(fl) - o
986 if f in needfiles:
991 if f in needfiles:
987 needs = needfiles[f]
992 needs = needfiles[f]
988 for new in xrange(o, len(fl)):
993 for new in xrange(o, len(fl)):
989 n = fl.node(new)
994 n = fl.node(new)
990 if n in needs:
995 if n in needs:
991 needs.remove(n)
996 needs.remove(n)
992 else:
997 else:
993 raise error.Abort(
998 raise error.Abort(
994 _("received spurious file revlog entry"))
999 _("received spurious file revlog entry"))
995 if not needs:
1000 if not needs:
996 del needfiles[f]
1001 del needfiles[f]
997 repo.ui.progress(_('files'), None)
1002 repo.ui.progress(_('files'), None)
998
1003
999 for f, needs in needfiles.iteritems():
1004 for f, needs in needfiles.iteritems():
1000 fl = repo.file(f)
1005 fl = repo.file(f)
1001 for n in needs:
1006 for n in needs:
1002 try:
1007 try:
1003 fl.rev(n)
1008 fl.rev(n)
1004 except error.LookupError:
1009 except error.LookupError:
1005 raise error.Abort(
1010 raise error.Abort(
1006 _('missing file data for %s:%s - run hg verify') %
1011 _('missing file data for %s:%s - run hg verify') %
1007 (f, hex(n)))
1012 (f, hex(n)))
1008
1013
1009 return revisions, files
1014 return revisions, files
@@ -1,433 +1,433 b''
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import short
15 from .node import short
16 from . import (
16 from . import (
17 bundle2,
17 bundle2,
18 changegroup,
18 changegroup,
19 discovery,
19 discovery,
20 error,
20 error,
21 exchange,
21 exchange,
22 obsolete,
22 obsolete,
23 obsutil,
23 obsutil,
24 util,
24 util,
25 )
25 )
26
26
27 def _bundle(repo, bases, heads, node, suffix, compress=True, obsolescence=True):
27 def _bundle(repo, bases, heads, node, suffix, compress=True, obsolescence=True):
28 """create a bundle with the specified revisions as a backup"""
28 """create a bundle with the specified revisions as a backup"""
29
29
30 backupdir = "strip-backup"
30 backupdir = "strip-backup"
31 vfs = repo.vfs
31 vfs = repo.vfs
32 if not vfs.isdir(backupdir):
32 if not vfs.isdir(backupdir):
33 vfs.mkdir(backupdir)
33 vfs.mkdir(backupdir)
34
34
35 # Include a hash of all the nodes in the filename for uniqueness
35 # Include a hash of all the nodes in the filename for uniqueness
36 allcommits = repo.set('%ln::%ln', bases, heads)
36 allcommits = repo.set('%ln::%ln', bases, heads)
37 allhashes = sorted(c.hex() for c in allcommits)
37 allhashes = sorted(c.hex() for c in allcommits)
38 totalhash = hashlib.sha1(''.join(allhashes)).hexdigest()
38 totalhash = hashlib.sha1(''.join(allhashes)).hexdigest()
39 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
39 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
40
40
41 cgversion = changegroup.safeversion(repo)
41 cgversion = changegroup.localversion(repo)
42 comp = None
42 comp = None
43 if cgversion != '01':
43 if cgversion != '01':
44 bundletype = "HG20"
44 bundletype = "HG20"
45 if compress:
45 if compress:
46 comp = 'BZ'
46 comp = 'BZ'
47 elif compress:
47 elif compress:
48 bundletype = "HG10BZ"
48 bundletype = "HG10BZ"
49 else:
49 else:
50 bundletype = "HG10UN"
50 bundletype = "HG10UN"
51
51
52 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads)
52 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads)
53 contentopts = {
53 contentopts = {
54 'cg.version': cgversion,
54 'cg.version': cgversion,
55 'obsolescence': obsolescence,
55 'obsolescence': obsolescence,
56 'phases': True,
56 'phases': True,
57 }
57 }
58 return bundle2.writenewbundle(repo.ui, repo, 'strip', name, bundletype,
58 return bundle2.writenewbundle(repo.ui, repo, 'strip', name, bundletype,
59 outgoing, contentopts, vfs, compression=comp)
59 outgoing, contentopts, vfs, compression=comp)
60
60
61 def _collectfiles(repo, striprev):
61 def _collectfiles(repo, striprev):
62 """find out the filelogs affected by the strip"""
62 """find out the filelogs affected by the strip"""
63 files = set()
63 files = set()
64
64
65 for x in xrange(striprev, len(repo)):
65 for x in xrange(striprev, len(repo)):
66 files.update(repo[x].files())
66 files.update(repo[x].files())
67
67
68 return sorted(files)
68 return sorted(files)
69
69
70 def _collectbrokencsets(repo, files, striprev):
70 def _collectbrokencsets(repo, files, striprev):
71 """return the changesets which will be broken by the truncation"""
71 """return the changesets which will be broken by the truncation"""
72 s = set()
72 s = set()
73 def collectone(revlog):
73 def collectone(revlog):
74 _, brokenset = revlog.getstrippoint(striprev)
74 _, brokenset = revlog.getstrippoint(striprev)
75 s.update([revlog.linkrev(r) for r in brokenset])
75 s.update([revlog.linkrev(r) for r in brokenset])
76
76
77 collectone(repo.manifestlog._revlog)
77 collectone(repo.manifestlog._revlog)
78 for fname in files:
78 for fname in files:
79 collectone(repo.file(fname))
79 collectone(repo.file(fname))
80
80
81 return s
81 return s
82
82
83 def strip(ui, repo, nodelist, backup=True, topic='backup'):
83 def strip(ui, repo, nodelist, backup=True, topic='backup'):
84 # This function requires the caller to lock the repo, but it operates
84 # This function requires the caller to lock the repo, but it operates
85 # within a transaction of its own, and thus requires there to be no current
85 # within a transaction of its own, and thus requires there to be no current
86 # transaction when it is called.
86 # transaction when it is called.
87 if repo.currenttransaction() is not None:
87 if repo.currenttransaction() is not None:
88 raise error.ProgrammingError('cannot strip from inside a transaction')
88 raise error.ProgrammingError('cannot strip from inside a transaction')
89
89
90 # Simple way to maintain backwards compatibility for this
90 # Simple way to maintain backwards compatibility for this
91 # argument.
91 # argument.
92 if backup in ['none', 'strip']:
92 if backup in ['none', 'strip']:
93 backup = False
93 backup = False
94
94
95 repo = repo.unfiltered()
95 repo = repo.unfiltered()
96 repo.destroying()
96 repo.destroying()
97
97
98 cl = repo.changelog
98 cl = repo.changelog
99 # TODO handle undo of merge sets
99 # TODO handle undo of merge sets
100 if isinstance(nodelist, str):
100 if isinstance(nodelist, str):
101 nodelist = [nodelist]
101 nodelist = [nodelist]
102 striplist = [cl.rev(node) for node in nodelist]
102 striplist = [cl.rev(node) for node in nodelist]
103 striprev = min(striplist)
103 striprev = min(striplist)
104
104
105 files = _collectfiles(repo, striprev)
105 files = _collectfiles(repo, striprev)
106 saverevs = _collectbrokencsets(repo, files, striprev)
106 saverevs = _collectbrokencsets(repo, files, striprev)
107
107
108 # Some revisions with rev > striprev may not be descendants of striprev.
108 # Some revisions with rev > striprev may not be descendants of striprev.
109 # We have to find these revisions and put them in a bundle, so that
109 # We have to find these revisions and put them in a bundle, so that
110 # we can restore them after the truncations.
110 # we can restore them after the truncations.
111 # To create the bundle we use repo.changegroupsubset which requires
111 # To create the bundle we use repo.changegroupsubset which requires
112 # the list of heads and bases of the set of interesting revisions.
112 # the list of heads and bases of the set of interesting revisions.
113 # (head = revision in the set that has no descendant in the set;
113 # (head = revision in the set that has no descendant in the set;
114 # base = revision in the set that has no ancestor in the set)
114 # base = revision in the set that has no ancestor in the set)
115 tostrip = set(striplist)
115 tostrip = set(striplist)
116 saveheads = set(saverevs)
116 saveheads = set(saverevs)
117 for r in cl.revs(start=striprev + 1):
117 for r in cl.revs(start=striprev + 1):
118 if any(p in tostrip for p in cl.parentrevs(r)):
118 if any(p in tostrip for p in cl.parentrevs(r)):
119 tostrip.add(r)
119 tostrip.add(r)
120
120
121 if r not in tostrip:
121 if r not in tostrip:
122 saverevs.add(r)
122 saverevs.add(r)
123 saveheads.difference_update(cl.parentrevs(r))
123 saveheads.difference_update(cl.parentrevs(r))
124 saveheads.add(r)
124 saveheads.add(r)
125 saveheads = [cl.node(r) for r in saveheads]
125 saveheads = [cl.node(r) for r in saveheads]
126
126
127 # compute base nodes
127 # compute base nodes
128 if saverevs:
128 if saverevs:
129 descendants = set(cl.descendants(saverevs))
129 descendants = set(cl.descendants(saverevs))
130 saverevs.difference_update(descendants)
130 saverevs.difference_update(descendants)
131 savebases = [cl.node(r) for r in saverevs]
131 savebases = [cl.node(r) for r in saverevs]
132 stripbases = [cl.node(r) for r in tostrip]
132 stripbases = [cl.node(r) for r in tostrip]
133
133
134 stripobsidx = obsmarkers = ()
134 stripobsidx = obsmarkers = ()
135 if repo.ui.configbool('devel', 'strip-obsmarkers'):
135 if repo.ui.configbool('devel', 'strip-obsmarkers'):
136 obsmarkers = obsutil.exclusivemarkers(repo, stripbases)
136 obsmarkers = obsutil.exclusivemarkers(repo, stripbases)
137 if obsmarkers:
137 if obsmarkers:
138 stripobsidx = [i for i, m in enumerate(repo.obsstore)
138 stripobsidx = [i for i, m in enumerate(repo.obsstore)
139 if m in obsmarkers]
139 if m in obsmarkers]
140
140
141 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
141 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
142 # is much faster
142 # is much faster
143 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
143 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
144 if newbmtarget:
144 if newbmtarget:
145 newbmtarget = repo[newbmtarget.first()].node()
145 newbmtarget = repo[newbmtarget.first()].node()
146 else:
146 else:
147 newbmtarget = '.'
147 newbmtarget = '.'
148
148
149 bm = repo._bookmarks
149 bm = repo._bookmarks
150 updatebm = []
150 updatebm = []
151 for m in bm:
151 for m in bm:
152 rev = repo[bm[m]].rev()
152 rev = repo[bm[m]].rev()
153 if rev in tostrip:
153 if rev in tostrip:
154 updatebm.append(m)
154 updatebm.append(m)
155
155
156 # create a changegroup for all the branches we need to keep
156 # create a changegroup for all the branches we need to keep
157 backupfile = None
157 backupfile = None
158 vfs = repo.vfs
158 vfs = repo.vfs
159 node = nodelist[-1]
159 node = nodelist[-1]
160 if backup:
160 if backup:
161 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
161 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
162 repo.ui.status(_("saved backup bundle to %s\n") %
162 repo.ui.status(_("saved backup bundle to %s\n") %
163 vfs.join(backupfile))
163 vfs.join(backupfile))
164 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
164 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
165 vfs.join(backupfile))
165 vfs.join(backupfile))
166 tmpbundlefile = None
166 tmpbundlefile = None
167 if saveheads:
167 if saveheads:
168 # do not compress temporary bundle if we remove it from disk later
168 # do not compress temporary bundle if we remove it from disk later
169 #
169 #
170 # We do not include obsolescence, it might re-introduce prune markers
170 # We do not include obsolescence, it might re-introduce prune markers
171 # we are trying to strip. This is harmless since the stripped markers
171 # we are trying to strip. This is harmless since the stripped markers
172 # are already backed up and we did not touched the markers for the
172 # are already backed up and we did not touched the markers for the
173 # saved changesets.
173 # saved changesets.
174 tmpbundlefile = _bundle(repo, savebases, saveheads, node, 'temp',
174 tmpbundlefile = _bundle(repo, savebases, saveheads, node, 'temp',
175 compress=False, obsolescence=False)
175 compress=False, obsolescence=False)
176
176
177 mfst = repo.manifestlog._revlog
177 mfst = repo.manifestlog._revlog
178
178
179 try:
179 try:
180 with repo.transaction("strip") as tr:
180 with repo.transaction("strip") as tr:
181 offset = len(tr.entries)
181 offset = len(tr.entries)
182
182
183 tr.startgroup()
183 tr.startgroup()
184 cl.strip(striprev, tr)
184 cl.strip(striprev, tr)
185 mfst.strip(striprev, tr)
185 mfst.strip(striprev, tr)
186 striptrees(repo, tr, striprev, files)
186 striptrees(repo, tr, striprev, files)
187
187
188 for fn in files:
188 for fn in files:
189 repo.file(fn).strip(striprev, tr)
189 repo.file(fn).strip(striprev, tr)
190 tr.endgroup()
190 tr.endgroup()
191
191
192 for i in xrange(offset, len(tr.entries)):
192 for i in xrange(offset, len(tr.entries)):
193 file, troffset, ignore = tr.entries[i]
193 file, troffset, ignore = tr.entries[i]
194 with repo.svfs(file, 'a', checkambig=True) as fp:
194 with repo.svfs(file, 'a', checkambig=True) as fp:
195 fp.truncate(troffset)
195 fp.truncate(troffset)
196 if troffset == 0:
196 if troffset == 0:
197 repo.store.markremoved(file)
197 repo.store.markremoved(file)
198
198
199 deleteobsmarkers(repo.obsstore, stripobsidx)
199 deleteobsmarkers(repo.obsstore, stripobsidx)
200 del repo.obsstore
200 del repo.obsstore
201
201
202 repo._phasecache.filterunknown(repo)
202 repo._phasecache.filterunknown(repo)
203 if tmpbundlefile:
203 if tmpbundlefile:
204 ui.note(_("adding branch\n"))
204 ui.note(_("adding branch\n"))
205 f = vfs.open(tmpbundlefile, "rb")
205 f = vfs.open(tmpbundlefile, "rb")
206 gen = exchange.readbundle(ui, f, tmpbundlefile, vfs)
206 gen = exchange.readbundle(ui, f, tmpbundlefile, vfs)
207 if not repo.ui.verbose:
207 if not repo.ui.verbose:
208 # silence internal shuffling chatter
208 # silence internal shuffling chatter
209 repo.ui.pushbuffer()
209 repo.ui.pushbuffer()
210 tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile)
210 tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile)
211 txnname = 'strip'
211 txnname = 'strip'
212 if not isinstance(gen, bundle2.unbundle20):
212 if not isinstance(gen, bundle2.unbundle20):
213 txnname = "strip\n%s" % util.hidepassword(tmpbundleurl)
213 txnname = "strip\n%s" % util.hidepassword(tmpbundleurl)
214 with repo.transaction(txnname) as tr:
214 with repo.transaction(txnname) as tr:
215 bundle2.applybundle(repo, gen, tr, source='strip',
215 bundle2.applybundle(repo, gen, tr, source='strip',
216 url=tmpbundleurl)
216 url=tmpbundleurl)
217 if not repo.ui.verbose:
217 if not repo.ui.verbose:
218 repo.ui.popbuffer()
218 repo.ui.popbuffer()
219 f.close()
219 f.close()
220 repo._phasecache.invalidate()
220 repo._phasecache.invalidate()
221
221
222
222
223 with repo.transaction('repair') as tr:
223 with repo.transaction('repair') as tr:
224 bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm]
224 bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm]
225 bm.applychanges(repo, tr, bmchanges)
225 bm.applychanges(repo, tr, bmchanges)
226
226
227 # remove undo files
227 # remove undo files
228 for undovfs, undofile in repo.undofiles():
228 for undovfs, undofile in repo.undofiles():
229 try:
229 try:
230 undovfs.unlink(undofile)
230 undovfs.unlink(undofile)
231 except OSError as e:
231 except OSError as e:
232 if e.errno != errno.ENOENT:
232 if e.errno != errno.ENOENT:
233 ui.warn(_('error removing %s: %s\n') %
233 ui.warn(_('error removing %s: %s\n') %
234 (undovfs.join(undofile), str(e)))
234 (undovfs.join(undofile), str(e)))
235
235
236 except: # re-raises
236 except: # re-raises
237 if backupfile:
237 if backupfile:
238 ui.warn(_("strip failed, backup bundle stored in '%s'\n")
238 ui.warn(_("strip failed, backup bundle stored in '%s'\n")
239 % vfs.join(backupfile))
239 % vfs.join(backupfile))
240 if tmpbundlefile:
240 if tmpbundlefile:
241 ui.warn(_("strip failed, unrecovered changes stored in '%s'\n")
241 ui.warn(_("strip failed, unrecovered changes stored in '%s'\n")
242 % vfs.join(tmpbundlefile))
242 % vfs.join(tmpbundlefile))
243 ui.warn(_("(fix the problem, then recover the changesets with "
243 ui.warn(_("(fix the problem, then recover the changesets with "
244 "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile))
244 "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile))
245 raise
245 raise
246 else:
246 else:
247 if tmpbundlefile:
247 if tmpbundlefile:
248 # Remove temporary bundle only if there were no exceptions
248 # Remove temporary bundle only if there were no exceptions
249 vfs.unlink(tmpbundlefile)
249 vfs.unlink(tmpbundlefile)
250
250
251 repo.destroyed()
251 repo.destroyed()
252 # return the backup file path (or None if 'backup' was False) so
252 # return the backup file path (or None if 'backup' was False) so
253 # extensions can use it
253 # extensions can use it
254 return backupfile
254 return backupfile
255
255
256 def safestriproots(ui, repo, nodes):
256 def safestriproots(ui, repo, nodes):
257 """return list of roots of nodes where descendants are covered by nodes"""
257 """return list of roots of nodes where descendants are covered by nodes"""
258 torev = repo.unfiltered().changelog.rev
258 torev = repo.unfiltered().changelog.rev
259 revs = set(torev(n) for n in nodes)
259 revs = set(torev(n) for n in nodes)
260 # tostrip = wanted - unsafe = wanted - ancestors(orphaned)
260 # tostrip = wanted - unsafe = wanted - ancestors(orphaned)
261 # orphaned = affected - wanted
261 # orphaned = affected - wanted
262 # affected = descendants(roots(wanted))
262 # affected = descendants(roots(wanted))
263 # wanted = revs
263 # wanted = revs
264 tostrip = set(repo.revs('%ld-(::((roots(%ld)::)-%ld))', revs, revs, revs))
264 tostrip = set(repo.revs('%ld-(::((roots(%ld)::)-%ld))', revs, revs, revs))
265 notstrip = revs - tostrip
265 notstrip = revs - tostrip
266 if notstrip:
266 if notstrip:
267 nodestr = ', '.join(sorted(short(repo[n].node()) for n in notstrip))
267 nodestr = ', '.join(sorted(short(repo[n].node()) for n in notstrip))
268 ui.warn(_('warning: orphaned descendants detected, '
268 ui.warn(_('warning: orphaned descendants detected, '
269 'not stripping %s\n') % nodestr)
269 'not stripping %s\n') % nodestr)
270 return [c.node() for c in repo.set('roots(%ld)', tostrip)]
270 return [c.node() for c in repo.set('roots(%ld)', tostrip)]
271
271
272 class stripcallback(object):
272 class stripcallback(object):
273 """used as a transaction postclose callback"""
273 """used as a transaction postclose callback"""
274
274
275 def __init__(self, ui, repo, backup, topic):
275 def __init__(self, ui, repo, backup, topic):
276 self.ui = ui
276 self.ui = ui
277 self.repo = repo
277 self.repo = repo
278 self.backup = backup
278 self.backup = backup
279 self.topic = topic or 'backup'
279 self.topic = topic or 'backup'
280 self.nodelist = []
280 self.nodelist = []
281
281
282 def addnodes(self, nodes):
282 def addnodes(self, nodes):
283 self.nodelist.extend(nodes)
283 self.nodelist.extend(nodes)
284
284
285 def __call__(self, tr):
285 def __call__(self, tr):
286 roots = safestriproots(self.ui, self.repo, self.nodelist)
286 roots = safestriproots(self.ui, self.repo, self.nodelist)
287 if roots:
287 if roots:
288 strip(self.ui, self.repo, roots, self.backup, self.topic)
288 strip(self.ui, self.repo, roots, self.backup, self.topic)
289
289
290 def delayedstrip(ui, repo, nodelist, topic=None):
290 def delayedstrip(ui, repo, nodelist, topic=None):
291 """like strip, but works inside transaction and won't strip irreverent revs
291 """like strip, but works inside transaction and won't strip irreverent revs
292
292
293 nodelist must explicitly contain all descendants. Otherwise a warning will
293 nodelist must explicitly contain all descendants. Otherwise a warning will
294 be printed that some nodes are not stripped.
294 be printed that some nodes are not stripped.
295
295
296 Always do a backup. The last non-None "topic" will be used as the backup
296 Always do a backup. The last non-None "topic" will be used as the backup
297 topic name. The default backup topic name is "backup".
297 topic name. The default backup topic name is "backup".
298 """
298 """
299 tr = repo.currenttransaction()
299 tr = repo.currenttransaction()
300 if not tr:
300 if not tr:
301 nodes = safestriproots(ui, repo, nodelist)
301 nodes = safestriproots(ui, repo, nodelist)
302 return strip(ui, repo, nodes, True, topic)
302 return strip(ui, repo, nodes, True, topic)
303 # transaction postclose callbacks are called in alphabet order.
303 # transaction postclose callbacks are called in alphabet order.
304 # use '\xff' as prefix so we are likely to be called last.
304 # use '\xff' as prefix so we are likely to be called last.
305 callback = tr.getpostclose('\xffstrip')
305 callback = tr.getpostclose('\xffstrip')
306 if callback is None:
306 if callback is None:
307 callback = stripcallback(ui, repo, True, topic)
307 callback = stripcallback(ui, repo, True, topic)
308 tr.addpostclose('\xffstrip', callback)
308 tr.addpostclose('\xffstrip', callback)
309 if topic:
309 if topic:
310 callback.topic = topic
310 callback.topic = topic
311 callback.addnodes(nodelist)
311 callback.addnodes(nodelist)
312
312
313 def striptrees(repo, tr, striprev, files):
313 def striptrees(repo, tr, striprev, files):
314 if 'treemanifest' in repo.requirements: # safe but unnecessary
314 if 'treemanifest' in repo.requirements: # safe but unnecessary
315 # otherwise
315 # otherwise
316 for unencoded, encoded, size in repo.store.datafiles():
316 for unencoded, encoded, size in repo.store.datafiles():
317 if (unencoded.startswith('meta/') and
317 if (unencoded.startswith('meta/') and
318 unencoded.endswith('00manifest.i')):
318 unencoded.endswith('00manifest.i')):
319 dir = unencoded[5:-12]
319 dir = unencoded[5:-12]
320 repo.manifestlog._revlog.dirlog(dir).strip(striprev, tr)
320 repo.manifestlog._revlog.dirlog(dir).strip(striprev, tr)
321
321
322 def rebuildfncache(ui, repo):
322 def rebuildfncache(ui, repo):
323 """Rebuilds the fncache file from repo history.
323 """Rebuilds the fncache file from repo history.
324
324
325 Missing entries will be added. Extra entries will be removed.
325 Missing entries will be added. Extra entries will be removed.
326 """
326 """
327 repo = repo.unfiltered()
327 repo = repo.unfiltered()
328
328
329 if 'fncache' not in repo.requirements:
329 if 'fncache' not in repo.requirements:
330 ui.warn(_('(not rebuilding fncache because repository does not '
330 ui.warn(_('(not rebuilding fncache because repository does not '
331 'support fncache)\n'))
331 'support fncache)\n'))
332 return
332 return
333
333
334 with repo.lock():
334 with repo.lock():
335 fnc = repo.store.fncache
335 fnc = repo.store.fncache
336 # Trigger load of fncache.
336 # Trigger load of fncache.
337 if 'irrelevant' in fnc:
337 if 'irrelevant' in fnc:
338 pass
338 pass
339
339
340 oldentries = set(fnc.entries)
340 oldentries = set(fnc.entries)
341 newentries = set()
341 newentries = set()
342 seenfiles = set()
342 seenfiles = set()
343
343
344 repolen = len(repo)
344 repolen = len(repo)
345 for rev in repo:
345 for rev in repo:
346 ui.progress(_('rebuilding'), rev, total=repolen,
346 ui.progress(_('rebuilding'), rev, total=repolen,
347 unit=_('changesets'))
347 unit=_('changesets'))
348
348
349 ctx = repo[rev]
349 ctx = repo[rev]
350 for f in ctx.files():
350 for f in ctx.files():
351 # This is to minimize I/O.
351 # This is to minimize I/O.
352 if f in seenfiles:
352 if f in seenfiles:
353 continue
353 continue
354 seenfiles.add(f)
354 seenfiles.add(f)
355
355
356 i = 'data/%s.i' % f
356 i = 'data/%s.i' % f
357 d = 'data/%s.d' % f
357 d = 'data/%s.d' % f
358
358
359 if repo.store._exists(i):
359 if repo.store._exists(i):
360 newentries.add(i)
360 newentries.add(i)
361 if repo.store._exists(d):
361 if repo.store._exists(d):
362 newentries.add(d)
362 newentries.add(d)
363
363
364 ui.progress(_('rebuilding'), None)
364 ui.progress(_('rebuilding'), None)
365
365
366 if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise
366 if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise
367 for dir in util.dirs(seenfiles):
367 for dir in util.dirs(seenfiles):
368 i = 'meta/%s/00manifest.i' % dir
368 i = 'meta/%s/00manifest.i' % dir
369 d = 'meta/%s/00manifest.d' % dir
369 d = 'meta/%s/00manifest.d' % dir
370
370
371 if repo.store._exists(i):
371 if repo.store._exists(i):
372 newentries.add(i)
372 newentries.add(i)
373 if repo.store._exists(d):
373 if repo.store._exists(d):
374 newentries.add(d)
374 newentries.add(d)
375
375
376 addcount = len(newentries - oldentries)
376 addcount = len(newentries - oldentries)
377 removecount = len(oldentries - newentries)
377 removecount = len(oldentries - newentries)
378 for p in sorted(oldentries - newentries):
378 for p in sorted(oldentries - newentries):
379 ui.write(_('removing %s\n') % p)
379 ui.write(_('removing %s\n') % p)
380 for p in sorted(newentries - oldentries):
380 for p in sorted(newentries - oldentries):
381 ui.write(_('adding %s\n') % p)
381 ui.write(_('adding %s\n') % p)
382
382
383 if addcount or removecount:
383 if addcount or removecount:
384 ui.write(_('%d items added, %d removed from fncache\n') %
384 ui.write(_('%d items added, %d removed from fncache\n') %
385 (addcount, removecount))
385 (addcount, removecount))
386 fnc.entries = newentries
386 fnc.entries = newentries
387 fnc._dirty = True
387 fnc._dirty = True
388
388
389 with repo.transaction('fncache') as tr:
389 with repo.transaction('fncache') as tr:
390 fnc.write(tr)
390 fnc.write(tr)
391 else:
391 else:
392 ui.write(_('fncache already up to date\n'))
392 ui.write(_('fncache already up to date\n'))
393
393
394 def stripbmrevset(repo, mark):
394 def stripbmrevset(repo, mark):
395 """
395 """
396 The revset to strip when strip is called with -B mark
396 The revset to strip when strip is called with -B mark
397
397
398 Needs to live here so extensions can use it and wrap it even when strip is
398 Needs to live here so extensions can use it and wrap it even when strip is
399 not enabled or not present on a box.
399 not enabled or not present on a box.
400 """
400 """
401 return repo.revs("ancestors(bookmark(%s)) - "
401 return repo.revs("ancestors(bookmark(%s)) - "
402 "ancestors(head() and not bookmark(%s)) - "
402 "ancestors(head() and not bookmark(%s)) - "
403 "ancestors(bookmark() and not bookmark(%s))",
403 "ancestors(bookmark() and not bookmark(%s))",
404 mark, mark, mark)
404 mark, mark, mark)
405
405
406 def deleteobsmarkers(obsstore, indices):
406 def deleteobsmarkers(obsstore, indices):
407 """Delete some obsmarkers from obsstore and return how many were deleted
407 """Delete some obsmarkers from obsstore and return how many were deleted
408
408
409 'indices' is a list of ints which are the indices
409 'indices' is a list of ints which are the indices
410 of the markers to be deleted.
410 of the markers to be deleted.
411
411
412 Every invocation of this function completely rewrites the obsstore file,
412 Every invocation of this function completely rewrites the obsstore file,
413 skipping the markers we want to be removed. The new temporary file is
413 skipping the markers we want to be removed. The new temporary file is
414 created, remaining markers are written there and on .close() this file
414 created, remaining markers are written there and on .close() this file
415 gets atomically renamed to obsstore, thus guaranteeing consistency."""
415 gets atomically renamed to obsstore, thus guaranteeing consistency."""
416 if not indices:
416 if not indices:
417 # we don't want to rewrite the obsstore with the same content
417 # we don't want to rewrite the obsstore with the same content
418 return
418 return
419
419
420 left = []
420 left = []
421 current = obsstore._all
421 current = obsstore._all
422 n = 0
422 n = 0
423 for i, m in enumerate(current):
423 for i, m in enumerate(current):
424 if i in indices:
424 if i in indices:
425 n += 1
425 n += 1
426 continue
426 continue
427 left.append(m)
427 left.append(m)
428
428
429 newobsstorefile = obsstore.svfs('obsstore', 'w', atomictemp=True)
429 newobsstorefile = obsstore.svfs('obsstore', 'w', atomictemp=True)
430 for bytes in obsolete.encodemarkers(left, True, obsstore._version):
430 for bytes in obsolete.encodemarkers(left, True, obsstore._version):
431 newobsstorefile.write(bytes)
431 newobsstorefile.write(bytes)
432 newobsstorefile.close()
432 newobsstorefile.close()
433 return n
433 return n
@@ -1,983 +1,982 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > rebase=
3 > rebase=
4 > drawdag=$TESTDIR/drawdag.py
4 > drawdag=$TESTDIR/drawdag.py
5 >
5 >
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 >
8 >
9 > [alias]
9 > [alias]
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
11 > EOF
11 > EOF
12
12
13
13
14 $ hg init a
14 $ hg init a
15 $ cd a
15 $ cd a
16 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
17 adding changesets
17 adding changesets
18 adding manifests
18 adding manifests
19 adding file changes
19 adding file changes
20 added 8 changesets with 7 changes to 7 files (+2 heads)
20 added 8 changesets with 7 changes to 7 files (+2 heads)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
22 $ hg up tip
22 $ hg up tip
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 $ cd ..
24 $ cd ..
25
25
26
26
27 Rebasing
27 Rebasing
28 D onto H - simple rebase:
28 D onto H - simple rebase:
29 (this also tests that editor is invoked if '--edit' is specified, and that we
29 (this also tests that editor is invoked if '--edit' is specified, and that we
30 can abort or warn for colliding untracked files)
30 can abort or warn for colliding untracked files)
31
31
32 $ hg clone -q -u . a a1
32 $ hg clone -q -u . a a1
33 $ cd a1
33 $ cd a1
34
34
35 $ hg tglog
35 $ hg tglog
36 @ 7: 'H'
36 @ 7: 'H'
37 |
37 |
38 | o 6: 'G'
38 | o 6: 'G'
39 |/|
39 |/|
40 o | 5: 'F'
40 o | 5: 'F'
41 | |
41 | |
42 | o 4: 'E'
42 | o 4: 'E'
43 |/
43 |/
44 | o 3: 'D'
44 | o 3: 'D'
45 | |
45 | |
46 | o 2: 'C'
46 | o 2: 'C'
47 | |
47 | |
48 | o 1: 'B'
48 | o 1: 'B'
49 |/
49 |/
50 o 0: 'A'
50 o 0: 'A'
51
51
52
52
53 $ hg status --rev "3^1" --rev 3
53 $ hg status --rev "3^1" --rev 3
54 A D
54 A D
55 $ echo collide > D
55 $ echo collide > D
56 $ HGEDITOR=cat hg rebase -s 3 -d 7 --edit --config merge.checkunknown=warn
56 $ HGEDITOR=cat hg rebase -s 3 -d 7 --edit --config merge.checkunknown=warn
57 rebasing 3:32af7686d403 "D"
57 rebasing 3:32af7686d403 "D"
58 D: replacing untracked file
58 D: replacing untracked file
59 D
59 D
60
60
61
61
62 HG: Enter commit message. Lines beginning with 'HG:' are removed.
62 HG: Enter commit message. Lines beginning with 'HG:' are removed.
63 HG: Leave message empty to abort commit.
63 HG: Leave message empty to abort commit.
64 HG: --
64 HG: --
65 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
65 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
66 HG: branch 'default'
66 HG: branch 'default'
67 HG: added D
67 HG: added D
68 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob)
68 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob)
69 $ cat D.orig
69 $ cat D.orig
70 collide
70 collide
71 $ rm D.orig
71 $ rm D.orig
72
72
73 $ hg tglog
73 $ hg tglog
74 o 7: 'D'
74 o 7: 'D'
75 |
75 |
76 @ 6: 'H'
76 @ 6: 'H'
77 |
77 |
78 | o 5: 'G'
78 | o 5: 'G'
79 |/|
79 |/|
80 o | 4: 'F'
80 o | 4: 'F'
81 | |
81 | |
82 | o 3: 'E'
82 | o 3: 'E'
83 |/
83 |/
84 | o 2: 'C'
84 | o 2: 'C'
85 | |
85 | |
86 | o 1: 'B'
86 | o 1: 'B'
87 |/
87 |/
88 o 0: 'A'
88 o 0: 'A'
89
89
90 $ cd ..
90 $ cd ..
91
91
92
92
93 D onto F - intermediate point:
93 D onto F - intermediate point:
94 (this also tests that editor is not invoked if '--edit' is not specified, and
94 (this also tests that editor is not invoked if '--edit' is not specified, and
95 that we can ignore for colliding untracked files)
95 that we can ignore for colliding untracked files)
96
96
97 $ hg clone -q -u . a a2
97 $ hg clone -q -u . a a2
98 $ cd a2
98 $ cd a2
99 $ echo collide > D
99 $ echo collide > D
100
100
101 $ HGEDITOR=cat hg rebase -s 3 -d 5 --config merge.checkunknown=ignore
101 $ HGEDITOR=cat hg rebase -s 3 -d 5 --config merge.checkunknown=ignore
102 rebasing 3:32af7686d403 "D"
102 rebasing 3:32af7686d403 "D"
103 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob)
103 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob)
104 $ cat D.orig
104 $ cat D.orig
105 collide
105 collide
106 $ rm D.orig
106 $ rm D.orig
107
107
108 $ hg tglog
108 $ hg tglog
109 o 7: 'D'
109 o 7: 'D'
110 |
110 |
111 | @ 6: 'H'
111 | @ 6: 'H'
112 |/
112 |/
113 | o 5: 'G'
113 | o 5: 'G'
114 |/|
114 |/|
115 o | 4: 'F'
115 o | 4: 'F'
116 | |
116 | |
117 | o 3: 'E'
117 | o 3: 'E'
118 |/
118 |/
119 | o 2: 'C'
119 | o 2: 'C'
120 | |
120 | |
121 | o 1: 'B'
121 | o 1: 'B'
122 |/
122 |/
123 o 0: 'A'
123 o 0: 'A'
124
124
125 $ cd ..
125 $ cd ..
126
126
127
127
128 E onto H - skip of G:
128 E onto H - skip of G:
129 (this also tests that we can overwrite untracked files and don't create backups
129 (this also tests that we can overwrite untracked files and don't create backups
130 if they have the same contents)
130 if they have the same contents)
131
131
132 $ hg clone -q -u . a a3
132 $ hg clone -q -u . a a3
133 $ cd a3
133 $ cd a3
134 $ hg cat -r 4 E | tee E
134 $ hg cat -r 4 E | tee E
135 E
135 E
136
136
137 $ hg rebase -s 4 -d 7
137 $ hg rebase -s 4 -d 7
138 rebasing 4:9520eea781bc "E"
138 rebasing 4:9520eea781bc "E"
139 rebasing 6:eea13746799a "G"
139 rebasing 6:eea13746799a "G"
140 note: rebase of 6:eea13746799a created no changes to commit
140 note: rebase of 6:eea13746799a created no changes to commit
141 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/9520eea781bc-fcd8edd4-rebase.hg (glob)
141 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/9520eea781bc-fcd8edd4-rebase.hg (glob)
142 $ f E.orig
142 $ f E.orig
143 E.orig: file not found
143 E.orig: file not found
144
144
145 $ hg tglog
145 $ hg tglog
146 o 6: 'E'
146 o 6: 'E'
147 |
147 |
148 @ 5: 'H'
148 @ 5: 'H'
149 |
149 |
150 o 4: 'F'
150 o 4: 'F'
151 |
151 |
152 | o 3: 'D'
152 | o 3: 'D'
153 | |
153 | |
154 | o 2: 'C'
154 | o 2: 'C'
155 | |
155 | |
156 | o 1: 'B'
156 | o 1: 'B'
157 |/
157 |/
158 o 0: 'A'
158 o 0: 'A'
159
159
160 $ cd ..
160 $ cd ..
161
161
162
162
163 F onto E - rebase of a branching point (skip G):
163 F onto E - rebase of a branching point (skip G):
164
164
165 $ hg clone -q -u . a a4
165 $ hg clone -q -u . a a4
166 $ cd a4
166 $ cd a4
167
167
168 $ hg rebase -s 5 -d 4
168 $ hg rebase -s 5 -d 4
169 rebasing 5:24b6387c8c8c "F"
169 rebasing 5:24b6387c8c8c "F"
170 rebasing 6:eea13746799a "G"
170 rebasing 6:eea13746799a "G"
171 note: rebase of 6:eea13746799a created no changes to commit
171 note: rebase of 6:eea13746799a created no changes to commit
172 rebasing 7:02de42196ebe "H" (tip)
172 rebasing 7:02de42196ebe "H" (tip)
173 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob)
173 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob)
174
174
175 $ hg tglog
175 $ hg tglog
176 @ 6: 'H'
176 @ 6: 'H'
177 |
177 |
178 o 5: 'F'
178 o 5: 'F'
179 |
179 |
180 o 4: 'E'
180 o 4: 'E'
181 |
181 |
182 | o 3: 'D'
182 | o 3: 'D'
183 | |
183 | |
184 | o 2: 'C'
184 | o 2: 'C'
185 | |
185 | |
186 | o 1: 'B'
186 | o 1: 'B'
187 |/
187 |/
188 o 0: 'A'
188 o 0: 'A'
189
189
190 $ cd ..
190 $ cd ..
191
191
192
192
193 G onto H - merged revision having a parent in ancestors of target:
193 G onto H - merged revision having a parent in ancestors of target:
194
194
195 $ hg clone -q -u . a a5
195 $ hg clone -q -u . a a5
196 $ cd a5
196 $ cd a5
197
197
198 $ hg rebase -s 6 -d 7
198 $ hg rebase -s 6 -d 7
199 rebasing 6:eea13746799a "G"
199 rebasing 6:eea13746799a "G"
200 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/eea13746799a-883828ed-rebase.hg (glob)
200 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/eea13746799a-883828ed-rebase.hg (glob)
201
201
202 $ hg tglog
202 $ hg tglog
203 o 7: 'G'
203 o 7: 'G'
204 |\
204 |\
205 | @ 6: 'H'
205 | @ 6: 'H'
206 | |
206 | |
207 | o 5: 'F'
207 | o 5: 'F'
208 | |
208 | |
209 o | 4: 'E'
209 o | 4: 'E'
210 |/
210 |/
211 | o 3: 'D'
211 | o 3: 'D'
212 | |
212 | |
213 | o 2: 'C'
213 | o 2: 'C'
214 | |
214 | |
215 | o 1: 'B'
215 | o 1: 'B'
216 |/
216 |/
217 o 0: 'A'
217 o 0: 'A'
218
218
219 $ cd ..
219 $ cd ..
220
220
221
221
222 F onto B - G maintains E as parent:
222 F onto B - G maintains E as parent:
223
223
224 $ hg clone -q -u . a a6
224 $ hg clone -q -u . a a6
225 $ cd a6
225 $ cd a6
226
226
227 $ hg rebase -s 5 -d 1
227 $ hg rebase -s 5 -d 1
228 rebasing 5:24b6387c8c8c "F"
228 rebasing 5:24b6387c8c8c "F"
229 rebasing 6:eea13746799a "G"
229 rebasing 6:eea13746799a "G"
230 rebasing 7:02de42196ebe "H" (tip)
230 rebasing 7:02de42196ebe "H" (tip)
231 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob)
231 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob)
232
232
233 $ hg tglog
233 $ hg tglog
234 @ 7: 'H'
234 @ 7: 'H'
235 |
235 |
236 | o 6: 'G'
236 | o 6: 'G'
237 |/|
237 |/|
238 o | 5: 'F'
238 o | 5: 'F'
239 | |
239 | |
240 | o 4: 'E'
240 | o 4: 'E'
241 | |
241 | |
242 | | o 3: 'D'
242 | | o 3: 'D'
243 | | |
243 | | |
244 +---o 2: 'C'
244 +---o 2: 'C'
245 | |
245 | |
246 o | 1: 'B'
246 o | 1: 'B'
247 |/
247 |/
248 o 0: 'A'
248 o 0: 'A'
249
249
250 $ cd ..
250 $ cd ..
251
251
252
252
253 These will fail (using --source):
253 These will fail (using --source):
254
254
255 G onto F - rebase onto an ancestor:
255 G onto F - rebase onto an ancestor:
256
256
257 $ hg clone -q -u . a a7
257 $ hg clone -q -u . a a7
258 $ cd a7
258 $ cd a7
259
259
260 $ hg rebase -s 6 -d 5
260 $ hg rebase -s 6 -d 5
261 nothing to rebase
261 nothing to rebase
262 [1]
262 [1]
263
263
264 F onto G - rebase onto a descendant:
264 F onto G - rebase onto a descendant:
265
265
266 $ hg rebase -s 5 -d 6
266 $ hg rebase -s 5 -d 6
267 abort: source is ancestor of destination
267 abort: source is ancestor of destination
268 [255]
268 [255]
269
269
270 G onto B - merge revision with both parents not in ancestors of target:
270 G onto B - merge revision with both parents not in ancestors of target:
271
271
272 $ hg rebase -s 6 -d 1
272 $ hg rebase -s 6 -d 1
273 rebasing 6:eea13746799a "G"
273 rebasing 6:eea13746799a "G"
274 abort: cannot use revision 6 as base, result would have 3 parents
274 abort: cannot use revision 6 as base, result would have 3 parents
275 [255]
275 [255]
276 $ hg rebase --abort
276 $ hg rebase --abort
277 rebase aborted
277 rebase aborted
278
278
279 These will abort gracefully (using --base):
279 These will abort gracefully (using --base):
280
280
281 G onto G - rebase onto same changeset:
281 G onto G - rebase onto same changeset:
282
282
283 $ hg rebase -b 6 -d 6
283 $ hg rebase -b 6 -d 6
284 nothing to rebase - eea13746799a is both "base" and destination
284 nothing to rebase - eea13746799a is both "base" and destination
285 [1]
285 [1]
286
286
287 G onto F - rebase onto an ancestor:
287 G onto F - rebase onto an ancestor:
288
288
289 $ hg rebase -b 6 -d 5
289 $ hg rebase -b 6 -d 5
290 nothing to rebase
290 nothing to rebase
291 [1]
291 [1]
292
292
293 F onto G - rebase onto a descendant:
293 F onto G - rebase onto a descendant:
294
294
295 $ hg rebase -b 5 -d 6
295 $ hg rebase -b 5 -d 6
296 nothing to rebase - "base" 24b6387c8c8c is already an ancestor of destination eea13746799a
296 nothing to rebase - "base" 24b6387c8c8c is already an ancestor of destination eea13746799a
297 [1]
297 [1]
298
298
299 C onto A - rebase onto an ancestor:
299 C onto A - rebase onto an ancestor:
300
300
301 $ hg rebase -d 0 -s 2
301 $ hg rebase -d 0 -s 2
302 rebasing 2:5fddd98957c8 "C"
302 rebasing 2:5fddd98957c8 "C"
303 rebasing 3:32af7686d403 "D"
303 rebasing 3:32af7686d403 "D"
304 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg (glob)
304 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg (glob)
305 $ hg tglog
305 $ hg tglog
306 o 7: 'D'
306 o 7: 'D'
307 |
307 |
308 o 6: 'C'
308 o 6: 'C'
309 |
309 |
310 | @ 5: 'H'
310 | @ 5: 'H'
311 | |
311 | |
312 | | o 4: 'G'
312 | | o 4: 'G'
313 | |/|
313 | |/|
314 | o | 3: 'F'
314 | o | 3: 'F'
315 |/ /
315 |/ /
316 | o 2: 'E'
316 | o 2: 'E'
317 |/
317 |/
318 | o 1: 'B'
318 | o 1: 'B'
319 |/
319 |/
320 o 0: 'A'
320 o 0: 'A'
321
321
322
322
323 Check rebasing public changeset
323 Check rebasing public changeset
324
324
325 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
325 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
326 $ hg rebase -d 0 -b 6
326 $ hg rebase -d 0 -b 6
327 nothing to rebase
327 nothing to rebase
328 [1]
328 [1]
329 $ hg rebase -d 5 -b 6
329 $ hg rebase -d 5 -b 6
330 abort: can't rebase public changeset e1c4361dd923
330 abort: can't rebase public changeset e1c4361dd923
331 (see 'hg help phases' for details)
331 (see 'hg help phases' for details)
332 [255]
332 [255]
333 $ hg rebase -d 5 -r '1 + (6::)'
333 $ hg rebase -d 5 -r '1 + (6::)'
334 abort: can't rebase public changeset e1c4361dd923
334 abort: can't rebase public changeset e1c4361dd923
335 (see 'hg help phases' for details)
335 (see 'hg help phases' for details)
336 [255]
336 [255]
337
337
338 $ hg rebase -d 5 -b 6 --keep
338 $ hg rebase -d 5 -b 6 --keep
339 rebasing 6:e1c4361dd923 "C"
339 rebasing 6:e1c4361dd923 "C"
340 rebasing 7:c9659aac0000 "D" (tip)
340 rebasing 7:c9659aac0000 "D" (tip)
341
341
342 Check rebasing mutable changeset
342 Check rebasing mutable changeset
343 Source phase greater or equal to destination phase: new changeset get the phase of source:
343 Source phase greater or equal to destination phase: new changeset get the phase of source:
344 $ hg id -n
344 $ hg id -n
345 5
345 5
346 $ hg rebase -s9 -d0
346 $ hg rebase -s9 -d0
347 rebasing 9:2b23e52411f4 "D" (tip)
347 rebasing 9:2b23e52411f4 "D" (tip)
348 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-f942decf-rebase.hg (glob)
348 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-f942decf-rebase.hg (glob)
349 $ hg id -n # check we updated back to parent
349 $ hg id -n # check we updated back to parent
350 5
350 5
351 $ hg log --template "{phase}\n" -r 9
351 $ hg log --template "{phase}\n" -r 9
352 draft
352 draft
353 $ hg rebase -s9 -d1
353 $ hg rebase -s9 -d1
354 rebasing 9:2cb10d0cfc6c "D" (tip)
354 rebasing 9:2cb10d0cfc6c "D" (tip)
355 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-ddb0f256-rebase.hg (glob)
355 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-ddb0f256-rebase.hg (glob)
356 $ hg log --template "{phase}\n" -r 9
356 $ hg log --template "{phase}\n" -r 9
357 draft
357 draft
358 $ hg phase --force --secret 9
358 $ hg phase --force --secret 9
359 $ hg rebase -s9 -d0
359 $ hg rebase -s9 -d0
360 rebasing 9:c5b12b67163a "D" (tip)
360 rebasing 9:c5b12b67163a "D" (tip)
361 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-4e372053-rebase.hg (glob)
361 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-4e372053-rebase.hg (glob)
362 $ hg log --template "{phase}\n" -r 9
362 $ hg log --template "{phase}\n" -r 9
363 secret
363 secret
364 $ hg rebase -s9 -d1
364 $ hg rebase -s9 -d1
365 rebasing 9:2a0524f868ac "D" (tip)
365 rebasing 9:2a0524f868ac "D" (tip)
366 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-cefd8574-rebase.hg (glob)
366 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-cefd8574-rebase.hg (glob)
367 $ hg log --template "{phase}\n" -r 9
367 $ hg log --template "{phase}\n" -r 9
368 secret
368 secret
369 Source phase lower than destination phase: new changeset get the phase of destination:
369 Source phase lower than destination phase: new changeset get the phase of destination:
370 $ hg rebase -s8 -d9
370 $ hg rebase -s8 -d9
371 rebasing 8:6d4f22462821 "C"
371 rebasing 8:6d4f22462821 "C"
372 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-3441f70b-rebase.hg (glob)
372 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-3441f70b-rebase.hg (glob)
373 $ hg log --template "{phase}\n" -r 'rev(9)'
373 $ hg log --template "{phase}\n" -r 'rev(9)'
374 secret
374 secret
375
375
376 $ cd ..
376 $ cd ..
377
377
378 Check that temporary bundle doesn't lose phase when not using generaldelta
378 Check that temporary bundle doesn't lose phase when not using generaldelta
379
379
380 $ hg --config format.usegeneraldelta=no init issue5678
380 $ hg --config format.usegeneraldelta=no init issue5678
381 $ cd issue5678
381 $ cd issue5678
382 $ grep generaldelta .hg/requires
382 $ grep generaldelta .hg/requires
383 [1]
383 [1]
384 $ echo a > a
384 $ echo a > a
385 $ hg ci -Aqm a
385 $ hg ci -Aqm a
386 $ echo b > b
386 $ echo b > b
387 $ hg ci -Aqm b
387 $ hg ci -Aqm b
388 $ hg co -q '.^'
388 $ hg co -q '.^'
389 $ echo c > c
389 $ echo c > c
390 $ hg ci -Aqm c
390 $ hg ci -Aqm c
391 $ hg phase --public
391 $ hg phase --public
392 $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n'
392 $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n'
393 @ 2:d36c public c
393 @ 2:d36c public c
394 |
394 |
395 | o 1:d2ae draft b
395 | o 1:d2ae draft b
396 |/
396 |/
397 o 0:cb9a public a
397 o 0:cb9a public a
398
398
399 $ hg rebase -s 1 -d 2
399 $ hg rebase -s 1 -d 2
400 rebasing 1:d2ae7f538514 "b"
400 rebasing 1:d2ae7f538514 "b"
401 saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/d2ae7f538514-2953539b-rebase.hg (glob)
401 saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/d2ae7f538514-2953539b-rebase.hg (glob)
402 BROKEN: d36c should remain public
403 $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n'
402 $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n'
404 o 2:c882 draft b
403 o 2:c882 draft b
405 |
404 |
406 @ 1:d36c draft c
405 @ 1:d36c public c
407 |
406 |
408 o 0:cb9a public a
407 o 0:cb9a public a
409
408
410 $ cd ..
409 $ cd ..
411
410
412 Test for revset
411 Test for revset
413
412
414 We need a bit different graph
413 We need a bit different graph
415 All destination are B
414 All destination are B
416
415
417 $ hg init ah
416 $ hg init ah
418 $ cd ah
417 $ cd ah
419 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
418 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
420 adding changesets
419 adding changesets
421 adding manifests
420 adding manifests
422 adding file changes
421 adding file changes
423 added 9 changesets with 9 changes to 9 files (+2 heads)
422 added 9 changesets with 9 changes to 9 files (+2 heads)
424 (run 'hg heads' to see heads, 'hg merge' to merge)
423 (run 'hg heads' to see heads, 'hg merge' to merge)
425 $ hg tglog
424 $ hg tglog
426 o 8: 'I'
425 o 8: 'I'
427 |
426 |
428 o 7: 'H'
427 o 7: 'H'
429 |
428 |
430 o 6: 'G'
429 o 6: 'G'
431 |
430 |
432 | o 5: 'F'
431 | o 5: 'F'
433 | |
432 | |
434 | o 4: 'E'
433 | o 4: 'E'
435 |/
434 |/
436 o 3: 'D'
435 o 3: 'D'
437 |
436 |
438 o 2: 'C'
437 o 2: 'C'
439 |
438 |
440 | o 1: 'B'
439 | o 1: 'B'
441 |/
440 |/
442 o 0: 'A'
441 o 0: 'A'
443
442
444 $ cd ..
443 $ cd ..
445
444
446
445
447 Simple case with keep:
446 Simple case with keep:
448
447
449 Source on have two descendant heads but ask for one
448 Source on have two descendant heads but ask for one
450
449
451 $ hg clone -q -u . ah ah1
450 $ hg clone -q -u . ah ah1
452 $ cd ah1
451 $ cd ah1
453 $ hg rebase -r '2::8' -d 1
452 $ hg rebase -r '2::8' -d 1
454 abort: can't remove original changesets with unrebased descendants
453 abort: can't remove original changesets with unrebased descendants
455 (use --keep to keep original changesets)
454 (use --keep to keep original changesets)
456 [255]
455 [255]
457 $ hg rebase -r '2::8' -d 1 -k
456 $ hg rebase -r '2::8' -d 1 -k
458 rebasing 2:c9e50f6cdc55 "C"
457 rebasing 2:c9e50f6cdc55 "C"
459 rebasing 3:ffd453c31098 "D"
458 rebasing 3:ffd453c31098 "D"
460 rebasing 6:3d8a618087a7 "G"
459 rebasing 6:3d8a618087a7 "G"
461 rebasing 7:72434a4e60b0 "H"
460 rebasing 7:72434a4e60b0 "H"
462 rebasing 8:479ddb54a924 "I" (tip)
461 rebasing 8:479ddb54a924 "I" (tip)
463 $ hg tglog
462 $ hg tglog
464 o 13: 'I'
463 o 13: 'I'
465 |
464 |
466 o 12: 'H'
465 o 12: 'H'
467 |
466 |
468 o 11: 'G'
467 o 11: 'G'
469 |
468 |
470 o 10: 'D'
469 o 10: 'D'
471 |
470 |
472 o 9: 'C'
471 o 9: 'C'
473 |
472 |
474 | o 8: 'I'
473 | o 8: 'I'
475 | |
474 | |
476 | o 7: 'H'
475 | o 7: 'H'
477 | |
476 | |
478 | o 6: 'G'
477 | o 6: 'G'
479 | |
478 | |
480 | | o 5: 'F'
479 | | o 5: 'F'
481 | | |
480 | | |
482 | | o 4: 'E'
481 | | o 4: 'E'
483 | |/
482 | |/
484 | o 3: 'D'
483 | o 3: 'D'
485 | |
484 | |
486 | o 2: 'C'
485 | o 2: 'C'
487 | |
486 | |
488 o | 1: 'B'
487 o | 1: 'B'
489 |/
488 |/
490 o 0: 'A'
489 o 0: 'A'
491
490
492
491
493 $ cd ..
492 $ cd ..
494
493
495 Base on have one descendant heads we ask for but common ancestor have two
494 Base on have one descendant heads we ask for but common ancestor have two
496
495
497 $ hg clone -q -u . ah ah2
496 $ hg clone -q -u . ah ah2
498 $ cd ah2
497 $ cd ah2
499 $ hg rebase -r '3::8' -d 1
498 $ hg rebase -r '3::8' -d 1
500 abort: can't remove original changesets with unrebased descendants
499 abort: can't remove original changesets with unrebased descendants
501 (use --keep to keep original changesets)
500 (use --keep to keep original changesets)
502 [255]
501 [255]
503 $ hg rebase -r '3::8' -d 1 --keep
502 $ hg rebase -r '3::8' -d 1 --keep
504 rebasing 3:ffd453c31098 "D"
503 rebasing 3:ffd453c31098 "D"
505 rebasing 6:3d8a618087a7 "G"
504 rebasing 6:3d8a618087a7 "G"
506 rebasing 7:72434a4e60b0 "H"
505 rebasing 7:72434a4e60b0 "H"
507 rebasing 8:479ddb54a924 "I" (tip)
506 rebasing 8:479ddb54a924 "I" (tip)
508 $ hg tglog
507 $ hg tglog
509 o 12: 'I'
508 o 12: 'I'
510 |
509 |
511 o 11: 'H'
510 o 11: 'H'
512 |
511 |
513 o 10: 'G'
512 o 10: 'G'
514 |
513 |
515 o 9: 'D'
514 o 9: 'D'
516 |
515 |
517 | o 8: 'I'
516 | o 8: 'I'
518 | |
517 | |
519 | o 7: 'H'
518 | o 7: 'H'
520 | |
519 | |
521 | o 6: 'G'
520 | o 6: 'G'
522 | |
521 | |
523 | | o 5: 'F'
522 | | o 5: 'F'
524 | | |
523 | | |
525 | | o 4: 'E'
524 | | o 4: 'E'
526 | |/
525 | |/
527 | o 3: 'D'
526 | o 3: 'D'
528 | |
527 | |
529 | o 2: 'C'
528 | o 2: 'C'
530 | |
529 | |
531 o | 1: 'B'
530 o | 1: 'B'
532 |/
531 |/
533 o 0: 'A'
532 o 0: 'A'
534
533
535
534
536 $ cd ..
535 $ cd ..
537
536
538 rebase subset
537 rebase subset
539
538
540 $ hg clone -q -u . ah ah3
539 $ hg clone -q -u . ah ah3
541 $ cd ah3
540 $ cd ah3
542 $ hg rebase -r '3::7' -d 1
541 $ hg rebase -r '3::7' -d 1
543 abort: can't remove original changesets with unrebased descendants
542 abort: can't remove original changesets with unrebased descendants
544 (use --keep to keep original changesets)
543 (use --keep to keep original changesets)
545 [255]
544 [255]
546 $ hg rebase -r '3::7' -d 1 --keep
545 $ hg rebase -r '3::7' -d 1 --keep
547 rebasing 3:ffd453c31098 "D"
546 rebasing 3:ffd453c31098 "D"
548 rebasing 6:3d8a618087a7 "G"
547 rebasing 6:3d8a618087a7 "G"
549 rebasing 7:72434a4e60b0 "H"
548 rebasing 7:72434a4e60b0 "H"
550 $ hg tglog
549 $ hg tglog
551 o 11: 'H'
550 o 11: 'H'
552 |
551 |
553 o 10: 'G'
552 o 10: 'G'
554 |
553 |
555 o 9: 'D'
554 o 9: 'D'
556 |
555 |
557 | o 8: 'I'
556 | o 8: 'I'
558 | |
557 | |
559 | o 7: 'H'
558 | o 7: 'H'
560 | |
559 | |
561 | o 6: 'G'
560 | o 6: 'G'
562 | |
561 | |
563 | | o 5: 'F'
562 | | o 5: 'F'
564 | | |
563 | | |
565 | | o 4: 'E'
564 | | o 4: 'E'
566 | |/
565 | |/
567 | o 3: 'D'
566 | o 3: 'D'
568 | |
567 | |
569 | o 2: 'C'
568 | o 2: 'C'
570 | |
569 | |
571 o | 1: 'B'
570 o | 1: 'B'
572 |/
571 |/
573 o 0: 'A'
572 o 0: 'A'
574
573
575
574
576 $ cd ..
575 $ cd ..
577
576
578 rebase subset with multiple head
577 rebase subset with multiple head
579
578
580 $ hg clone -q -u . ah ah4
579 $ hg clone -q -u . ah ah4
581 $ cd ah4
580 $ cd ah4
582 $ hg rebase -r '3::(7+5)' -d 1
581 $ hg rebase -r '3::(7+5)' -d 1
583 abort: can't remove original changesets with unrebased descendants
582 abort: can't remove original changesets with unrebased descendants
584 (use --keep to keep original changesets)
583 (use --keep to keep original changesets)
585 [255]
584 [255]
586 $ hg rebase -r '3::(7+5)' -d 1 --keep
585 $ hg rebase -r '3::(7+5)' -d 1 --keep
587 rebasing 3:ffd453c31098 "D"
586 rebasing 3:ffd453c31098 "D"
588 rebasing 4:c01897464e7f "E"
587 rebasing 4:c01897464e7f "E"
589 rebasing 5:41bfcc75ed73 "F"
588 rebasing 5:41bfcc75ed73 "F"
590 rebasing 6:3d8a618087a7 "G"
589 rebasing 6:3d8a618087a7 "G"
591 rebasing 7:72434a4e60b0 "H"
590 rebasing 7:72434a4e60b0 "H"
592 $ hg tglog
591 $ hg tglog
593 o 13: 'H'
592 o 13: 'H'
594 |
593 |
595 o 12: 'G'
594 o 12: 'G'
596 |
595 |
597 | o 11: 'F'
596 | o 11: 'F'
598 | |
597 | |
599 | o 10: 'E'
598 | o 10: 'E'
600 |/
599 |/
601 o 9: 'D'
600 o 9: 'D'
602 |
601 |
603 | o 8: 'I'
602 | o 8: 'I'
604 | |
603 | |
605 | o 7: 'H'
604 | o 7: 'H'
606 | |
605 | |
607 | o 6: 'G'
606 | o 6: 'G'
608 | |
607 | |
609 | | o 5: 'F'
608 | | o 5: 'F'
610 | | |
609 | | |
611 | | o 4: 'E'
610 | | o 4: 'E'
612 | |/
611 | |/
613 | o 3: 'D'
612 | o 3: 'D'
614 | |
613 | |
615 | o 2: 'C'
614 | o 2: 'C'
616 | |
615 | |
617 o | 1: 'B'
616 o | 1: 'B'
618 |/
617 |/
619 o 0: 'A'
618 o 0: 'A'
620
619
621
620
622 $ cd ..
621 $ cd ..
623
622
624 More advanced tests
623 More advanced tests
625
624
626 rebase on ancestor with revset
625 rebase on ancestor with revset
627
626
628 $ hg clone -q -u . ah ah5
627 $ hg clone -q -u . ah ah5
629 $ cd ah5
628 $ cd ah5
630 $ hg rebase -r '6::' -d 2
629 $ hg rebase -r '6::' -d 2
631 rebasing 6:3d8a618087a7 "G"
630 rebasing 6:3d8a618087a7 "G"
632 rebasing 7:72434a4e60b0 "H"
631 rebasing 7:72434a4e60b0 "H"
633 rebasing 8:479ddb54a924 "I" (tip)
632 rebasing 8:479ddb54a924 "I" (tip)
634 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-b4f73f31-rebase.hg (glob)
633 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-b4f73f31-rebase.hg (glob)
635 $ hg tglog
634 $ hg tglog
636 o 8: 'I'
635 o 8: 'I'
637 |
636 |
638 o 7: 'H'
637 o 7: 'H'
639 |
638 |
640 o 6: 'G'
639 o 6: 'G'
641 |
640 |
642 | o 5: 'F'
641 | o 5: 'F'
643 | |
642 | |
644 | o 4: 'E'
643 | o 4: 'E'
645 | |
644 | |
646 | o 3: 'D'
645 | o 3: 'D'
647 |/
646 |/
648 o 2: 'C'
647 o 2: 'C'
649 |
648 |
650 | o 1: 'B'
649 | o 1: 'B'
651 |/
650 |/
652 o 0: 'A'
651 o 0: 'A'
653
652
654 $ cd ..
653 $ cd ..
655
654
656
655
657 rebase with multiple root.
656 rebase with multiple root.
658 We rebase E and G on B
657 We rebase E and G on B
659 We would expect heads are I, F if it was supported
658 We would expect heads are I, F if it was supported
660
659
661 $ hg clone -q -u . ah ah6
660 $ hg clone -q -u . ah ah6
662 $ cd ah6
661 $ cd ah6
663 $ hg rebase -r '(4+6)::' -d 1
662 $ hg rebase -r '(4+6)::' -d 1
664 rebasing 4:c01897464e7f "E"
663 rebasing 4:c01897464e7f "E"
665 rebasing 5:41bfcc75ed73 "F"
664 rebasing 5:41bfcc75ed73 "F"
666 rebasing 6:3d8a618087a7 "G"
665 rebasing 6:3d8a618087a7 "G"
667 rebasing 7:72434a4e60b0 "H"
666 rebasing 7:72434a4e60b0 "H"
668 rebasing 8:479ddb54a924 "I" (tip)
667 rebasing 8:479ddb54a924 "I" (tip)
669 saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-aae93a24-rebase.hg (glob)
668 saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-aae93a24-rebase.hg (glob)
670 $ hg tglog
669 $ hg tglog
671 o 8: 'I'
670 o 8: 'I'
672 |
671 |
673 o 7: 'H'
672 o 7: 'H'
674 |
673 |
675 o 6: 'G'
674 o 6: 'G'
676 |
675 |
677 | o 5: 'F'
676 | o 5: 'F'
678 | |
677 | |
679 | o 4: 'E'
678 | o 4: 'E'
680 |/
679 |/
681 | o 3: 'D'
680 | o 3: 'D'
682 | |
681 | |
683 | o 2: 'C'
682 | o 2: 'C'
684 | |
683 | |
685 o | 1: 'B'
684 o | 1: 'B'
686 |/
685 |/
687 o 0: 'A'
686 o 0: 'A'
688
687
689 $ cd ..
688 $ cd ..
690
689
691 More complex rebase with multiple roots
690 More complex rebase with multiple roots
692 each root have a different common ancestor with the destination and this is a detach
691 each root have a different common ancestor with the destination and this is a detach
693
692
694 (setup)
693 (setup)
695
694
696 $ hg clone -q -u . a a8
695 $ hg clone -q -u . a a8
697 $ cd a8
696 $ cd a8
698 $ echo I > I
697 $ echo I > I
699 $ hg add I
698 $ hg add I
700 $ hg commit -m I
699 $ hg commit -m I
701 $ hg up 4
700 $ hg up 4
702 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
701 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
703 $ echo I > J
702 $ echo I > J
704 $ hg add J
703 $ hg add J
705 $ hg commit -m J
704 $ hg commit -m J
706 created new head
705 created new head
707 $ echo I > K
706 $ echo I > K
708 $ hg add K
707 $ hg add K
709 $ hg commit -m K
708 $ hg commit -m K
710 $ hg tglog
709 $ hg tglog
711 @ 10: 'K'
710 @ 10: 'K'
712 |
711 |
713 o 9: 'J'
712 o 9: 'J'
714 |
713 |
715 | o 8: 'I'
714 | o 8: 'I'
716 | |
715 | |
717 | o 7: 'H'
716 | o 7: 'H'
718 | |
717 | |
719 +---o 6: 'G'
718 +---o 6: 'G'
720 | |/
719 | |/
721 | o 5: 'F'
720 | o 5: 'F'
722 | |
721 | |
723 o | 4: 'E'
722 o | 4: 'E'
724 |/
723 |/
725 | o 3: 'D'
724 | o 3: 'D'
726 | |
725 | |
727 | o 2: 'C'
726 | o 2: 'C'
728 | |
727 | |
729 | o 1: 'B'
728 | o 1: 'B'
730 |/
729 |/
731 o 0: 'A'
730 o 0: 'A'
732
731
733 (actual test)
732 (actual test)
734
733
735 $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
734 $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
736 rebasing 8:e7ec4e813ba6 "I"
735 rebasing 8:e7ec4e813ba6 "I"
737 rebasing 10:23a4ace37988 "K" (tip)
736 rebasing 10:23a4ace37988 "K" (tip)
738 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-b06984b3-rebase.hg (glob)
737 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-b06984b3-rebase.hg (glob)
739 $ hg log --rev 'children(desc(G))'
738 $ hg log --rev 'children(desc(G))'
740 changeset: 9:adb617877056
739 changeset: 9:adb617877056
741 parent: 6:eea13746799a
740 parent: 6:eea13746799a
742 user: test
741 user: test
743 date: Thu Jan 01 00:00:00 1970 +0000
742 date: Thu Jan 01 00:00:00 1970 +0000
744 summary: I
743 summary: I
745
744
746 changeset: 10:882431a34a0e
745 changeset: 10:882431a34a0e
747 tag: tip
746 tag: tip
748 parent: 6:eea13746799a
747 parent: 6:eea13746799a
749 user: test
748 user: test
750 date: Thu Jan 01 00:00:00 1970 +0000
749 date: Thu Jan 01 00:00:00 1970 +0000
751 summary: K
750 summary: K
752
751
753 $ hg tglog
752 $ hg tglog
754 @ 10: 'K'
753 @ 10: 'K'
755 |
754 |
756 | o 9: 'I'
755 | o 9: 'I'
757 |/
756 |/
758 | o 8: 'J'
757 | o 8: 'J'
759 | |
758 | |
760 | | o 7: 'H'
759 | | o 7: 'H'
761 | | |
760 | | |
762 o---+ 6: 'G'
761 o---+ 6: 'G'
763 |/ /
762 |/ /
764 | o 5: 'F'
763 | o 5: 'F'
765 | |
764 | |
766 o | 4: 'E'
765 o | 4: 'E'
767 |/
766 |/
768 | o 3: 'D'
767 | o 3: 'D'
769 | |
768 | |
770 | o 2: 'C'
769 | o 2: 'C'
771 | |
770 | |
772 | o 1: 'B'
771 | o 1: 'B'
773 |/
772 |/
774 o 0: 'A'
773 o 0: 'A'
775
774
776
775
777 Test that rebase is not confused by $CWD disappearing during rebase (issue4121)
776 Test that rebase is not confused by $CWD disappearing during rebase (issue4121)
778
777
779 $ cd ..
778 $ cd ..
780 $ hg init cwd-vanish
779 $ hg init cwd-vanish
781 $ cd cwd-vanish
780 $ cd cwd-vanish
782 $ touch initial-file
781 $ touch initial-file
783 $ hg add initial-file
782 $ hg add initial-file
784 $ hg commit -m 'initial commit'
783 $ hg commit -m 'initial commit'
785 $ touch dest-file
784 $ touch dest-file
786 $ hg add dest-file
785 $ hg add dest-file
787 $ hg commit -m 'dest commit'
786 $ hg commit -m 'dest commit'
788 $ hg up 0
787 $ hg up 0
789 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
788 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
790 $ touch other-file
789 $ touch other-file
791 $ hg add other-file
790 $ hg add other-file
792 $ hg commit -m 'first source commit'
791 $ hg commit -m 'first source commit'
793 created new head
792 created new head
794 $ mkdir subdir
793 $ mkdir subdir
795 $ cd subdir
794 $ cd subdir
796 $ touch subfile
795 $ touch subfile
797 $ hg add subfile
796 $ hg add subfile
798 $ hg commit -m 'second source with subdir'
797 $ hg commit -m 'second source with subdir'
799
798
800 $ hg rebase -b . -d 1 --traceback
799 $ hg rebase -b . -d 1 --traceback
801 rebasing 2:779a07b1b7a0 "first source commit"
800 rebasing 2:779a07b1b7a0 "first source commit"
802 current directory was removed (rmcwd !)
801 current directory was removed (rmcwd !)
803 (consider changing to repo root: $TESTTMP/cwd-vanish) (rmcwd !)
802 (consider changing to repo root: $TESTTMP/cwd-vanish) (rmcwd !)
804 rebasing 3:a7d6f3a00bf3 "second source with subdir" (tip)
803 rebasing 3:a7d6f3a00bf3 "second source with subdir" (tip)
805 saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-853e0073-rebase.hg (glob)
804 saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-853e0073-rebase.hg (glob)
806
805
807 Get back to the root of cwd-vanish. Note that even though `cd ..`
806 Get back to the root of cwd-vanish. Note that even though `cd ..`
808 works on most systems, it does not work on FreeBSD 10, so we use an
807 works on most systems, it does not work on FreeBSD 10, so we use an
809 absolute path to get back to the repository.
808 absolute path to get back to the repository.
810 $ cd $TESTTMP
809 $ cd $TESTTMP
811
810
812 Test that rebase is done in topo order (issue5370)
811 Test that rebase is done in topo order (issue5370)
813
812
814 $ hg init order
813 $ hg init order
815 $ cd order
814 $ cd order
816 $ touch a && hg add a && hg ci -m A
815 $ touch a && hg add a && hg ci -m A
817 $ touch b && hg add b && hg ci -m B
816 $ touch b && hg add b && hg ci -m B
818 $ touch c && hg add c && hg ci -m C
817 $ touch c && hg add c && hg ci -m C
819 $ hg up 1
818 $ hg up 1
820 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
819 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
821 $ touch d && hg add d && hg ci -m D
820 $ touch d && hg add d && hg ci -m D
822 created new head
821 created new head
823 $ hg up 2
822 $ hg up 2
824 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
823 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
825 $ touch e && hg add e && hg ci -m E
824 $ touch e && hg add e && hg ci -m E
826 $ hg up 3
825 $ hg up 3
827 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
826 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
828 $ touch f && hg add f && hg ci -m F
827 $ touch f && hg add f && hg ci -m F
829 $ hg up 0
828 $ hg up 0
830 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
829 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
831 $ touch g && hg add g && hg ci -m G
830 $ touch g && hg add g && hg ci -m G
832 created new head
831 created new head
833
832
834 $ hg tglog
833 $ hg tglog
835 @ 6: 'G'
834 @ 6: 'G'
836 |
835 |
837 | o 5: 'F'
836 | o 5: 'F'
838 | |
837 | |
839 | | o 4: 'E'
838 | | o 4: 'E'
840 | | |
839 | | |
841 | o | 3: 'D'
840 | o | 3: 'D'
842 | | |
841 | | |
843 | | o 2: 'C'
842 | | o 2: 'C'
844 | |/
843 | |/
845 | o 1: 'B'
844 | o 1: 'B'
846 |/
845 |/
847 o 0: 'A'
846 o 0: 'A'
848
847
849
848
850 $ hg rebase -s 1 -d 6
849 $ hg rebase -s 1 -d 6
851 rebasing 1:76035bbd54bd "B"
850 rebasing 1:76035bbd54bd "B"
852 rebasing 2:d84f5cfaaf14 "C"
851 rebasing 2:d84f5cfaaf14 "C"
853 rebasing 4:82ae8dc7a9b7 "E"
852 rebasing 4:82ae8dc7a9b7 "E"
854 rebasing 3:ab709c9f7171 "D"
853 rebasing 3:ab709c9f7171 "D"
855 rebasing 5:412b391de760 "F"
854 rebasing 5:412b391de760 "F"
856 saved backup bundle to $TESTTMP/order/.hg/strip-backup/76035bbd54bd-e341bc99-rebase.hg (glob)
855 saved backup bundle to $TESTTMP/order/.hg/strip-backup/76035bbd54bd-e341bc99-rebase.hg (glob)
857
856
858 $ hg tglog
857 $ hg tglog
859 o 6: 'F'
858 o 6: 'F'
860 |
859 |
861 o 5: 'D'
860 o 5: 'D'
862 |
861 |
863 | o 4: 'E'
862 | o 4: 'E'
864 | |
863 | |
865 | o 3: 'C'
864 | o 3: 'C'
866 |/
865 |/
867 o 2: 'B'
866 o 2: 'B'
868 |
867 |
869 @ 1: 'G'
868 @ 1: 'G'
870 |
869 |
871 o 0: 'A'
870 o 0: 'A'
872
871
873
872
874 Test experimental revset
873 Test experimental revset
875 ========================
874 ========================
876
875
877 $ cd ../cwd-vanish
876 $ cd ../cwd-vanish
878
877
879 Make the repo a bit more interesting
878 Make the repo a bit more interesting
880
879
881 $ hg up 1
880 $ hg up 1
882 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
881 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
883 $ echo aaa > aaa
882 $ echo aaa > aaa
884 $ hg add aaa
883 $ hg add aaa
885 $ hg commit -m aaa
884 $ hg commit -m aaa
886 created new head
885 created new head
887 $ hg log -G
886 $ hg log -G
888 @ changeset: 4:5f7bc9025ed2
887 @ changeset: 4:5f7bc9025ed2
889 | tag: tip
888 | tag: tip
890 | parent: 1:58d79cc1cf43
889 | parent: 1:58d79cc1cf43
891 | user: test
890 | user: test
892 | date: Thu Jan 01 00:00:00 1970 +0000
891 | date: Thu Jan 01 00:00:00 1970 +0000
893 | summary: aaa
892 | summary: aaa
894 |
893 |
895 | o changeset: 3:1910d5ff34ea
894 | o changeset: 3:1910d5ff34ea
896 | | user: test
895 | | user: test
897 | | date: Thu Jan 01 00:00:00 1970 +0000
896 | | date: Thu Jan 01 00:00:00 1970 +0000
898 | | summary: second source with subdir
897 | | summary: second source with subdir
899 | |
898 | |
900 | o changeset: 2:82901330b6ef
899 | o changeset: 2:82901330b6ef
901 |/ user: test
900 |/ user: test
902 | date: Thu Jan 01 00:00:00 1970 +0000
901 | date: Thu Jan 01 00:00:00 1970 +0000
903 | summary: first source commit
902 | summary: first source commit
904 |
903 |
905 o changeset: 1:58d79cc1cf43
904 o changeset: 1:58d79cc1cf43
906 | user: test
905 | user: test
907 | date: Thu Jan 01 00:00:00 1970 +0000
906 | date: Thu Jan 01 00:00:00 1970 +0000
908 | summary: dest commit
907 | summary: dest commit
909 |
908 |
910 o changeset: 0:e94b687f7da3
909 o changeset: 0:e94b687f7da3
911 user: test
910 user: test
912 date: Thu Jan 01 00:00:00 1970 +0000
911 date: Thu Jan 01 00:00:00 1970 +0000
913 summary: initial commit
912 summary: initial commit
914
913
915
914
916 Testing from lower head
915 Testing from lower head
917
916
918 $ hg up 3
917 $ hg up 3
919 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
918 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
920 $ hg log -r '_destrebase()'
919 $ hg log -r '_destrebase()'
921 changeset: 4:5f7bc9025ed2
920 changeset: 4:5f7bc9025ed2
922 tag: tip
921 tag: tip
923 parent: 1:58d79cc1cf43
922 parent: 1:58d79cc1cf43
924 user: test
923 user: test
925 date: Thu Jan 01 00:00:00 1970 +0000
924 date: Thu Jan 01 00:00:00 1970 +0000
926 summary: aaa
925 summary: aaa
927
926
928
927
929 Testing from upper head
928 Testing from upper head
930
929
931 $ hg log -r '_destrebase(4)'
930 $ hg log -r '_destrebase(4)'
932 changeset: 3:1910d5ff34ea
931 changeset: 3:1910d5ff34ea
933 user: test
932 user: test
934 date: Thu Jan 01 00:00:00 1970 +0000
933 date: Thu Jan 01 00:00:00 1970 +0000
935 summary: second source with subdir
934 summary: second source with subdir
936
935
937 $ hg up 4
936 $ hg up 4
938 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
937 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
939 $ hg log -r '_destrebase()'
938 $ hg log -r '_destrebase()'
940 changeset: 3:1910d5ff34ea
939 changeset: 3:1910d5ff34ea
941 user: test
940 user: test
942 date: Thu Jan 01 00:00:00 1970 +0000
941 date: Thu Jan 01 00:00:00 1970 +0000
943 summary: second source with subdir
942 summary: second source with subdir
944
943
945 Testing rebase being called inside another transaction
944 Testing rebase being called inside another transaction
946
945
947 $ cd $TESTTMP
946 $ cd $TESTTMP
948 $ hg init tr-state
947 $ hg init tr-state
949 $ cd tr-state
948 $ cd tr-state
950 $ cat > $TESTTMP/wraprebase.py <<EOF
949 $ cat > $TESTTMP/wraprebase.py <<EOF
951 > from __future__ import absolute_import
950 > from __future__ import absolute_import
952 > from mercurial import extensions
951 > from mercurial import extensions
953 > def _rebase(orig, ui, repo, *args, **kwargs):
952 > def _rebase(orig, ui, repo, *args, **kwargs):
954 > with repo.wlock():
953 > with repo.wlock():
955 > with repo.lock():
954 > with repo.lock():
956 > with repo.transaction('wrappedrebase'):
955 > with repo.transaction('wrappedrebase'):
957 > return orig(ui, repo, *args, **kwargs)
956 > return orig(ui, repo, *args, **kwargs)
958 > def wraprebase(loaded):
957 > def wraprebase(loaded):
959 > assert loaded
958 > assert loaded
960 > rebasemod = extensions.find('rebase')
959 > rebasemod = extensions.find('rebase')
961 > extensions.wrapcommand(rebasemod.cmdtable, 'rebase', _rebase)
960 > extensions.wrapcommand(rebasemod.cmdtable, 'rebase', _rebase)
962 > def extsetup(ui):
961 > def extsetup(ui):
963 > extensions.afterloaded('rebase', wraprebase)
962 > extensions.afterloaded('rebase', wraprebase)
964 > EOF
963 > EOF
965
964
966 $ cat >> .hg/hgrc <<EOF
965 $ cat >> .hg/hgrc <<EOF
967 > [extensions]
966 > [extensions]
968 > wraprebase=$TESTTMP/wraprebase.py
967 > wraprebase=$TESTTMP/wraprebase.py
969 > [experimental]
968 > [experimental]
970 > evolution=all
969 > evolution=all
971 > EOF
970 > EOF
972
971
973 $ hg debugdrawdag <<'EOS'
972 $ hg debugdrawdag <<'EOS'
974 > B C
973 > B C
975 > |/
974 > |/
976 > A
975 > A
977 > EOS
976 > EOS
978
977
979 $ hg rebase -s C -d B
978 $ hg rebase -s C -d B
980 rebasing 2:dc0947a82db8 "C" (C tip)
979 rebasing 2:dc0947a82db8 "C" (C tip)
981
980
982 $ [ -f .hg/rebasestate ] && echo 'WRONG: rebasestate should not exist'
981 $ [ -f .hg/rebasestate ] && echo 'WRONG: rebasestate should not exist'
983 [1]
982 [1]
@@ -1,1121 +1,1121 b''
1 $ echo "[format]" >> $HGRCPATH
1 $ echo "[format]" >> $HGRCPATH
2 $ echo "usegeneraldelta=yes" >> $HGRCPATH
2 $ echo "usegeneraldelta=yes" >> $HGRCPATH
3 $ echo "[extensions]" >> $HGRCPATH
3 $ echo "[extensions]" >> $HGRCPATH
4 $ echo "strip=" >> $HGRCPATH
4 $ echo "strip=" >> $HGRCPATH
5 $ echo "drawdag=$TESTDIR/drawdag.py" >> $HGRCPATH
5 $ echo "drawdag=$TESTDIR/drawdag.py" >> $HGRCPATH
6
6
7 $ restore() {
7 $ restore() {
8 > hg unbundle -q .hg/strip-backup/*
8 > hg unbundle -q .hg/strip-backup/*
9 > rm .hg/strip-backup/*
9 > rm .hg/strip-backup/*
10 > }
10 > }
11 $ teststrip() {
11 $ teststrip() {
12 > hg up -C $1
12 > hg up -C $1
13 > echo % before update $1, strip $2
13 > echo % before update $1, strip $2
14 > hg parents
14 > hg parents
15 > hg --traceback strip $2
15 > hg --traceback strip $2
16 > echo % after update $1, strip $2
16 > echo % after update $1, strip $2
17 > hg parents
17 > hg parents
18 > restore
18 > restore
19 > }
19 > }
20
20
21 $ hg init test
21 $ hg init test
22 $ cd test
22 $ cd test
23
23
24 $ echo foo > bar
24 $ echo foo > bar
25 $ hg ci -Ama
25 $ hg ci -Ama
26 adding bar
26 adding bar
27
27
28 $ echo more >> bar
28 $ echo more >> bar
29 $ hg ci -Amb
29 $ hg ci -Amb
30
30
31 $ echo blah >> bar
31 $ echo blah >> bar
32 $ hg ci -Amc
32 $ hg ci -Amc
33
33
34 $ hg up 1
34 $ hg up 1
35 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ echo blah >> bar
36 $ echo blah >> bar
37 $ hg ci -Amd
37 $ hg ci -Amd
38 created new head
38 created new head
39
39
40 $ echo final >> bar
40 $ echo final >> bar
41 $ hg ci -Ame
41 $ hg ci -Ame
42
42
43 $ hg log
43 $ hg log
44 changeset: 4:443431ffac4f
44 changeset: 4:443431ffac4f
45 tag: tip
45 tag: tip
46 user: test
46 user: test
47 date: Thu Jan 01 00:00:00 1970 +0000
47 date: Thu Jan 01 00:00:00 1970 +0000
48 summary: e
48 summary: e
49
49
50 changeset: 3:65bd5f99a4a3
50 changeset: 3:65bd5f99a4a3
51 parent: 1:ef3a871183d7
51 parent: 1:ef3a871183d7
52 user: test
52 user: test
53 date: Thu Jan 01 00:00:00 1970 +0000
53 date: Thu Jan 01 00:00:00 1970 +0000
54 summary: d
54 summary: d
55
55
56 changeset: 2:264128213d29
56 changeset: 2:264128213d29
57 user: test
57 user: test
58 date: Thu Jan 01 00:00:00 1970 +0000
58 date: Thu Jan 01 00:00:00 1970 +0000
59 summary: c
59 summary: c
60
60
61 changeset: 1:ef3a871183d7
61 changeset: 1:ef3a871183d7
62 user: test
62 user: test
63 date: Thu Jan 01 00:00:00 1970 +0000
63 date: Thu Jan 01 00:00:00 1970 +0000
64 summary: b
64 summary: b
65
65
66 changeset: 0:9ab35a2d17cb
66 changeset: 0:9ab35a2d17cb
67 user: test
67 user: test
68 date: Thu Jan 01 00:00:00 1970 +0000
68 date: Thu Jan 01 00:00:00 1970 +0000
69 summary: a
69 summary: a
70
70
71
71
72 $ teststrip 4 4
72 $ teststrip 4 4
73 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
73 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
74 % before update 4, strip 4
74 % before update 4, strip 4
75 changeset: 4:443431ffac4f
75 changeset: 4:443431ffac4f
76 tag: tip
76 tag: tip
77 user: test
77 user: test
78 date: Thu Jan 01 00:00:00 1970 +0000
78 date: Thu Jan 01 00:00:00 1970 +0000
79 summary: e
79 summary: e
80
80
81 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
82 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
83 % after update 4, strip 4
83 % after update 4, strip 4
84 changeset: 3:65bd5f99a4a3
84 changeset: 3:65bd5f99a4a3
85 tag: tip
85 tag: tip
86 parent: 1:ef3a871183d7
86 parent: 1:ef3a871183d7
87 user: test
87 user: test
88 date: Thu Jan 01 00:00:00 1970 +0000
88 date: Thu Jan 01 00:00:00 1970 +0000
89 summary: d
89 summary: d
90
90
91 $ teststrip 4 3
91 $ teststrip 4 3
92 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 % before update 4, strip 3
93 % before update 4, strip 3
94 changeset: 4:443431ffac4f
94 changeset: 4:443431ffac4f
95 tag: tip
95 tag: tip
96 user: test
96 user: test
97 date: Thu Jan 01 00:00:00 1970 +0000
97 date: Thu Jan 01 00:00:00 1970 +0000
98 summary: e
98 summary: e
99
99
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
101 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
102 % after update 4, strip 3
102 % after update 4, strip 3
103 changeset: 1:ef3a871183d7
103 changeset: 1:ef3a871183d7
104 user: test
104 user: test
105 date: Thu Jan 01 00:00:00 1970 +0000
105 date: Thu Jan 01 00:00:00 1970 +0000
106 summary: b
106 summary: b
107
107
108 $ teststrip 1 4
108 $ teststrip 1 4
109 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 % before update 1, strip 4
110 % before update 1, strip 4
111 changeset: 1:ef3a871183d7
111 changeset: 1:ef3a871183d7
112 user: test
112 user: test
113 date: Thu Jan 01 00:00:00 1970 +0000
113 date: Thu Jan 01 00:00:00 1970 +0000
114 summary: b
114 summary: b
115
115
116 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
116 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
117 % after update 1, strip 4
117 % after update 1, strip 4
118 changeset: 1:ef3a871183d7
118 changeset: 1:ef3a871183d7
119 user: test
119 user: test
120 date: Thu Jan 01 00:00:00 1970 +0000
120 date: Thu Jan 01 00:00:00 1970 +0000
121 summary: b
121 summary: b
122
122
123 $ teststrip 4 2
123 $ teststrip 4 2
124 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 % before update 4, strip 2
125 % before update 4, strip 2
126 changeset: 4:443431ffac4f
126 changeset: 4:443431ffac4f
127 tag: tip
127 tag: tip
128 user: test
128 user: test
129 date: Thu Jan 01 00:00:00 1970 +0000
129 date: Thu Jan 01 00:00:00 1970 +0000
130 summary: e
130 summary: e
131
131
132 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
132 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
133 % after update 4, strip 2
133 % after update 4, strip 2
134 changeset: 3:443431ffac4f
134 changeset: 3:443431ffac4f
135 tag: tip
135 tag: tip
136 user: test
136 user: test
137 date: Thu Jan 01 00:00:00 1970 +0000
137 date: Thu Jan 01 00:00:00 1970 +0000
138 summary: e
138 summary: e
139
139
140 $ teststrip 4 1
140 $ teststrip 4 1
141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 % before update 4, strip 1
142 % before update 4, strip 1
143 changeset: 4:264128213d29
143 changeset: 4:264128213d29
144 tag: tip
144 tag: tip
145 parent: 1:ef3a871183d7
145 parent: 1:ef3a871183d7
146 user: test
146 user: test
147 date: Thu Jan 01 00:00:00 1970 +0000
147 date: Thu Jan 01 00:00:00 1970 +0000
148 summary: c
148 summary: c
149
149
150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
151 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
152 % after update 4, strip 1
152 % after update 4, strip 1
153 changeset: 0:9ab35a2d17cb
153 changeset: 0:9ab35a2d17cb
154 tag: tip
154 tag: tip
155 user: test
155 user: test
156 date: Thu Jan 01 00:00:00 1970 +0000
156 date: Thu Jan 01 00:00:00 1970 +0000
157 summary: a
157 summary: a
158
158
159 $ teststrip null 4
159 $ teststrip null 4
160 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
160 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
161 % before update null, strip 4
161 % before update null, strip 4
162 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
162 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
163 % after update null, strip 4
163 % after update null, strip 4
164
164
165 $ hg log
165 $ hg log
166 changeset: 4:264128213d29
166 changeset: 4:264128213d29
167 tag: tip
167 tag: tip
168 parent: 1:ef3a871183d7
168 parent: 1:ef3a871183d7
169 user: test
169 user: test
170 date: Thu Jan 01 00:00:00 1970 +0000
170 date: Thu Jan 01 00:00:00 1970 +0000
171 summary: c
171 summary: c
172
172
173 changeset: 3:443431ffac4f
173 changeset: 3:443431ffac4f
174 user: test
174 user: test
175 date: Thu Jan 01 00:00:00 1970 +0000
175 date: Thu Jan 01 00:00:00 1970 +0000
176 summary: e
176 summary: e
177
177
178 changeset: 2:65bd5f99a4a3
178 changeset: 2:65bd5f99a4a3
179 user: test
179 user: test
180 date: Thu Jan 01 00:00:00 1970 +0000
180 date: Thu Jan 01 00:00:00 1970 +0000
181 summary: d
181 summary: d
182
182
183 changeset: 1:ef3a871183d7
183 changeset: 1:ef3a871183d7
184 user: test
184 user: test
185 date: Thu Jan 01 00:00:00 1970 +0000
185 date: Thu Jan 01 00:00:00 1970 +0000
186 summary: b
186 summary: b
187
187
188 changeset: 0:9ab35a2d17cb
188 changeset: 0:9ab35a2d17cb
189 user: test
189 user: test
190 date: Thu Jan 01 00:00:00 1970 +0000
190 date: Thu Jan 01 00:00:00 1970 +0000
191 summary: a
191 summary: a
192
192
193 $ hg up -C 4
193 $ hg up -C 4
194 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 $ hg parents
195 $ hg parents
196 changeset: 4:264128213d29
196 changeset: 4:264128213d29
197 tag: tip
197 tag: tip
198 parent: 1:ef3a871183d7
198 parent: 1:ef3a871183d7
199 user: test
199 user: test
200 date: Thu Jan 01 00:00:00 1970 +0000
200 date: Thu Jan 01 00:00:00 1970 +0000
201 summary: c
201 summary: c
202
202
203
203
204 $ hg --traceback strip 4
204 $ hg --traceback strip 4
205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
206 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
206 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
207 $ hg parents
207 $ hg parents
208 changeset: 1:ef3a871183d7
208 changeset: 1:ef3a871183d7
209 user: test
209 user: test
210 date: Thu Jan 01 00:00:00 1970 +0000
210 date: Thu Jan 01 00:00:00 1970 +0000
211 summary: b
211 summary: b
212
212
213 $ hg debugbundle .hg/strip-backup/*
213 $ hg debugbundle .hg/strip-backup/*
214 Stream params: sortdict([('Compression', 'BZ')])
214 Stream params: sortdict([('Compression', 'BZ')])
215 changegroup -- "sortdict([('version', '02'), ('nbchanges', '1')])"
215 changegroup -- "sortdict([('version', '02'), ('nbchanges', '1')])"
216 264128213d290d868c54642d13aeaa3675551a78
216 264128213d290d868c54642d13aeaa3675551a78
217 phase-heads -- 'sortdict()'
217 phase-heads -- 'sortdict()'
218 264128213d290d868c54642d13aeaa3675551a78 draft
218 264128213d290d868c54642d13aeaa3675551a78 draft
219 $ hg pull .hg/strip-backup/*
219 $ hg pull .hg/strip-backup/*
220 pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
220 pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
221 searching for changes
221 searching for changes
222 adding changesets
222 adding changesets
223 adding manifests
223 adding manifests
224 adding file changes
224 adding file changes
225 added 1 changesets with 0 changes to 0 files (+1 heads)
225 added 1 changesets with 0 changes to 0 files (+1 heads)
226 (run 'hg heads' to see heads, 'hg merge' to merge)
226 (run 'hg heads' to see heads, 'hg merge' to merge)
227 $ rm .hg/strip-backup/*
227 $ rm .hg/strip-backup/*
228 $ hg log --graph
228 $ hg log --graph
229 o changeset: 4:264128213d29
229 o changeset: 4:264128213d29
230 | tag: tip
230 | tag: tip
231 | parent: 1:ef3a871183d7
231 | parent: 1:ef3a871183d7
232 | user: test
232 | user: test
233 | date: Thu Jan 01 00:00:00 1970 +0000
233 | date: Thu Jan 01 00:00:00 1970 +0000
234 | summary: c
234 | summary: c
235 |
235 |
236 | o changeset: 3:443431ffac4f
236 | o changeset: 3:443431ffac4f
237 | | user: test
237 | | user: test
238 | | date: Thu Jan 01 00:00:00 1970 +0000
238 | | date: Thu Jan 01 00:00:00 1970 +0000
239 | | summary: e
239 | | summary: e
240 | |
240 | |
241 | o changeset: 2:65bd5f99a4a3
241 | o changeset: 2:65bd5f99a4a3
242 |/ user: test
242 |/ user: test
243 | date: Thu Jan 01 00:00:00 1970 +0000
243 | date: Thu Jan 01 00:00:00 1970 +0000
244 | summary: d
244 | summary: d
245 |
245 |
246 @ changeset: 1:ef3a871183d7
246 @ changeset: 1:ef3a871183d7
247 | user: test
247 | user: test
248 | date: Thu Jan 01 00:00:00 1970 +0000
248 | date: Thu Jan 01 00:00:00 1970 +0000
249 | summary: b
249 | summary: b
250 |
250 |
251 o changeset: 0:9ab35a2d17cb
251 o changeset: 0:9ab35a2d17cb
252 user: test
252 user: test
253 date: Thu Jan 01 00:00:00 1970 +0000
253 date: Thu Jan 01 00:00:00 1970 +0000
254 summary: a
254 summary: a
255
255
256 $ hg up -C 2
256 $ hg up -C 2
257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 $ hg merge 4
258 $ hg merge 4
259 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 (branch merge, don't forget to commit)
260 (branch merge, don't forget to commit)
261
261
262 before strip of merge parent
262 before strip of merge parent
263
263
264 $ hg parents
264 $ hg parents
265 changeset: 2:65bd5f99a4a3
265 changeset: 2:65bd5f99a4a3
266 user: test
266 user: test
267 date: Thu Jan 01 00:00:00 1970 +0000
267 date: Thu Jan 01 00:00:00 1970 +0000
268 summary: d
268 summary: d
269
269
270 changeset: 4:264128213d29
270 changeset: 4:264128213d29
271 tag: tip
271 tag: tip
272 parent: 1:ef3a871183d7
272 parent: 1:ef3a871183d7
273 user: test
273 user: test
274 date: Thu Jan 01 00:00:00 1970 +0000
274 date: Thu Jan 01 00:00:00 1970 +0000
275 summary: c
275 summary: c
276
276
277 $ hg strip 4
277 $ hg strip 4
278 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
278 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
279 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
280
280
281 after strip of merge parent
281 after strip of merge parent
282
282
283 $ hg parents
283 $ hg parents
284 changeset: 1:ef3a871183d7
284 changeset: 1:ef3a871183d7
285 user: test
285 user: test
286 date: Thu Jan 01 00:00:00 1970 +0000
286 date: Thu Jan 01 00:00:00 1970 +0000
287 summary: b
287 summary: b
288
288
289 $ restore
289 $ restore
290
290
291 $ hg up
291 $ hg up
292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
293 updated to "264128213d29: c"
293 updated to "264128213d29: c"
294 1 other heads for branch "default"
294 1 other heads for branch "default"
295 $ hg log -G
295 $ hg log -G
296 @ changeset: 4:264128213d29
296 @ changeset: 4:264128213d29
297 | tag: tip
297 | tag: tip
298 | parent: 1:ef3a871183d7
298 | parent: 1:ef3a871183d7
299 | user: test
299 | user: test
300 | date: Thu Jan 01 00:00:00 1970 +0000
300 | date: Thu Jan 01 00:00:00 1970 +0000
301 | summary: c
301 | summary: c
302 |
302 |
303 | o changeset: 3:443431ffac4f
303 | o changeset: 3:443431ffac4f
304 | | user: test
304 | | user: test
305 | | date: Thu Jan 01 00:00:00 1970 +0000
305 | | date: Thu Jan 01 00:00:00 1970 +0000
306 | | summary: e
306 | | summary: e
307 | |
307 | |
308 | o changeset: 2:65bd5f99a4a3
308 | o changeset: 2:65bd5f99a4a3
309 |/ user: test
309 |/ user: test
310 | date: Thu Jan 01 00:00:00 1970 +0000
310 | date: Thu Jan 01 00:00:00 1970 +0000
311 | summary: d
311 | summary: d
312 |
312 |
313 o changeset: 1:ef3a871183d7
313 o changeset: 1:ef3a871183d7
314 | user: test
314 | user: test
315 | date: Thu Jan 01 00:00:00 1970 +0000
315 | date: Thu Jan 01 00:00:00 1970 +0000
316 | summary: b
316 | summary: b
317 |
317 |
318 o changeset: 0:9ab35a2d17cb
318 o changeset: 0:9ab35a2d17cb
319 user: test
319 user: test
320 date: Thu Jan 01 00:00:00 1970 +0000
320 date: Thu Jan 01 00:00:00 1970 +0000
321 summary: a
321 summary: a
322
322
323
323
324 2 is parent of 3, only one strip should happen
324 2 is parent of 3, only one strip should happen
325
325
326 $ hg strip "roots(2)" 3
326 $ hg strip "roots(2)" 3
327 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
327 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
328 $ hg log -G
328 $ hg log -G
329 @ changeset: 2:264128213d29
329 @ changeset: 2:264128213d29
330 | tag: tip
330 | tag: tip
331 | user: test
331 | user: test
332 | date: Thu Jan 01 00:00:00 1970 +0000
332 | date: Thu Jan 01 00:00:00 1970 +0000
333 | summary: c
333 | summary: c
334 |
334 |
335 o changeset: 1:ef3a871183d7
335 o changeset: 1:ef3a871183d7
336 | user: test
336 | user: test
337 | date: Thu Jan 01 00:00:00 1970 +0000
337 | date: Thu Jan 01 00:00:00 1970 +0000
338 | summary: b
338 | summary: b
339 |
339 |
340 o changeset: 0:9ab35a2d17cb
340 o changeset: 0:9ab35a2d17cb
341 user: test
341 user: test
342 date: Thu Jan 01 00:00:00 1970 +0000
342 date: Thu Jan 01 00:00:00 1970 +0000
343 summary: a
343 summary: a
344
344
345 $ restore
345 $ restore
346 $ hg log -G
346 $ hg log -G
347 o changeset: 4:443431ffac4f
347 o changeset: 4:443431ffac4f
348 | tag: tip
348 | tag: tip
349 | user: test
349 | user: test
350 | date: Thu Jan 01 00:00:00 1970 +0000
350 | date: Thu Jan 01 00:00:00 1970 +0000
351 | summary: e
351 | summary: e
352 |
352 |
353 o changeset: 3:65bd5f99a4a3
353 o changeset: 3:65bd5f99a4a3
354 | parent: 1:ef3a871183d7
354 | parent: 1:ef3a871183d7
355 | user: test
355 | user: test
356 | date: Thu Jan 01 00:00:00 1970 +0000
356 | date: Thu Jan 01 00:00:00 1970 +0000
357 | summary: d
357 | summary: d
358 |
358 |
359 | @ changeset: 2:264128213d29
359 | @ changeset: 2:264128213d29
360 |/ user: test
360 |/ user: test
361 | date: Thu Jan 01 00:00:00 1970 +0000
361 | date: Thu Jan 01 00:00:00 1970 +0000
362 | summary: c
362 | summary: c
363 |
363 |
364 o changeset: 1:ef3a871183d7
364 o changeset: 1:ef3a871183d7
365 | user: test
365 | user: test
366 | date: Thu Jan 01 00:00:00 1970 +0000
366 | date: Thu Jan 01 00:00:00 1970 +0000
367 | summary: b
367 | summary: b
368 |
368 |
369 o changeset: 0:9ab35a2d17cb
369 o changeset: 0:9ab35a2d17cb
370 user: test
370 user: test
371 date: Thu Jan 01 00:00:00 1970 +0000
371 date: Thu Jan 01 00:00:00 1970 +0000
372 summary: a
372 summary: a
373
373
374 Failed hook while applying "saveheads" bundle.
374 Failed hook while applying "saveheads" bundle.
375
375
376 $ hg strip 2 --config hooks.pretxnchangegroup.bad=false
376 $ hg strip 2 --config hooks.pretxnchangegroup.bad=false
377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
378 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
379 transaction abort!
379 transaction abort!
380 rollback completed
380 rollback completed
381 strip failed, backup bundle stored in '$TESTTMP/test/.hg/strip-backup/*-backup.hg' (glob)
381 strip failed, backup bundle stored in '$TESTTMP/test/.hg/strip-backup/*-backup.hg' (glob)
382 strip failed, unrecovered changes stored in '$TESTTMP/test/.hg/strip-backup/*-temp.hg' (glob)
382 strip failed, unrecovered changes stored in '$TESTTMP/test/.hg/strip-backup/*-temp.hg' (glob)
383 (fix the problem, then recover the changesets with "hg unbundle '$TESTTMP/test/.hg/strip-backup/*-temp.hg'") (glob)
383 (fix the problem, then recover the changesets with "hg unbundle '$TESTTMP/test/.hg/strip-backup/*-temp.hg'") (glob)
384 abort: pretxnchangegroup.bad hook exited with status 1
384 abort: pretxnchangegroup.bad hook exited with status 1
385 [255]
385 [255]
386 $ restore
386 $ restore
387 $ hg log -G
387 $ hg log -G
388 o changeset: 4:443431ffac4f
388 o changeset: 4:443431ffac4f
389 | tag: tip
389 | tag: tip
390 | user: test
390 | user: test
391 | date: Thu Jan 01 00:00:00 1970 +0000
391 | date: Thu Jan 01 00:00:00 1970 +0000
392 | summary: e
392 | summary: e
393 |
393 |
394 o changeset: 3:65bd5f99a4a3
394 o changeset: 3:65bd5f99a4a3
395 | parent: 1:ef3a871183d7
395 | parent: 1:ef3a871183d7
396 | user: test
396 | user: test
397 | date: Thu Jan 01 00:00:00 1970 +0000
397 | date: Thu Jan 01 00:00:00 1970 +0000
398 | summary: d
398 | summary: d
399 |
399 |
400 | o changeset: 2:264128213d29
400 | o changeset: 2:264128213d29
401 |/ user: test
401 |/ user: test
402 | date: Thu Jan 01 00:00:00 1970 +0000
402 | date: Thu Jan 01 00:00:00 1970 +0000
403 | summary: c
403 | summary: c
404 |
404 |
405 @ changeset: 1:ef3a871183d7
405 @ changeset: 1:ef3a871183d7
406 | user: test
406 | user: test
407 | date: Thu Jan 01 00:00:00 1970 +0000
407 | date: Thu Jan 01 00:00:00 1970 +0000
408 | summary: b
408 | summary: b
409 |
409 |
410 o changeset: 0:9ab35a2d17cb
410 o changeset: 0:9ab35a2d17cb
411 user: test
411 user: test
412 date: Thu Jan 01 00:00:00 1970 +0000
412 date: Thu Jan 01 00:00:00 1970 +0000
413 summary: a
413 summary: a
414
414
415
415
416 2 different branches: 2 strips
416 2 different branches: 2 strips
417
417
418 $ hg strip 2 4
418 $ hg strip 2 4
419 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
419 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
420 $ hg log -G
420 $ hg log -G
421 o changeset: 2:65bd5f99a4a3
421 o changeset: 2:65bd5f99a4a3
422 | tag: tip
422 | tag: tip
423 | user: test
423 | user: test
424 | date: Thu Jan 01 00:00:00 1970 +0000
424 | date: Thu Jan 01 00:00:00 1970 +0000
425 | summary: d
425 | summary: d
426 |
426 |
427 @ changeset: 1:ef3a871183d7
427 @ changeset: 1:ef3a871183d7
428 | user: test
428 | user: test
429 | date: Thu Jan 01 00:00:00 1970 +0000
429 | date: Thu Jan 01 00:00:00 1970 +0000
430 | summary: b
430 | summary: b
431 |
431 |
432 o changeset: 0:9ab35a2d17cb
432 o changeset: 0:9ab35a2d17cb
433 user: test
433 user: test
434 date: Thu Jan 01 00:00:00 1970 +0000
434 date: Thu Jan 01 00:00:00 1970 +0000
435 summary: a
435 summary: a
436
436
437 $ restore
437 $ restore
438
438
439 2 different branches and a common ancestor: 1 strip
439 2 different branches and a common ancestor: 1 strip
440
440
441 $ hg strip 1 "2|4"
441 $ hg strip 1 "2|4"
442 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
443 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
444 $ restore
444 $ restore
445
445
446 verify fncache is kept up-to-date
446 verify fncache is kept up-to-date
447
447
448 $ touch a
448 $ touch a
449 $ hg ci -qAm a
449 $ hg ci -qAm a
450 $ cat .hg/store/fncache | sort
450 $ cat .hg/store/fncache | sort
451 data/a.i
451 data/a.i
452 data/bar.i
452 data/bar.i
453 $ hg strip tip
453 $ hg strip tip
454 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
454 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
455 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
455 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
456 $ cat .hg/store/fncache
456 $ cat .hg/store/fncache
457 data/bar.i
457 data/bar.i
458
458
459 stripping an empty revset
459 stripping an empty revset
460
460
461 $ hg strip "1 and not 1"
461 $ hg strip "1 and not 1"
462 abort: empty revision set
462 abort: empty revision set
463 [255]
463 [255]
464
464
465 remove branchy history for qimport tests
465 remove branchy history for qimport tests
466
466
467 $ hg strip 3
467 $ hg strip 3
468 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
468 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
469
469
470
470
471 strip of applied mq should cleanup status file
471 strip of applied mq should cleanup status file
472
472
473 $ echo "mq=" >> $HGRCPATH
473 $ echo "mq=" >> $HGRCPATH
474 $ hg up -C 3
474 $ hg up -C 3
475 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
475 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
476 $ echo fooagain >> bar
476 $ echo fooagain >> bar
477 $ hg ci -mf
477 $ hg ci -mf
478 $ hg qimport -r tip:2
478 $ hg qimport -r tip:2
479
479
480 applied patches before strip
480 applied patches before strip
481
481
482 $ hg qapplied
482 $ hg qapplied
483 d
483 d
484 e
484 e
485 f
485 f
486
486
487 stripping revision in queue
487 stripping revision in queue
488
488
489 $ hg strip 3
489 $ hg strip 3
490 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
490 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
491 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
491 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
492
492
493 applied patches after stripping rev in queue
493 applied patches after stripping rev in queue
494
494
495 $ hg qapplied
495 $ hg qapplied
496 d
496 d
497
497
498 stripping ancestor of queue
498 stripping ancestor of queue
499
499
500 $ hg strip 1
500 $ hg strip 1
501 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
501 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
502 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
502 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
503
503
504 applied patches after stripping ancestor of queue
504 applied patches after stripping ancestor of queue
505
505
506 $ hg qapplied
506 $ hg qapplied
507
507
508 Verify strip protects against stripping wc parent when there are uncommitted mods
508 Verify strip protects against stripping wc parent when there are uncommitted mods
509
509
510 $ echo b > b
510 $ echo b > b
511 $ echo bb > bar
511 $ echo bb > bar
512 $ hg add b
512 $ hg add b
513 $ hg ci -m 'b'
513 $ hg ci -m 'b'
514 $ hg log --graph
514 $ hg log --graph
515 @ changeset: 1:76dcf9fab855
515 @ changeset: 1:76dcf9fab855
516 | tag: tip
516 | tag: tip
517 | user: test
517 | user: test
518 | date: Thu Jan 01 00:00:00 1970 +0000
518 | date: Thu Jan 01 00:00:00 1970 +0000
519 | summary: b
519 | summary: b
520 |
520 |
521 o changeset: 0:9ab35a2d17cb
521 o changeset: 0:9ab35a2d17cb
522 user: test
522 user: test
523 date: Thu Jan 01 00:00:00 1970 +0000
523 date: Thu Jan 01 00:00:00 1970 +0000
524 summary: a
524 summary: a
525
525
526 $ hg up 0
526 $ hg up 0
527 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
527 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
528 $ echo c > bar
528 $ echo c > bar
529 $ hg up -t false
529 $ hg up -t false
530 merging bar
530 merging bar
531 merging bar failed!
531 merging bar failed!
532 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
532 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
533 use 'hg resolve' to retry unresolved file merges
533 use 'hg resolve' to retry unresolved file merges
534 [1]
534 [1]
535 $ hg sum
535 $ hg sum
536 parent: 1:76dcf9fab855 tip
536 parent: 1:76dcf9fab855 tip
537 b
537 b
538 branch: default
538 branch: default
539 commit: 1 modified, 1 unknown, 1 unresolved
539 commit: 1 modified, 1 unknown, 1 unresolved
540 update: (current)
540 update: (current)
541 phases: 2 draft
541 phases: 2 draft
542 mq: 3 unapplied
542 mq: 3 unapplied
543
543
544 $ echo c > b
544 $ echo c > b
545 $ hg strip tip
545 $ hg strip tip
546 abort: local changes found
546 abort: local changes found
547 [255]
547 [255]
548 $ hg strip tip --keep
548 $ hg strip tip --keep
549 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
549 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
550 $ hg log --graph
550 $ hg log --graph
551 @ changeset: 0:9ab35a2d17cb
551 @ changeset: 0:9ab35a2d17cb
552 tag: tip
552 tag: tip
553 user: test
553 user: test
554 date: Thu Jan 01 00:00:00 1970 +0000
554 date: Thu Jan 01 00:00:00 1970 +0000
555 summary: a
555 summary: a
556
556
557 $ hg status
557 $ hg status
558 M bar
558 M bar
559 ? b
559 ? b
560 ? bar.orig
560 ? bar.orig
561
561
562 $ rm bar.orig
562 $ rm bar.orig
563 $ hg sum
563 $ hg sum
564 parent: 0:9ab35a2d17cb tip
564 parent: 0:9ab35a2d17cb tip
565 a
565 a
566 branch: default
566 branch: default
567 commit: 1 modified, 1 unknown
567 commit: 1 modified, 1 unknown
568 update: (current)
568 update: (current)
569 phases: 1 draft
569 phases: 1 draft
570 mq: 3 unapplied
570 mq: 3 unapplied
571
571
572 Strip adds, removes, modifies with --keep
572 Strip adds, removes, modifies with --keep
573
573
574 $ touch b
574 $ touch b
575 $ hg add b
575 $ hg add b
576 $ hg commit -mb
576 $ hg commit -mb
577 $ touch c
577 $ touch c
578
578
579 ... with a clean working dir
579 ... with a clean working dir
580
580
581 $ hg add c
581 $ hg add c
582 $ hg rm bar
582 $ hg rm bar
583 $ hg commit -mc
583 $ hg commit -mc
584 $ hg status
584 $ hg status
585 $ hg strip --keep tip
585 $ hg strip --keep tip
586 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
586 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
587 $ hg status
587 $ hg status
588 ! bar
588 ! bar
589 ? c
589 ? c
590
590
591 ... with a dirty working dir
591 ... with a dirty working dir
592
592
593 $ hg add c
593 $ hg add c
594 $ hg rm bar
594 $ hg rm bar
595 $ hg commit -mc
595 $ hg commit -mc
596 $ hg status
596 $ hg status
597 $ echo b > b
597 $ echo b > b
598 $ echo d > d
598 $ echo d > d
599 $ hg strip --keep tip
599 $ hg strip --keep tip
600 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
600 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
601 $ hg status
601 $ hg status
602 M b
602 M b
603 ! bar
603 ! bar
604 ? c
604 ? c
605 ? d
605 ? d
606
606
607 ... after updating the dirstate
607 ... after updating the dirstate
608 $ hg add c
608 $ hg add c
609 $ hg commit -mc
609 $ hg commit -mc
610 $ hg rm c
610 $ hg rm c
611 $ hg commit -mc
611 $ hg commit -mc
612 $ hg strip --keep '.^' -q
612 $ hg strip --keep '.^' -q
613 $ cd ..
613 $ cd ..
614
614
615 stripping many nodes on a complex graph (issue3299)
615 stripping many nodes on a complex graph (issue3299)
616
616
617 $ hg init issue3299
617 $ hg init issue3299
618 $ cd issue3299
618 $ cd issue3299
619 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
619 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
620 $ hg strip 'not ancestors(x)'
620 $ hg strip 'not ancestors(x)'
621 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
621 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
622
622
623 test hg strip -B bookmark
623 test hg strip -B bookmark
624
624
625 $ cd ..
625 $ cd ..
626 $ hg init bookmarks
626 $ hg init bookmarks
627 $ cd bookmarks
627 $ cd bookmarks
628 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b<m+2:d<2.:e<m+1:f'
628 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b<m+2:d<2.:e<m+1:f'
629 $ hg bookmark -r 'a' 'todelete'
629 $ hg bookmark -r 'a' 'todelete'
630 $ hg bookmark -r 'b' 'B'
630 $ hg bookmark -r 'b' 'B'
631 $ hg bookmark -r 'b' 'nostrip'
631 $ hg bookmark -r 'b' 'nostrip'
632 $ hg bookmark -r 'c' 'delete'
632 $ hg bookmark -r 'c' 'delete'
633 $ hg bookmark -r 'd' 'multipledelete1'
633 $ hg bookmark -r 'd' 'multipledelete1'
634 $ hg bookmark -r 'e' 'multipledelete2'
634 $ hg bookmark -r 'e' 'multipledelete2'
635 $ hg bookmark -r 'f' 'singlenode1'
635 $ hg bookmark -r 'f' 'singlenode1'
636 $ hg bookmark -r 'f' 'singlenode2'
636 $ hg bookmark -r 'f' 'singlenode2'
637 $ hg up -C todelete
637 $ hg up -C todelete
638 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
638 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
639 (activating bookmark todelete)
639 (activating bookmark todelete)
640 $ hg strip -B nostrip
640 $ hg strip -B nostrip
641 bookmark 'nostrip' deleted
641 bookmark 'nostrip' deleted
642 abort: empty revision set
642 abort: empty revision set
643 [255]
643 [255]
644 $ hg strip -B todelete
644 $ hg strip -B todelete
645 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
645 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
646 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
646 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
647 bookmark 'todelete' deleted
647 bookmark 'todelete' deleted
648 $ hg id -ir dcbb326fdec2
648 $ hg id -ir dcbb326fdec2
649 abort: unknown revision 'dcbb326fdec2'!
649 abort: unknown revision 'dcbb326fdec2'!
650 [255]
650 [255]
651 $ hg id -ir d62d843c9a01
651 $ hg id -ir d62d843c9a01
652 d62d843c9a01
652 d62d843c9a01
653 $ hg bookmarks
653 $ hg bookmarks
654 B 9:ff43616e5d0f
654 B 9:ff43616e5d0f
655 delete 6:2702dd0c91e7
655 delete 6:2702dd0c91e7
656 multipledelete1 11:e46a4836065c
656 multipledelete1 11:e46a4836065c
657 multipledelete2 12:b4594d867745
657 multipledelete2 12:b4594d867745
658 singlenode1 13:43227190fef8
658 singlenode1 13:43227190fef8
659 singlenode2 13:43227190fef8
659 singlenode2 13:43227190fef8
660 $ hg strip -B multipledelete1 -B multipledelete2
660 $ hg strip -B multipledelete1 -B multipledelete2
661 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/e46a4836065c-89ec65c2-backup.hg (glob)
661 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/e46a4836065c-89ec65c2-backup.hg (glob)
662 bookmark 'multipledelete1' deleted
662 bookmark 'multipledelete1' deleted
663 bookmark 'multipledelete2' deleted
663 bookmark 'multipledelete2' deleted
664 $ hg id -ir e46a4836065c
664 $ hg id -ir e46a4836065c
665 abort: unknown revision 'e46a4836065c'!
665 abort: unknown revision 'e46a4836065c'!
666 [255]
666 [255]
667 $ hg id -ir b4594d867745
667 $ hg id -ir b4594d867745
668 abort: unknown revision 'b4594d867745'!
668 abort: unknown revision 'b4594d867745'!
669 [255]
669 [255]
670 $ hg strip -B singlenode1 -B singlenode2
670 $ hg strip -B singlenode1 -B singlenode2
671 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/43227190fef8-8da858f2-backup.hg (glob)
671 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/43227190fef8-8da858f2-backup.hg (glob)
672 bookmark 'singlenode1' deleted
672 bookmark 'singlenode1' deleted
673 bookmark 'singlenode2' deleted
673 bookmark 'singlenode2' deleted
674 $ hg id -ir 43227190fef8
674 $ hg id -ir 43227190fef8
675 abort: unknown revision '43227190fef8'!
675 abort: unknown revision '43227190fef8'!
676 [255]
676 [255]
677 $ hg strip -B unknownbookmark
677 $ hg strip -B unknownbookmark
678 abort: bookmark 'unknownbookmark' not found
678 abort: bookmark 'unknownbookmark' not found
679 [255]
679 [255]
680 $ hg strip -B unknownbookmark1 -B unknownbookmark2
680 $ hg strip -B unknownbookmark1 -B unknownbookmark2
681 abort: bookmark 'unknownbookmark1,unknownbookmark2' not found
681 abort: bookmark 'unknownbookmark1,unknownbookmark2' not found
682 [255]
682 [255]
683 $ hg strip -B delete -B unknownbookmark
683 $ hg strip -B delete -B unknownbookmark
684 abort: bookmark 'unknownbookmark' not found
684 abort: bookmark 'unknownbookmark' not found
685 [255]
685 [255]
686 $ hg strip -B delete
686 $ hg strip -B delete
687 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
687 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
688 bookmark 'delete' deleted
688 bookmark 'delete' deleted
689 $ hg id -ir 6:2702dd0c91e7
689 $ hg id -ir 6:2702dd0c91e7
690 abort: unknown revision '2702dd0c91e7'!
690 abort: unknown revision '2702dd0c91e7'!
691 [255]
691 [255]
692 $ hg update B
692 $ hg update B
693 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
693 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
694 (activating bookmark B)
694 (activating bookmark B)
695 $ echo a > a
695 $ echo a > a
696 $ hg add a
696 $ hg add a
697 $ hg strip -B B
697 $ hg strip -B B
698 abort: local changes found
698 abort: local changes found
699 [255]
699 [255]
700 $ hg bookmarks
700 $ hg bookmarks
701 * B 6:ff43616e5d0f
701 * B 6:ff43616e5d0f
702
702
703 Make sure no one adds back a -b option:
703 Make sure no one adds back a -b option:
704
704
705 $ hg strip -b tip
705 $ hg strip -b tip
706 hg strip: option -b not recognized
706 hg strip: option -b not recognized
707 hg strip [-k] [-f] [-B bookmark] [-r] REV...
707 hg strip [-k] [-f] [-B bookmark] [-r] REV...
708
708
709 strip changesets and all their descendants from the repository
709 strip changesets and all their descendants from the repository
710
710
711 (use 'hg help -e strip' to show help for the strip extension)
711 (use 'hg help -e strip' to show help for the strip extension)
712
712
713 options ([+] can be repeated):
713 options ([+] can be repeated):
714
714
715 -r --rev REV [+] strip specified revision (optional, can specify
715 -r --rev REV [+] strip specified revision (optional, can specify
716 revisions without this option)
716 revisions without this option)
717 -f --force force removal of changesets, discard uncommitted
717 -f --force force removal of changesets, discard uncommitted
718 changes (no backup)
718 changes (no backup)
719 --no-backup no backups
719 --no-backup no backups
720 -k --keep do not modify working directory during strip
720 -k --keep do not modify working directory during strip
721 -B --bookmark VALUE [+] remove revs only reachable from given bookmark
721 -B --bookmark VALUE [+] remove revs only reachable from given bookmark
722 --mq operate on patch repository
722 --mq operate on patch repository
723
723
724 (use 'hg strip -h' to show more help)
724 (use 'hg strip -h' to show more help)
725 [255]
725 [255]
726
726
727 $ cd ..
727 $ cd ..
728
728
729 Verify bundles don't get overwritten:
729 Verify bundles don't get overwritten:
730
730
731 $ hg init doublebundle
731 $ hg init doublebundle
732 $ cd doublebundle
732 $ cd doublebundle
733 $ touch a
733 $ touch a
734 $ hg commit -Aqm a
734 $ hg commit -Aqm a
735 $ touch b
735 $ touch b
736 $ hg commit -Aqm b
736 $ hg commit -Aqm b
737 $ hg strip -r 0
737 $ hg strip -r 0
738 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
738 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
739 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg (glob)
739 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg (glob)
740 $ ls .hg/strip-backup
740 $ ls .hg/strip-backup
741 3903775176ed-e68910bd-backup.hg
741 3903775176ed-e68910bd-backup.hg
742 $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg
742 $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg
743 $ hg strip -r 0
743 $ hg strip -r 0
744 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg (glob)
744 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg (glob)
745 $ ls .hg/strip-backup
745 $ ls .hg/strip-backup
746 3903775176ed-54390173-backup.hg
746 3903775176ed-54390173-backup.hg
747 3903775176ed-e68910bd-backup.hg
747 3903775176ed-e68910bd-backup.hg
748 $ cd ..
748 $ cd ..
749
749
750 Test that we only bundle the stripped changesets (issue4736)
750 Test that we only bundle the stripped changesets (issue4736)
751 ------------------------------------------------------------
751 ------------------------------------------------------------
752
752
753 initialization (previous repo is empty anyway)
753 initialization (previous repo is empty anyway)
754
754
755 $ hg init issue4736
755 $ hg init issue4736
756 $ cd issue4736
756 $ cd issue4736
757 $ echo a > a
757 $ echo a > a
758 $ hg add a
758 $ hg add a
759 $ hg commit -m commitA
759 $ hg commit -m commitA
760 $ echo b > b
760 $ echo b > b
761 $ hg add b
761 $ hg add b
762 $ hg commit -m commitB
762 $ hg commit -m commitB
763 $ echo c > c
763 $ echo c > c
764 $ hg add c
764 $ hg add c
765 $ hg commit -m commitC
765 $ hg commit -m commitC
766 $ hg up 'desc(commitB)'
766 $ hg up 'desc(commitB)'
767 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
767 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
768 $ echo d > d
768 $ echo d > d
769 $ hg add d
769 $ hg add d
770 $ hg commit -m commitD
770 $ hg commit -m commitD
771 created new head
771 created new head
772 $ hg up 'desc(commitC)'
772 $ hg up 'desc(commitC)'
773 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
773 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
774 $ hg merge 'desc(commitD)'
774 $ hg merge 'desc(commitD)'
775 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
775 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
776 (branch merge, don't forget to commit)
776 (branch merge, don't forget to commit)
777 $ hg ci -m 'mergeCD'
777 $ hg ci -m 'mergeCD'
778 $ hg log -G
778 $ hg log -G
779 @ changeset: 4:d8db9d137221
779 @ changeset: 4:d8db9d137221
780 |\ tag: tip
780 |\ tag: tip
781 | | parent: 2:5c51d8d6557d
781 | | parent: 2:5c51d8d6557d
782 | | parent: 3:6625a5168474
782 | | parent: 3:6625a5168474
783 | | user: test
783 | | user: test
784 | | date: Thu Jan 01 00:00:00 1970 +0000
784 | | date: Thu Jan 01 00:00:00 1970 +0000
785 | | summary: mergeCD
785 | | summary: mergeCD
786 | |
786 | |
787 | o changeset: 3:6625a5168474
787 | o changeset: 3:6625a5168474
788 | | parent: 1:eca11cf91c71
788 | | parent: 1:eca11cf91c71
789 | | user: test
789 | | user: test
790 | | date: Thu Jan 01 00:00:00 1970 +0000
790 | | date: Thu Jan 01 00:00:00 1970 +0000
791 | | summary: commitD
791 | | summary: commitD
792 | |
792 | |
793 o | changeset: 2:5c51d8d6557d
793 o | changeset: 2:5c51d8d6557d
794 |/ user: test
794 |/ user: test
795 | date: Thu Jan 01 00:00:00 1970 +0000
795 | date: Thu Jan 01 00:00:00 1970 +0000
796 | summary: commitC
796 | summary: commitC
797 |
797 |
798 o changeset: 1:eca11cf91c71
798 o changeset: 1:eca11cf91c71
799 | user: test
799 | user: test
800 | date: Thu Jan 01 00:00:00 1970 +0000
800 | date: Thu Jan 01 00:00:00 1970 +0000
801 | summary: commitB
801 | summary: commitB
802 |
802 |
803 o changeset: 0:105141ef12d0
803 o changeset: 0:105141ef12d0
804 user: test
804 user: test
805 date: Thu Jan 01 00:00:00 1970 +0000
805 date: Thu Jan 01 00:00:00 1970 +0000
806 summary: commitA
806 summary: commitA
807
807
808
808
809 Check bundle behavior:
809 Check bundle behavior:
810
810
811 $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg
811 $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg
812 2 changesets found
812 2 changesets found
813 $ hg log -r 'bundle()' -R ../issue4736.hg
813 $ hg log -r 'bundle()' -R ../issue4736.hg
814 changeset: 3:6625a5168474
814 changeset: 3:6625a5168474
815 parent: 1:eca11cf91c71
815 parent: 1:eca11cf91c71
816 user: test
816 user: test
817 date: Thu Jan 01 00:00:00 1970 +0000
817 date: Thu Jan 01 00:00:00 1970 +0000
818 summary: commitD
818 summary: commitD
819
819
820 changeset: 4:d8db9d137221
820 changeset: 4:d8db9d137221
821 tag: tip
821 tag: tip
822 parent: 2:5c51d8d6557d
822 parent: 2:5c51d8d6557d
823 parent: 3:6625a5168474
823 parent: 3:6625a5168474
824 user: test
824 user: test
825 date: Thu Jan 01 00:00:00 1970 +0000
825 date: Thu Jan 01 00:00:00 1970 +0000
826 summary: mergeCD
826 summary: mergeCD
827
827
828
828
829 check strip behavior
829 check strip behavior
830
830
831 $ hg --config extensions.strip= strip 'desc(commitD)' --debug
831 $ hg --config extensions.strip= strip 'desc(commitD)' --debug
832 resolving manifests
832 resolving manifests
833 branchmerge: False, force: True, partial: False
833 branchmerge: False, force: True, partial: False
834 ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
834 ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
835 c: other deleted -> r
835 c: other deleted -> r
836 removing c
836 removing c
837 d: other deleted -> r
837 d: other deleted -> r
838 removing d
838 removing d
839 starting 4 threads for background file closing (?)
839 starting 4 threads for background file closing (?)
840 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
840 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
841 2 changesets found
841 2 changesets found
842 list of changesets:
842 list of changesets:
843 6625a516847449b6f0fa3737b9ba56e9f0f3032c
843 6625a516847449b6f0fa3737b9ba56e9f0f3032c
844 d8db9d1372214336d2b5570f20ee468d2c72fa8b
844 d8db9d1372214336d2b5570f20ee468d2c72fa8b
845 bundle2-output-bundle: "HG20", (1 params) 2 parts total
845 bundle2-output-bundle: "HG20", (1 params) 2 parts total
846 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
846 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
847 bundle2-output-part: "phase-heads" 24 bytes payload
847 bundle2-output-part: "phase-heads" 24 bytes payload
848 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob)
848 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob)
849 updating the branch cache
849 updating the branch cache
850 invalid branchheads cache (served): tip differs
850 invalid branchheads cache (served): tip differs
851 truncating cache/rbc-revs-v1 to 24
851 truncating cache/rbc-revs-v1 to 24
852 $ hg log -G
852 $ hg log -G
853 o changeset: 2:5c51d8d6557d
853 o changeset: 2:5c51d8d6557d
854 | tag: tip
854 | tag: tip
855 | user: test
855 | user: test
856 | date: Thu Jan 01 00:00:00 1970 +0000
856 | date: Thu Jan 01 00:00:00 1970 +0000
857 | summary: commitC
857 | summary: commitC
858 |
858 |
859 @ changeset: 1:eca11cf91c71
859 @ changeset: 1:eca11cf91c71
860 | user: test
860 | user: test
861 | date: Thu Jan 01 00:00:00 1970 +0000
861 | date: Thu Jan 01 00:00:00 1970 +0000
862 | summary: commitB
862 | summary: commitB
863 |
863 |
864 o changeset: 0:105141ef12d0
864 o changeset: 0:105141ef12d0
865 user: test
865 user: test
866 date: Thu Jan 01 00:00:00 1970 +0000
866 date: Thu Jan 01 00:00:00 1970 +0000
867 summary: commitA
867 summary: commitA
868
868
869
869
870 strip backup content
870 strip backup content
871
871
872 $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg
872 $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg
873 changeset: 3:6625a5168474
873 changeset: 3:6625a5168474
874 parent: 1:eca11cf91c71
874 parent: 1:eca11cf91c71
875 user: test
875 user: test
876 date: Thu Jan 01 00:00:00 1970 +0000
876 date: Thu Jan 01 00:00:00 1970 +0000
877 summary: commitD
877 summary: commitD
878
878
879 changeset: 4:d8db9d137221
879 changeset: 4:d8db9d137221
880 tag: tip
880 tag: tip
881 parent: 2:5c51d8d6557d
881 parent: 2:5c51d8d6557d
882 parent: 3:6625a5168474
882 parent: 3:6625a5168474
883 user: test
883 user: test
884 date: Thu Jan 01 00:00:00 1970 +0000
884 date: Thu Jan 01 00:00:00 1970 +0000
885 summary: mergeCD
885 summary: mergeCD
886
886
887 Check that the phase cache is properly invalidated after a strip with bookmark.
887 Check that the phase cache is properly invalidated after a strip with bookmark.
888
888
889 $ cat > ../stripstalephasecache.py << EOF
889 $ cat > ../stripstalephasecache.py << EOF
890 > from mercurial import extensions, localrepo
890 > from mercurial import extensions, localrepo
891 > def transactioncallback(orig, repo, desc, *args, **kwargs):
891 > def transactioncallback(orig, repo, desc, *args, **kwargs):
892 > def test(transaction):
892 > def test(transaction):
893 > # observe cache inconsistency
893 > # observe cache inconsistency
894 > try:
894 > try:
895 > [repo.changelog.node(r) for r in repo.revs("not public()")]
895 > [repo.changelog.node(r) for r in repo.revs("not public()")]
896 > except IndexError:
896 > except IndexError:
897 > repo.ui.status("Index error!\n")
897 > repo.ui.status("Index error!\n")
898 > transaction = orig(repo, desc, *args, **kwargs)
898 > transaction = orig(repo, desc, *args, **kwargs)
899 > # warm up the phase cache
899 > # warm up the phase cache
900 > list(repo.revs("not public()"))
900 > list(repo.revs("not public()"))
901 > if desc != 'strip':
901 > if desc != 'strip':
902 > transaction.addpostclose("phase invalidation test", test)
902 > transaction.addpostclose("phase invalidation test", test)
903 > return transaction
903 > return transaction
904 > def extsetup(ui):
904 > def extsetup(ui):
905 > extensions.wrapfunction(localrepo.localrepository, "transaction",
905 > extensions.wrapfunction(localrepo.localrepository, "transaction",
906 > transactioncallback)
906 > transactioncallback)
907 > EOF
907 > EOF
908 $ hg up -C 2
908 $ hg up -C 2
909 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
909 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
910 $ echo k > k
910 $ echo k > k
911 $ hg add k
911 $ hg add k
912 $ hg commit -m commitK
912 $ hg commit -m commitK
913 $ echo l > l
913 $ echo l > l
914 $ hg add l
914 $ hg add l
915 $ hg commit -m commitL
915 $ hg commit -m commitL
916 $ hg book -r tip blah
916 $ hg book -r tip blah
917 $ hg strip ".^" --config extensions.crash=$TESTTMP/stripstalephasecache.py
917 $ hg strip ".^" --config extensions.crash=$TESTTMP/stripstalephasecache.py
918 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
918 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
919 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/8f0b4384875c-4fa10deb-backup.hg (glob)
919 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/8f0b4384875c-4fa10deb-backup.hg (glob)
920 $ hg up -C 1
920 $ hg up -C 1
921 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
921 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
922
922
923 Error during post-close callback of the strip transaction
923 Error during post-close callback of the strip transaction
924 (They should be gracefully handled and reported)
924 (They should be gracefully handled and reported)
925
925
926 $ cat > ../crashstrip.py << EOF
926 $ cat > ../crashstrip.py << EOF
927 > from mercurial import error
927 > from mercurial import error
928 > def reposetup(ui, repo):
928 > def reposetup(ui, repo):
929 > class crashstriprepo(repo.__class__):
929 > class crashstriprepo(repo.__class__):
930 > def transaction(self, desc, *args, **kwargs):
930 > def transaction(self, desc, *args, **kwargs):
931 > tr = super(crashstriprepo, self).transaction(desc, *args, **kwargs)
931 > tr = super(crashstriprepo, self).transaction(desc, *args, **kwargs)
932 > if desc == 'strip':
932 > if desc == 'strip':
933 > def crash(tra): raise error.Abort('boom')
933 > def crash(tra): raise error.Abort('boom')
934 > tr.addpostclose('crash', crash)
934 > tr.addpostclose('crash', crash)
935 > return tr
935 > return tr
936 > repo.__class__ = crashstriprepo
936 > repo.__class__ = crashstriprepo
937 > EOF
937 > EOF
938 $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py
938 $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py
939 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg (glob)
939 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg (glob)
940 strip failed, backup bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' (glob)
940 strip failed, backup bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' (glob)
941 abort: boom
941 abort: boom
942 [255]
942 [255]
943
943
944 Use delayedstrip to strip inside a transaction
944 Use delayedstrip to strip inside a transaction
945
945
946 $ cd $TESTTMP
946 $ cd $TESTTMP
947 $ hg init delayedstrip
947 $ hg init delayedstrip
948 $ cd delayedstrip
948 $ cd delayedstrip
949 $ hg debugdrawdag <<'EOS'
949 $ hg debugdrawdag <<'EOS'
950 > D
950 > D
951 > |
951 > |
952 > C F H # Commit on top of "I",
952 > C F H # Commit on top of "I",
953 > | |/| # Strip B+D+I+E+G+H+Z
953 > | |/| # Strip B+D+I+E+G+H+Z
954 > I B E G
954 > I B E G
955 > \|/
955 > \|/
956 > A Z
956 > A Z
957 > EOS
957 > EOS
958 $ cp -R . ../scmutilcleanup
958 $ cp -R . ../scmutilcleanup
959
959
960 $ hg up -C I
960 $ hg up -C I
961 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
961 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
962 $ echo 3 >> I
962 $ echo 3 >> I
963 $ cat > $TESTTMP/delayedstrip.py <<EOF
963 $ cat > $TESTTMP/delayedstrip.py <<EOF
964 > from mercurial import repair, commands
964 > from mercurial import repair, commands
965 > def reposetup(ui, repo):
965 > def reposetup(ui, repo):
966 > def getnodes(expr):
966 > def getnodes(expr):
967 > return [repo.changelog.node(r) for r in repo.revs(expr)]
967 > return [repo.changelog.node(r) for r in repo.revs(expr)]
968 > with repo.wlock():
968 > with repo.wlock():
969 > with repo.lock():
969 > with repo.lock():
970 > with repo.transaction('delayedstrip'):
970 > with repo.transaction('delayedstrip'):
971 > repair.delayedstrip(ui, repo, getnodes('B+I+Z+D+E'), 'J')
971 > repair.delayedstrip(ui, repo, getnodes('B+I+Z+D+E'), 'J')
972 > repair.delayedstrip(ui, repo, getnodes('G+H+Z'), 'I')
972 > repair.delayedstrip(ui, repo, getnodes('G+H+Z'), 'I')
973 > commands.commit(ui, repo, message='J', date='0 0')
973 > commands.commit(ui, repo, message='J', date='0 0')
974 > EOF
974 > EOF
975 $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/delayedstrip.py
975 $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/delayedstrip.py
976 warning: orphaned descendants detected, not stripping 08ebfeb61bac, 112478962961, 7fb047a69f22
976 warning: orphaned descendants detected, not stripping 08ebfeb61bac, 112478962961, 7fb047a69f22
977 saved backup bundle to $TESTTMP/delayedstrip/.hg/strip-backup/f585351a92f8-17475721-I.hg (glob)
977 saved backup bundle to $TESTTMP/delayedstrip/.hg/strip-backup/f585351a92f8-17475721-I.hg (glob)
978
978
979 $ hg log -G -T '{rev}:{node|short} {desc}' -r 'sort(all(), topo)'
979 $ hg log -G -T '{rev}:{node|short} {desc}' -r 'sort(all(), topo)'
980 @ 6:2f2d51af6205 J
980 @ 6:2f2d51af6205 J
981 |
981 |
982 o 3:08ebfeb61bac I
982 o 3:08ebfeb61bac I
983 |
983 |
984 | o 5:64a8289d2492 F
984 | o 5:64a8289d2492 F
985 | |
985 | |
986 | o 2:7fb047a69f22 E
986 | o 2:7fb047a69f22 E
987 |/
987 |/
988 | o 4:26805aba1e60 C
988 | o 4:26805aba1e60 C
989 | |
989 | |
990 | o 1:112478962961 B
990 | o 1:112478962961 B
991 |/
991 |/
992 o 0:426bada5c675 A
992 o 0:426bada5c675 A
993
993
994 Test high-level scmutil.cleanupnodes API
994 Test high-level scmutil.cleanupnodes API
995
995
996 $ cd $TESTTMP/scmutilcleanup
996 $ cd $TESTTMP/scmutilcleanup
997 $ hg debugdrawdag <<'EOS'
997 $ hg debugdrawdag <<'EOS'
998 > D2 F2 G2 # D2, F2, G2 are replacements for D, F, G
998 > D2 F2 G2 # D2, F2, G2 are replacements for D, F, G
999 > | | |
999 > | | |
1000 > C H G
1000 > C H G
1001 > EOS
1001 > EOS
1002 $ for i in B C D F G I Z; do
1002 $ for i in B C D F G I Z; do
1003 > hg bookmark -i -r $i b-$i
1003 > hg bookmark -i -r $i b-$i
1004 > done
1004 > done
1005 $ hg bookmark -i -r E 'b-F@divergent1'
1005 $ hg bookmark -i -r E 'b-F@divergent1'
1006 $ hg bookmark -i -r H 'b-F@divergent2'
1006 $ hg bookmark -i -r H 'b-F@divergent2'
1007 $ hg bookmark -i -r G 'b-F@divergent3'
1007 $ hg bookmark -i -r G 'b-F@divergent3'
1008 $ cp -R . ../scmutilcleanup.obsstore
1008 $ cp -R . ../scmutilcleanup.obsstore
1009
1009
1010 $ cat > $TESTTMP/scmutilcleanup.py <<EOF
1010 $ cat > $TESTTMP/scmutilcleanup.py <<EOF
1011 > from mercurial import scmutil
1011 > from mercurial import scmutil
1012 > def reposetup(ui, repo):
1012 > def reposetup(ui, repo):
1013 > def nodes(expr):
1013 > def nodes(expr):
1014 > return [repo.changelog.node(r) for r in repo.revs(expr)]
1014 > return [repo.changelog.node(r) for r in repo.revs(expr)]
1015 > def node(expr):
1015 > def node(expr):
1016 > return nodes(expr)[0]
1016 > return nodes(expr)[0]
1017 > with repo.wlock():
1017 > with repo.wlock():
1018 > with repo.lock():
1018 > with repo.lock():
1019 > with repo.transaction('delayedstrip'):
1019 > with repo.transaction('delayedstrip'):
1020 > mapping = {node('F'): [node('F2')],
1020 > mapping = {node('F'): [node('F2')],
1021 > node('D'): [node('D2')],
1021 > node('D'): [node('D2')],
1022 > node('G'): [node('G2')]}
1022 > node('G'): [node('G2')]}
1023 > scmutil.cleanupnodes(repo, mapping, 'replace')
1023 > scmutil.cleanupnodes(repo, mapping, 'replace')
1024 > scmutil.cleanupnodes(repo, nodes('((B::)+I+Z)-D2'), 'replace')
1024 > scmutil.cleanupnodes(repo, nodes('((B::)+I+Z)-D2'), 'replace')
1025 > EOF
1025 > EOF
1026 $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/scmutilcleanup.py
1026 $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/scmutilcleanup.py
1027 warning: orphaned descendants detected, not stripping 112478962961, 1fc8102cda62, 26805aba1e60
1027 warning: orphaned descendants detected, not stripping 112478962961, 1fc8102cda62, 26805aba1e60
1028 saved backup bundle to $TESTTMP/scmutilcleanup/.hg/strip-backup/f585351a92f8-73fb7c03-replace.hg (glob)
1028 saved backup bundle to $TESTTMP/scmutilcleanup/.hg/strip-backup/f585351a92f8-73fb7c03-replace.hg (glob)
1029
1029
1030 $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)'
1030 $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)'
1031 o 8:1473d4b996d1 G2 b-F@divergent3 b-G
1031 o 8:1473d4b996d1 G2 b-F@divergent3 b-G
1032 |
1032 |
1033 | o 7:d11b3456a873 F2 b-F
1033 | o 7:d11b3456a873 F2 b-F
1034 | |
1034 | |
1035 | o 5:5cb05ba470a7 H
1035 | o 5:5cb05ba470a7 H
1036 |/|
1036 |/|
1037 | o 3:7fb047a69f22 E b-F@divergent1
1037 | o 3:7fb047a69f22 E b-F@divergent1
1038 | |
1038 | |
1039 | | o 6:7c78f703e465 D2 b-D
1039 | | o 6:7c78f703e465 D2 b-D
1040 | | |
1040 | | |
1041 | | o 4:26805aba1e60 C
1041 | | o 4:26805aba1e60 C
1042 | | |
1042 | | |
1043 | | o 2:112478962961 B
1043 | | o 2:112478962961 B
1044 | |/
1044 | |/
1045 o | 1:1fc8102cda62 G
1045 o | 1:1fc8102cda62 G
1046 /
1046 /
1047 o 0:426bada5c675 A b-B b-C b-I
1047 o 0:426bada5c675 A b-B b-C b-I
1048
1048
1049 $ hg bookmark
1049 $ hg bookmark
1050 b-B 0:426bada5c675
1050 b-B 0:426bada5c675
1051 b-C 0:426bada5c675
1051 b-C 0:426bada5c675
1052 b-D 6:7c78f703e465
1052 b-D 6:7c78f703e465
1053 b-F 7:d11b3456a873
1053 b-F 7:d11b3456a873
1054 b-F@divergent1 3:7fb047a69f22
1054 b-F@divergent1 3:7fb047a69f22
1055 b-F@divergent3 8:1473d4b996d1
1055 b-F@divergent3 8:1473d4b996d1
1056 b-G 8:1473d4b996d1
1056 b-G 8:1473d4b996d1
1057 b-I 0:426bada5c675
1057 b-I 0:426bada5c675
1058 b-Z -1:000000000000
1058 b-Z -1:000000000000
1059
1059
1060 Test the above using obsstore "by the way". Not directly related to strip, but
1060 Test the above using obsstore "by the way". Not directly related to strip, but
1061 we have reusable code here
1061 we have reusable code here
1062
1062
1063 $ cd $TESTTMP/scmutilcleanup.obsstore
1063 $ cd $TESTTMP/scmutilcleanup.obsstore
1064 $ cat >> .hg/hgrc <<EOF
1064 $ cat >> .hg/hgrc <<EOF
1065 > [experimental]
1065 > [experimental]
1066 > evolution=all
1066 > evolution=all
1067 > evolution.track-operation=1
1067 > evolution.track-operation=1
1068 > EOF
1068 > EOF
1069
1069
1070 $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/scmutilcleanup.py
1070 $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/scmutilcleanup.py
1071
1071
1072 $ rm .hg/localtags
1072 $ rm .hg/localtags
1073 $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)'
1073 $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)'
1074 o 12:1473d4b996d1 G2 b-F@divergent3 b-G
1074 o 12:1473d4b996d1 G2 b-F@divergent3 b-G
1075 |
1075 |
1076 | o 11:d11b3456a873 F2 b-F
1076 | o 11:d11b3456a873 F2 b-F
1077 | |
1077 | |
1078 | o 8:5cb05ba470a7 H
1078 | o 8:5cb05ba470a7 H
1079 |/|
1079 |/|
1080 | o 4:7fb047a69f22 E b-F@divergent1
1080 | o 4:7fb047a69f22 E b-F@divergent1
1081 | |
1081 | |
1082 | | o 10:7c78f703e465 D2 b-D
1082 | | o 10:7c78f703e465 D2 b-D
1083 | | |
1083 | | |
1084 | | x 6:26805aba1e60 C
1084 | | x 6:26805aba1e60 C
1085 | | |
1085 | | |
1086 | | x 3:112478962961 B
1086 | | x 3:112478962961 B
1087 | |/
1087 | |/
1088 x | 1:1fc8102cda62 G
1088 x | 1:1fc8102cda62 G
1089 /
1089 /
1090 o 0:426bada5c675 A b-B b-C b-I
1090 o 0:426bada5c675 A b-B b-C b-I
1091
1091
1092 $ hg debugobsolete
1092 $ hg debugobsolete
1093 1fc8102cda6204549f031015641606ccf5513ec3 1473d4b996d1d1b121de6b39fab6a04fbf9d873e 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1093 1fc8102cda6204549f031015641606ccf5513ec3 1473d4b996d1d1b121de6b39fab6a04fbf9d873e 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1094 64a8289d249234b9886244d379f15e6b650b28e3 d11b3456a873daec7c7bc53e5622e8df6d741bd2 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1094 64a8289d249234b9886244d379f15e6b650b28e3 d11b3456a873daec7c7bc53e5622e8df6d741bd2 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1095 f585351a92f85104bff7c284233c338b10eb1df7 7c78f703e465d73102cc8780667ce269c5208a40 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1095 f585351a92f85104bff7c284233c338b10eb1df7 7c78f703e465d73102cc8780667ce269c5208a40 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1096 48b9aae0607f43ff110d84e6883c151942add5ab 0 {0000000000000000000000000000000000000000} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1096 48b9aae0607f43ff110d84e6883c151942add5ab 0 {0000000000000000000000000000000000000000} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1097 112478962961147124edd43549aedd1a335e44bf 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1097 112478962961147124edd43549aedd1a335e44bf 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1098 08ebfeb61bac6e3f12079de774d285a0d6689eba 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1098 08ebfeb61bac6e3f12079de774d285a0d6689eba 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1099 26805aba1e600a82e93661149f2313866a221a7b 0 {112478962961147124edd43549aedd1a335e44bf} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1099 26805aba1e600a82e93661149f2313866a221a7b 0 {112478962961147124edd43549aedd1a335e44bf} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
1100 $ cd ..
1100 $ cd ..
1101
1101
1102 Test that obsmarkers are restored even when not using generaldelta
1102 Test that obsmarkers are restored even when not using generaldelta
1103
1103
1104 $ hg --config format.usegeneraldelta=no init issue5678
1104 $ hg --config format.usegeneraldelta=no init issue5678
1105 $ cd issue5678
1105 $ cd issue5678
1106 $ cat >> .hg/hgrc <<EOF
1106 $ cat >> .hg/hgrc <<EOF
1107 > [experimental]
1107 > [experimental]
1108 > evolution=all
1108 > evolution=all
1109 > EOF
1109 > EOF
1110 $ echo a > a
1110 $ echo a > a
1111 $ hg ci -Aqm a
1111 $ hg ci -Aqm a
1112 $ hg ci --amend -m a2
1112 $ hg ci --amend -m a2
1113 $ hg debugobsolete
1113 $ hg debugobsolete
1114 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1114 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1115 $ hg strip .
1115 $ hg strip .
1116 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1116 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1117 saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/489bac576828-bef27e14-backup.hg (glob)
1117 saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/489bac576828-bef27e14-backup.hg (glob)
1118 $ hg unbundle -q .hg/strip-backup/*
1118 $ hg unbundle -q .hg/strip-backup/*
1119 BROKEN: obsmarker got lost
1120 $ hg debugobsolete
1119 $ hg debugobsolete
1120 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1121 $ cd ..
1121 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now