##// END OF EJS Templates
changegroup: factor changelog chunk generation into own function...
Gregory Szorc -
r39012:f7228c90 default
parent child Browse files
Show More
@@ -1,1411 +1,1437 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 weakref
12 import weakref
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 hex,
16 hex,
17 nullid,
17 nullid,
18 nullrev,
18 nullrev,
19 short,
19 short,
20 )
20 )
21
21
22 from .thirdparty import (
22 from .thirdparty import (
23 attr,
23 attr,
24 )
24 )
25
25
26 from . import (
26 from . import (
27 dagutil,
27 dagutil,
28 error,
28 error,
29 manifest,
29 manifest,
30 match as matchmod,
30 match as matchmod,
31 mdiff,
31 mdiff,
32 phases,
32 phases,
33 pycompat,
33 pycompat,
34 repository,
34 repository,
35 revlog,
35 revlog,
36 util,
36 util,
37 )
37 )
38
38
39 from .utils import (
39 from .utils import (
40 stringutil,
40 stringutil,
41 )
41 )
42
42
43 _CHANGEGROUPV1_DELTA_HEADER = struct.Struct("20s20s20s20s")
43 _CHANGEGROUPV1_DELTA_HEADER = struct.Struct("20s20s20s20s")
44 _CHANGEGROUPV2_DELTA_HEADER = struct.Struct("20s20s20s20s20s")
44 _CHANGEGROUPV2_DELTA_HEADER = struct.Struct("20s20s20s20s20s")
45 _CHANGEGROUPV3_DELTA_HEADER = struct.Struct(">20s20s20s20s20sH")
45 _CHANGEGROUPV3_DELTA_HEADER = struct.Struct(">20s20s20s20s20sH")
46
46
47 LFS_REQUIREMENT = 'lfs'
47 LFS_REQUIREMENT = 'lfs'
48
48
49 readexactly = util.readexactly
49 readexactly = util.readexactly
50
50
51 def getchunk(stream):
51 def getchunk(stream):
52 """return the next chunk from stream as a string"""
52 """return the next chunk from stream as a string"""
53 d = readexactly(stream, 4)
53 d = readexactly(stream, 4)
54 l = struct.unpack(">l", d)[0]
54 l = struct.unpack(">l", d)[0]
55 if l <= 4:
55 if l <= 4:
56 if l:
56 if l:
57 raise error.Abort(_("invalid chunk length %d") % l)
57 raise error.Abort(_("invalid chunk length %d") % l)
58 return ""
58 return ""
59 return readexactly(stream, l - 4)
59 return readexactly(stream, l - 4)
60
60
61 def chunkheader(length):
61 def chunkheader(length):
62 """return a changegroup chunk header (string)"""
62 """return a changegroup chunk header (string)"""
63 return struct.pack(">l", length + 4)
63 return struct.pack(">l", length + 4)
64
64
65 def closechunk():
65 def closechunk():
66 """return a changegroup chunk header (string) for a zero-length chunk"""
66 """return a changegroup chunk header (string) for a zero-length chunk"""
67 return struct.pack(">l", 0)
67 return struct.pack(">l", 0)
68
68
69 def writechunks(ui, chunks, filename, vfs=None):
69 def writechunks(ui, chunks, filename, vfs=None):
70 """Write chunks to a file and return its filename.
70 """Write chunks to a file and return its filename.
71
71
72 The stream is assumed to be a bundle file.
72 The stream is assumed to be a bundle file.
73 Existing files will not be overwritten.
73 Existing files will not be overwritten.
74 If no filename is specified, a temporary file is created.
74 If no filename is specified, a temporary file is created.
75 """
75 """
76 fh = None
76 fh = None
77 cleanup = None
77 cleanup = None
78 try:
78 try:
79 if filename:
79 if filename:
80 if vfs:
80 if vfs:
81 fh = vfs.open(filename, "wb")
81 fh = vfs.open(filename, "wb")
82 else:
82 else:
83 # Increase default buffer size because default is usually
83 # Increase default buffer size because default is usually
84 # small (4k is common on Linux).
84 # small (4k is common on Linux).
85 fh = open(filename, "wb", 131072)
85 fh = open(filename, "wb", 131072)
86 else:
86 else:
87 fd, filename = pycompat.mkstemp(prefix="hg-bundle-", suffix=".hg")
87 fd, filename = pycompat.mkstemp(prefix="hg-bundle-", suffix=".hg")
88 fh = os.fdopen(fd, r"wb")
88 fh = os.fdopen(fd, r"wb")
89 cleanup = filename
89 cleanup = filename
90 for c in chunks:
90 for c in chunks:
91 fh.write(c)
91 fh.write(c)
92 cleanup = None
92 cleanup = None
93 return filename
93 return filename
94 finally:
94 finally:
95 if fh is not None:
95 if fh is not None:
96 fh.close()
96 fh.close()
97 if cleanup is not None:
97 if cleanup is not None:
98 if filename and vfs:
98 if filename and vfs:
99 vfs.unlink(cleanup)
99 vfs.unlink(cleanup)
100 else:
100 else:
101 os.unlink(cleanup)
101 os.unlink(cleanup)
102
102
103 class cg1unpacker(object):
103 class cg1unpacker(object):
104 """Unpacker for cg1 changegroup streams.
104 """Unpacker for cg1 changegroup streams.
105
105
106 A changegroup unpacker handles the framing of the revision data in
106 A changegroup unpacker handles the framing of the revision data in
107 the wire format. Most consumers will want to use the apply()
107 the wire format. Most consumers will want to use the apply()
108 method to add the changes from the changegroup to a repository.
108 method to add the changes from the changegroup to a repository.
109
109
110 If you're forwarding a changegroup unmodified to another consumer,
110 If you're forwarding a changegroup unmodified to another consumer,
111 use getchunks(), which returns an iterator of changegroup
111 use getchunks(), which returns an iterator of changegroup
112 chunks. This is mostly useful for cases where you need to know the
112 chunks. This is mostly useful for cases where you need to know the
113 data stream has ended by observing the end of the changegroup.
113 data stream has ended by observing the end of the changegroup.
114
114
115 deltachunk() is useful only if you're applying delta data. Most
115 deltachunk() is useful only if you're applying delta data. Most
116 consumers should prefer apply() instead.
116 consumers should prefer apply() instead.
117
117
118 A few other public methods exist. Those are used only for
118 A few other public methods exist. Those are used only for
119 bundlerepo and some debug commands - their use is discouraged.
119 bundlerepo and some debug commands - their use is discouraged.
120 """
120 """
121 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
121 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
122 deltaheadersize = deltaheader.size
122 deltaheadersize = deltaheader.size
123 version = '01'
123 version = '01'
124 _grouplistcount = 1 # One list of files after the manifests
124 _grouplistcount = 1 # One list of files after the manifests
125
125
126 def __init__(self, fh, alg, extras=None):
126 def __init__(self, fh, alg, extras=None):
127 if alg is None:
127 if alg is None:
128 alg = 'UN'
128 alg = 'UN'
129 if alg not in util.compengines.supportedbundletypes:
129 if alg not in util.compengines.supportedbundletypes:
130 raise error.Abort(_('unknown stream compression type: %s')
130 raise error.Abort(_('unknown stream compression type: %s')
131 % alg)
131 % alg)
132 if alg == 'BZ':
132 if alg == 'BZ':
133 alg = '_truncatedBZ'
133 alg = '_truncatedBZ'
134
134
135 compengine = util.compengines.forbundletype(alg)
135 compengine = util.compengines.forbundletype(alg)
136 self._stream = compengine.decompressorreader(fh)
136 self._stream = compengine.decompressorreader(fh)
137 self._type = alg
137 self._type = alg
138 self.extras = extras or {}
138 self.extras = extras or {}
139 self.callback = None
139 self.callback = None
140
140
141 # These methods (compressed, read, seek, tell) all appear to only
141 # These methods (compressed, read, seek, tell) all appear to only
142 # be used by bundlerepo, but it's a little hard to tell.
142 # be used by bundlerepo, but it's a little hard to tell.
143 def compressed(self):
143 def compressed(self):
144 return self._type is not None and self._type != 'UN'
144 return self._type is not None and self._type != 'UN'
145 def read(self, l):
145 def read(self, l):
146 return self._stream.read(l)
146 return self._stream.read(l)
147 def seek(self, pos):
147 def seek(self, pos):
148 return self._stream.seek(pos)
148 return self._stream.seek(pos)
149 def tell(self):
149 def tell(self):
150 return self._stream.tell()
150 return self._stream.tell()
151 def close(self):
151 def close(self):
152 return self._stream.close()
152 return self._stream.close()
153
153
154 def _chunklength(self):
154 def _chunklength(self):
155 d = readexactly(self._stream, 4)
155 d = readexactly(self._stream, 4)
156 l = struct.unpack(">l", d)[0]
156 l = struct.unpack(">l", d)[0]
157 if l <= 4:
157 if l <= 4:
158 if l:
158 if l:
159 raise error.Abort(_("invalid chunk length %d") % l)
159 raise error.Abort(_("invalid chunk length %d") % l)
160 return 0
160 return 0
161 if self.callback:
161 if self.callback:
162 self.callback()
162 self.callback()
163 return l - 4
163 return l - 4
164
164
165 def changelogheader(self):
165 def changelogheader(self):
166 """v10 does not have a changelog header chunk"""
166 """v10 does not have a changelog header chunk"""
167 return {}
167 return {}
168
168
169 def manifestheader(self):
169 def manifestheader(self):
170 """v10 does not have a manifest header chunk"""
170 """v10 does not have a manifest header chunk"""
171 return {}
171 return {}
172
172
173 def filelogheader(self):
173 def filelogheader(self):
174 """return the header of the filelogs chunk, v10 only has the filename"""
174 """return the header of the filelogs chunk, v10 only has the filename"""
175 l = self._chunklength()
175 l = self._chunklength()
176 if not l:
176 if not l:
177 return {}
177 return {}
178 fname = readexactly(self._stream, l)
178 fname = readexactly(self._stream, l)
179 return {'filename': fname}
179 return {'filename': fname}
180
180
181 def _deltaheader(self, headertuple, prevnode):
181 def _deltaheader(self, headertuple, prevnode):
182 node, p1, p2, cs = headertuple
182 node, p1, p2, cs = headertuple
183 if prevnode is None:
183 if prevnode is None:
184 deltabase = p1
184 deltabase = p1
185 else:
185 else:
186 deltabase = prevnode
186 deltabase = prevnode
187 flags = 0
187 flags = 0
188 return node, p1, p2, deltabase, cs, flags
188 return node, p1, p2, deltabase, cs, flags
189
189
190 def deltachunk(self, prevnode):
190 def deltachunk(self, prevnode):
191 l = self._chunklength()
191 l = self._chunklength()
192 if not l:
192 if not l:
193 return {}
193 return {}
194 headerdata = readexactly(self._stream, self.deltaheadersize)
194 headerdata = readexactly(self._stream, self.deltaheadersize)
195 header = self.deltaheader.unpack(headerdata)
195 header = self.deltaheader.unpack(headerdata)
196 delta = readexactly(self._stream, l - self.deltaheadersize)
196 delta = readexactly(self._stream, l - self.deltaheadersize)
197 node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode)
197 node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode)
198 return (node, p1, p2, cs, deltabase, delta, flags)
198 return (node, p1, p2, cs, deltabase, delta, flags)
199
199
200 def getchunks(self):
200 def getchunks(self):
201 """returns all the chunks contains in the bundle
201 """returns all the chunks contains in the bundle
202
202
203 Used when you need to forward the binary stream to a file or another
203 Used when you need to forward the binary stream to a file or another
204 network API. To do so, it parse the changegroup data, otherwise it will
204 network API. To do so, it parse the changegroup data, otherwise it will
205 block in case of sshrepo because it don't know the end of the stream.
205 block in case of sshrepo because it don't know the end of the stream.
206 """
206 """
207 # For changegroup 1 and 2, we expect 3 parts: changelog, manifestlog,
207 # For changegroup 1 and 2, we expect 3 parts: changelog, manifestlog,
208 # and a list of filelogs. For changegroup 3, we expect 4 parts:
208 # and a list of filelogs. For changegroup 3, we expect 4 parts:
209 # changelog, manifestlog, a list of tree manifestlogs, and a list of
209 # changelog, manifestlog, a list of tree manifestlogs, and a list of
210 # filelogs.
210 # filelogs.
211 #
211 #
212 # Changelog and manifestlog parts are terminated with empty chunks. The
212 # Changelog and manifestlog parts are terminated with empty chunks. The
213 # tree and file parts are a list of entry sections. Each entry section
213 # tree and file parts are a list of entry sections. Each entry section
214 # is a series of chunks terminating in an empty chunk. The list of these
214 # is a series of chunks terminating in an empty chunk. The list of these
215 # entry sections is terminated in yet another empty chunk, so we know
215 # entry sections is terminated in yet another empty chunk, so we know
216 # we've reached the end of the tree/file list when we reach an empty
216 # we've reached the end of the tree/file list when we reach an empty
217 # chunk that was proceeded by no non-empty chunks.
217 # chunk that was proceeded by no non-empty chunks.
218
218
219 parts = 0
219 parts = 0
220 while parts < 2 + self._grouplistcount:
220 while parts < 2 + self._grouplistcount:
221 noentries = True
221 noentries = True
222 while True:
222 while True:
223 chunk = getchunk(self)
223 chunk = getchunk(self)
224 if not chunk:
224 if not chunk:
225 # The first two empty chunks represent the end of the
225 # The first two empty chunks represent the end of the
226 # changelog and the manifestlog portions. The remaining
226 # changelog and the manifestlog portions. The remaining
227 # empty chunks represent either A) the end of individual
227 # empty chunks represent either A) the end of individual
228 # tree or file entries in the file list, or B) the end of
228 # tree or file entries in the file list, or B) the end of
229 # the entire list. It's the end of the entire list if there
229 # the entire list. It's the end of the entire list if there
230 # were no entries (i.e. noentries is True).
230 # were no entries (i.e. noentries is True).
231 if parts < 2:
231 if parts < 2:
232 parts += 1
232 parts += 1
233 elif noentries:
233 elif noentries:
234 parts += 1
234 parts += 1
235 break
235 break
236 noentries = False
236 noentries = False
237 yield chunkheader(len(chunk))
237 yield chunkheader(len(chunk))
238 pos = 0
238 pos = 0
239 while pos < len(chunk):
239 while pos < len(chunk):
240 next = pos + 2**20
240 next = pos + 2**20
241 yield chunk[pos:next]
241 yield chunk[pos:next]
242 pos = next
242 pos = next
243 yield closechunk()
243 yield closechunk()
244
244
245 def _unpackmanifests(self, repo, revmap, trp, prog):
245 def _unpackmanifests(self, repo, revmap, trp, prog):
246 self.callback = prog.increment
246 self.callback = prog.increment
247 # no need to check for empty manifest group here:
247 # no need to check for empty manifest group here:
248 # if the result of the merge of 1 and 2 is the same in 3 and 4,
248 # if the result of the merge of 1 and 2 is the same in 3 and 4,
249 # no new manifest will be created and the manifest group will
249 # no new manifest will be created and the manifest group will
250 # be empty during the pull
250 # be empty during the pull
251 self.manifestheader()
251 self.manifestheader()
252 deltas = self.deltaiter()
252 deltas = self.deltaiter()
253 repo.manifestlog.addgroup(deltas, revmap, trp)
253 repo.manifestlog.addgroup(deltas, revmap, trp)
254 prog.complete()
254 prog.complete()
255 self.callback = None
255 self.callback = None
256
256
257 def apply(self, repo, tr, srctype, url, targetphase=phases.draft,
257 def apply(self, repo, tr, srctype, url, targetphase=phases.draft,
258 expectedtotal=None):
258 expectedtotal=None):
259 """Add the changegroup returned by source.read() to this repo.
259 """Add the changegroup returned by source.read() to this repo.
260 srctype is a string like 'push', 'pull', or 'unbundle'. url is
260 srctype is a string like 'push', 'pull', or 'unbundle'. url is
261 the URL of the repo where this changegroup is coming from.
261 the URL of the repo where this changegroup is coming from.
262
262
263 Return an integer summarizing the change to this repo:
263 Return an integer summarizing the change to this repo:
264 - nothing changed or no source: 0
264 - nothing changed or no source: 0
265 - more heads than before: 1+added heads (2..n)
265 - more heads than before: 1+added heads (2..n)
266 - fewer heads than before: -1-removed heads (-2..-n)
266 - fewer heads than before: -1-removed heads (-2..-n)
267 - number of heads stays the same: 1
267 - number of heads stays the same: 1
268 """
268 """
269 repo = repo.unfiltered()
269 repo = repo.unfiltered()
270 def csmap(x):
270 def csmap(x):
271 repo.ui.debug("add changeset %s\n" % short(x))
271 repo.ui.debug("add changeset %s\n" % short(x))
272 return len(cl)
272 return len(cl)
273
273
274 def revmap(x):
274 def revmap(x):
275 return cl.rev(x)
275 return cl.rev(x)
276
276
277 changesets = files = revisions = 0
277 changesets = files = revisions = 0
278
278
279 try:
279 try:
280 # The transaction may already carry source information. In this
280 # The transaction may already carry source information. In this
281 # case we use the top level data. We overwrite the argument
281 # case we use the top level data. We overwrite the argument
282 # because we need to use the top level value (if they exist)
282 # because we need to use the top level value (if they exist)
283 # in this function.
283 # in this function.
284 srctype = tr.hookargs.setdefault('source', srctype)
284 srctype = tr.hookargs.setdefault('source', srctype)
285 url = tr.hookargs.setdefault('url', url)
285 url = tr.hookargs.setdefault('url', url)
286 repo.hook('prechangegroup',
286 repo.hook('prechangegroup',
287 throw=True, **pycompat.strkwargs(tr.hookargs))
287 throw=True, **pycompat.strkwargs(tr.hookargs))
288
288
289 # write changelog data to temp files so concurrent readers
289 # write changelog data to temp files so concurrent readers
290 # will not see an inconsistent view
290 # will not see an inconsistent view
291 cl = repo.changelog
291 cl = repo.changelog
292 cl.delayupdate(tr)
292 cl.delayupdate(tr)
293 oldheads = set(cl.heads())
293 oldheads = set(cl.heads())
294
294
295 trp = weakref.proxy(tr)
295 trp = weakref.proxy(tr)
296 # pull off the changeset group
296 # pull off the changeset group
297 repo.ui.status(_("adding changesets\n"))
297 repo.ui.status(_("adding changesets\n"))
298 clstart = len(cl)
298 clstart = len(cl)
299 progress = repo.ui.makeprogress(_('changesets'), unit=_('chunks'),
299 progress = repo.ui.makeprogress(_('changesets'), unit=_('chunks'),
300 total=expectedtotal)
300 total=expectedtotal)
301 self.callback = progress.increment
301 self.callback = progress.increment
302
302
303 efiles = set()
303 efiles = set()
304 def onchangelog(cl, node):
304 def onchangelog(cl, node):
305 efiles.update(cl.readfiles(node))
305 efiles.update(cl.readfiles(node))
306
306
307 self.changelogheader()
307 self.changelogheader()
308 deltas = self.deltaiter()
308 deltas = self.deltaiter()
309 cgnodes = cl.addgroup(deltas, csmap, trp, addrevisioncb=onchangelog)
309 cgnodes = cl.addgroup(deltas, csmap, trp, addrevisioncb=onchangelog)
310 efiles = len(efiles)
310 efiles = len(efiles)
311
311
312 if not cgnodes:
312 if not cgnodes:
313 repo.ui.develwarn('applied empty changegroup',
313 repo.ui.develwarn('applied empty changegroup',
314 config='warn-empty-changegroup')
314 config='warn-empty-changegroup')
315 clend = len(cl)
315 clend = len(cl)
316 changesets = clend - clstart
316 changesets = clend - clstart
317 progress.complete()
317 progress.complete()
318 self.callback = None
318 self.callback = None
319
319
320 # pull off the manifest group
320 # pull off the manifest group
321 repo.ui.status(_("adding manifests\n"))
321 repo.ui.status(_("adding manifests\n"))
322 # We know that we'll never have more manifests than we had
322 # We know that we'll never have more manifests than we had
323 # changesets.
323 # changesets.
324 progress = repo.ui.makeprogress(_('manifests'), unit=_('chunks'),
324 progress = repo.ui.makeprogress(_('manifests'), unit=_('chunks'),
325 total=changesets)
325 total=changesets)
326 self._unpackmanifests(repo, revmap, trp, progress)
326 self._unpackmanifests(repo, revmap, trp, progress)
327
327
328 needfiles = {}
328 needfiles = {}
329 if repo.ui.configbool('server', 'validate'):
329 if repo.ui.configbool('server', 'validate'):
330 cl = repo.changelog
330 cl = repo.changelog
331 ml = repo.manifestlog
331 ml = repo.manifestlog
332 # validate incoming csets have their manifests
332 # validate incoming csets have their manifests
333 for cset in pycompat.xrange(clstart, clend):
333 for cset in pycompat.xrange(clstart, clend):
334 mfnode = cl.changelogrevision(cset).manifest
334 mfnode = cl.changelogrevision(cset).manifest
335 mfest = ml[mfnode].readdelta()
335 mfest = ml[mfnode].readdelta()
336 # store file cgnodes we must see
336 # store file cgnodes we must see
337 for f, n in mfest.iteritems():
337 for f, n in mfest.iteritems():
338 needfiles.setdefault(f, set()).add(n)
338 needfiles.setdefault(f, set()).add(n)
339
339
340 # process the files
340 # process the files
341 repo.ui.status(_("adding file changes\n"))
341 repo.ui.status(_("adding file changes\n"))
342 newrevs, newfiles = _addchangegroupfiles(
342 newrevs, newfiles = _addchangegroupfiles(
343 repo, self, revmap, trp, efiles, needfiles)
343 repo, self, revmap, trp, efiles, needfiles)
344 revisions += newrevs
344 revisions += newrevs
345 files += newfiles
345 files += newfiles
346
346
347 deltaheads = 0
347 deltaheads = 0
348 if oldheads:
348 if oldheads:
349 heads = cl.heads()
349 heads = cl.heads()
350 deltaheads = len(heads) - len(oldheads)
350 deltaheads = len(heads) - len(oldheads)
351 for h in heads:
351 for h in heads:
352 if h not in oldheads and repo[h].closesbranch():
352 if h not in oldheads and repo[h].closesbranch():
353 deltaheads -= 1
353 deltaheads -= 1
354 htext = ""
354 htext = ""
355 if deltaheads:
355 if deltaheads:
356 htext = _(" (%+d heads)") % deltaheads
356 htext = _(" (%+d heads)") % deltaheads
357
357
358 repo.ui.status(_("added %d changesets"
358 repo.ui.status(_("added %d changesets"
359 " with %d changes to %d files%s\n")
359 " with %d changes to %d files%s\n")
360 % (changesets, revisions, files, htext))
360 % (changesets, revisions, files, htext))
361 repo.invalidatevolatilesets()
361 repo.invalidatevolatilesets()
362
362
363 if changesets > 0:
363 if changesets > 0:
364 if 'node' not in tr.hookargs:
364 if 'node' not in tr.hookargs:
365 tr.hookargs['node'] = hex(cl.node(clstart))
365 tr.hookargs['node'] = hex(cl.node(clstart))
366 tr.hookargs['node_last'] = hex(cl.node(clend - 1))
366 tr.hookargs['node_last'] = hex(cl.node(clend - 1))
367 hookargs = dict(tr.hookargs)
367 hookargs = dict(tr.hookargs)
368 else:
368 else:
369 hookargs = dict(tr.hookargs)
369 hookargs = dict(tr.hookargs)
370 hookargs['node'] = hex(cl.node(clstart))
370 hookargs['node'] = hex(cl.node(clstart))
371 hookargs['node_last'] = hex(cl.node(clend - 1))
371 hookargs['node_last'] = hex(cl.node(clend - 1))
372 repo.hook('pretxnchangegroup',
372 repo.hook('pretxnchangegroup',
373 throw=True, **pycompat.strkwargs(hookargs))
373 throw=True, **pycompat.strkwargs(hookargs))
374
374
375 added = [cl.node(r) for r in pycompat.xrange(clstart, clend)]
375 added = [cl.node(r) for r in pycompat.xrange(clstart, clend)]
376 phaseall = None
376 phaseall = None
377 if srctype in ('push', 'serve'):
377 if srctype in ('push', 'serve'):
378 # Old servers can not push the boundary themselves.
378 # Old servers can not push the boundary themselves.
379 # New servers won't push the boundary if changeset already
379 # New servers won't push the boundary if changeset already
380 # exists locally as secret
380 # exists locally as secret
381 #
381 #
382 # We should not use added here but the list of all change in
382 # We should not use added here but the list of all change in
383 # the bundle
383 # the bundle
384 if repo.publishing():
384 if repo.publishing():
385 targetphase = phaseall = phases.public
385 targetphase = phaseall = phases.public
386 else:
386 else:
387 # closer target phase computation
387 # closer target phase computation
388
388
389 # Those changesets have been pushed from the
389 # Those changesets have been pushed from the
390 # outside, their phases are going to be pushed
390 # outside, their phases are going to be pushed
391 # alongside. Therefor `targetphase` is
391 # alongside. Therefor `targetphase` is
392 # ignored.
392 # ignored.
393 targetphase = phaseall = phases.draft
393 targetphase = phaseall = phases.draft
394 if added:
394 if added:
395 phases.registernew(repo, tr, targetphase, added)
395 phases.registernew(repo, tr, targetphase, added)
396 if phaseall is not None:
396 if phaseall is not None:
397 phases.advanceboundary(repo, tr, phaseall, cgnodes)
397 phases.advanceboundary(repo, tr, phaseall, cgnodes)
398
398
399 if changesets > 0:
399 if changesets > 0:
400
400
401 def runhooks():
401 def runhooks():
402 # These hooks run when the lock releases, not when the
402 # These hooks run when the lock releases, not when the
403 # transaction closes. So it's possible for the changelog
403 # transaction closes. So it's possible for the changelog
404 # to have changed since we last saw it.
404 # to have changed since we last saw it.
405 if clstart >= len(repo):
405 if clstart >= len(repo):
406 return
406 return
407
407
408 repo.hook("changegroup", **pycompat.strkwargs(hookargs))
408 repo.hook("changegroup", **pycompat.strkwargs(hookargs))
409
409
410 for n in added:
410 for n in added:
411 args = hookargs.copy()
411 args = hookargs.copy()
412 args['node'] = hex(n)
412 args['node'] = hex(n)
413 del args['node_last']
413 del args['node_last']
414 repo.hook("incoming", **pycompat.strkwargs(args))
414 repo.hook("incoming", **pycompat.strkwargs(args))
415
415
416 newheads = [h for h in repo.heads()
416 newheads = [h for h in repo.heads()
417 if h not in oldheads]
417 if h not in oldheads]
418 repo.ui.log("incoming",
418 repo.ui.log("incoming",
419 "%d incoming changes - new heads: %s\n",
419 "%d incoming changes - new heads: %s\n",
420 len(added),
420 len(added),
421 ', '.join([hex(c[:6]) for c in newheads]))
421 ', '.join([hex(c[:6]) for c in newheads]))
422
422
423 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
423 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
424 lambda tr: repo._afterlock(runhooks))
424 lambda tr: repo._afterlock(runhooks))
425 finally:
425 finally:
426 repo.ui.flush()
426 repo.ui.flush()
427 # never return 0 here:
427 # never return 0 here:
428 if deltaheads < 0:
428 if deltaheads < 0:
429 ret = deltaheads - 1
429 ret = deltaheads - 1
430 else:
430 else:
431 ret = deltaheads + 1
431 ret = deltaheads + 1
432 return ret
432 return ret
433
433
434 def deltaiter(self):
434 def deltaiter(self):
435 """
435 """
436 returns an iterator of the deltas in this changegroup
436 returns an iterator of the deltas in this changegroup
437
437
438 Useful for passing to the underlying storage system to be stored.
438 Useful for passing to the underlying storage system to be stored.
439 """
439 """
440 chain = None
440 chain = None
441 for chunkdata in iter(lambda: self.deltachunk(chain), {}):
441 for chunkdata in iter(lambda: self.deltachunk(chain), {}):
442 # Chunkdata: (node, p1, p2, cs, deltabase, delta, flags)
442 # Chunkdata: (node, p1, p2, cs, deltabase, delta, flags)
443 yield chunkdata
443 yield chunkdata
444 chain = chunkdata[0]
444 chain = chunkdata[0]
445
445
446 class cg2unpacker(cg1unpacker):
446 class cg2unpacker(cg1unpacker):
447 """Unpacker for cg2 streams.
447 """Unpacker for cg2 streams.
448
448
449 cg2 streams add support for generaldelta, so the delta header
449 cg2 streams add support for generaldelta, so the delta header
450 format is slightly different. All other features about the data
450 format is slightly different. All other features about the data
451 remain the same.
451 remain the same.
452 """
452 """
453 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
453 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
454 deltaheadersize = deltaheader.size
454 deltaheadersize = deltaheader.size
455 version = '02'
455 version = '02'
456
456
457 def _deltaheader(self, headertuple, prevnode):
457 def _deltaheader(self, headertuple, prevnode):
458 node, p1, p2, deltabase, cs = headertuple
458 node, p1, p2, deltabase, cs = headertuple
459 flags = 0
459 flags = 0
460 return node, p1, p2, deltabase, cs, flags
460 return node, p1, p2, deltabase, cs, flags
461
461
462 class cg3unpacker(cg2unpacker):
462 class cg3unpacker(cg2unpacker):
463 """Unpacker for cg3 streams.
463 """Unpacker for cg3 streams.
464
464
465 cg3 streams add support for exchanging treemanifests and revlog
465 cg3 streams add support for exchanging treemanifests and revlog
466 flags. It adds the revlog flags to the delta header and an empty chunk
466 flags. It adds the revlog flags to the delta header and an empty chunk
467 separating manifests and files.
467 separating manifests and files.
468 """
468 """
469 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
469 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
470 deltaheadersize = deltaheader.size
470 deltaheadersize = deltaheader.size
471 version = '03'
471 version = '03'
472 _grouplistcount = 2 # One list of manifests and one list of files
472 _grouplistcount = 2 # One list of manifests and one list of files
473
473
474 def _deltaheader(self, headertuple, prevnode):
474 def _deltaheader(self, headertuple, prevnode):
475 node, p1, p2, deltabase, cs, flags = headertuple
475 node, p1, p2, deltabase, cs, flags = headertuple
476 return node, p1, p2, deltabase, cs, flags
476 return node, p1, p2, deltabase, cs, flags
477
477
478 def _unpackmanifests(self, repo, revmap, trp, prog):
478 def _unpackmanifests(self, repo, revmap, trp, prog):
479 super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog)
479 super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog)
480 for chunkdata in iter(self.filelogheader, {}):
480 for chunkdata in iter(self.filelogheader, {}):
481 # If we get here, there are directory manifests in the changegroup
481 # If we get here, there are directory manifests in the changegroup
482 d = chunkdata["filename"]
482 d = chunkdata["filename"]
483 repo.ui.debug("adding %s revisions\n" % d)
483 repo.ui.debug("adding %s revisions\n" % d)
484 dirlog = repo.manifestlog._revlog.dirlog(d)
484 dirlog = repo.manifestlog._revlog.dirlog(d)
485 deltas = self.deltaiter()
485 deltas = self.deltaiter()
486 if not dirlog.addgroup(deltas, revmap, trp):
486 if not dirlog.addgroup(deltas, revmap, trp):
487 raise error.Abort(_("received dir revlog group is empty"))
487 raise error.Abort(_("received dir revlog group is empty"))
488
488
489 class headerlessfixup(object):
489 class headerlessfixup(object):
490 def __init__(self, fh, h):
490 def __init__(self, fh, h):
491 self._h = h
491 self._h = h
492 self._fh = fh
492 self._fh = fh
493 def read(self, n):
493 def read(self, n):
494 if self._h:
494 if self._h:
495 d, self._h = self._h[:n], self._h[n:]
495 d, self._h = self._h[:n], self._h[n:]
496 if len(d) < n:
496 if len(d) < n:
497 d += readexactly(self._fh, n - len(d))
497 d += readexactly(self._fh, n - len(d))
498 return d
498 return d
499 return readexactly(self._fh, n)
499 return readexactly(self._fh, n)
500
500
501 @attr.s(slots=True, frozen=True)
501 @attr.s(slots=True, frozen=True)
502 class revisiondelta(object):
502 class revisiondelta(object):
503 """Describes a delta entry in a changegroup.
503 """Describes a delta entry in a changegroup.
504
504
505 Captured data is sufficient to serialize the delta into multiple
505 Captured data is sufficient to serialize the delta into multiple
506 formats.
506 formats.
507 """
507 """
508 # 20 byte node of this revision.
508 # 20 byte node of this revision.
509 node = attr.ib()
509 node = attr.ib()
510 # 20 byte nodes of parent revisions.
510 # 20 byte nodes of parent revisions.
511 p1node = attr.ib()
511 p1node = attr.ib()
512 p2node = attr.ib()
512 p2node = attr.ib()
513 # 20 byte node of node this delta is against.
513 # 20 byte node of node this delta is against.
514 basenode = attr.ib()
514 basenode = attr.ib()
515 # 20 byte node of changeset revision this delta is associated with.
515 # 20 byte node of changeset revision this delta is associated with.
516 linknode = attr.ib()
516 linknode = attr.ib()
517 # 2 bytes of flags to apply to revision data.
517 # 2 bytes of flags to apply to revision data.
518 flags = attr.ib()
518 flags = attr.ib()
519 # Iterable of chunks holding raw delta data.
519 # Iterable of chunks holding raw delta data.
520 deltachunks = attr.ib()
520 deltachunks = attr.ib()
521
521
522 class cgpacker(object):
522 class cgpacker(object):
523 def __init__(self, repo, filematcher, version, allowreorder,
523 def __init__(self, repo, filematcher, version, allowreorder,
524 deltaparentfn, builddeltaheader, manifestsend,
524 deltaparentfn, builddeltaheader, manifestsend,
525 bundlecaps=None, ellipses=False,
525 bundlecaps=None, ellipses=False,
526 shallow=False, ellipsisroots=None, fullnodes=None):
526 shallow=False, ellipsisroots=None, fullnodes=None):
527 """Given a source repo, construct a bundler.
527 """Given a source repo, construct a bundler.
528
528
529 filematcher is a matcher that matches on files to include in the
529 filematcher is a matcher that matches on files to include in the
530 changegroup. Used to facilitate sparse changegroups.
530 changegroup. Used to facilitate sparse changegroups.
531
531
532 allowreorder controls whether reordering of revisions is allowed.
532 allowreorder controls whether reordering of revisions is allowed.
533 This value is used when ``bundle.reorder`` is ``auto`` or isn't
533 This value is used when ``bundle.reorder`` is ``auto`` or isn't
534 set.
534 set.
535
535
536 deltaparentfn is a callable that resolves the delta parent for
536 deltaparentfn is a callable that resolves the delta parent for
537 a specific revision.
537 a specific revision.
538
538
539 builddeltaheader is a callable that constructs the header for a group
539 builddeltaheader is a callable that constructs the header for a group
540 delta.
540 delta.
541
541
542 manifestsend is a chunk to send after manifests have been fully emitted.
542 manifestsend is a chunk to send after manifests have been fully emitted.
543
543
544 ellipses indicates whether ellipsis serving mode is enabled.
544 ellipses indicates whether ellipsis serving mode is enabled.
545
545
546 bundlecaps is optional and can be used to specify the set of
546 bundlecaps is optional and can be used to specify the set of
547 capabilities which can be used to build the bundle. While bundlecaps is
547 capabilities which can be used to build the bundle. While bundlecaps is
548 unused in core Mercurial, extensions rely on this feature to communicate
548 unused in core Mercurial, extensions rely on this feature to communicate
549 capabilities to customize the changegroup packer.
549 capabilities to customize the changegroup packer.
550
550
551 shallow indicates whether shallow data might be sent. The packer may
551 shallow indicates whether shallow data might be sent. The packer may
552 need to pack file contents not introduced by the changes being packed.
552 need to pack file contents not introduced by the changes being packed.
553
553
554 fullnodes is the list of nodes which should not be ellipsis nodes. We
554 fullnodes is the list of nodes which should not be ellipsis nodes. We
555 store this rather than the set of nodes that should be ellipsis because
555 store this rather than the set of nodes that should be ellipsis because
556 for very large histories we expect this to be significantly smaller.
556 for very large histories we expect this to be significantly smaller.
557 """
557 """
558 assert filematcher
558 assert filematcher
559 self._filematcher = filematcher
559 self._filematcher = filematcher
560
560
561 self.version = version
561 self.version = version
562 self._deltaparentfn = deltaparentfn
562 self._deltaparentfn = deltaparentfn
563 self._builddeltaheader = builddeltaheader
563 self._builddeltaheader = builddeltaheader
564 self._manifestsend = manifestsend
564 self._manifestsend = manifestsend
565 self._ellipses = ellipses
565 self._ellipses = ellipses
566
566
567 # Set of capabilities we can use to build the bundle.
567 # Set of capabilities we can use to build the bundle.
568 if bundlecaps is None:
568 if bundlecaps is None:
569 bundlecaps = set()
569 bundlecaps = set()
570 self._bundlecaps = bundlecaps
570 self._bundlecaps = bundlecaps
571 self._isshallow = shallow
571 self._isshallow = shallow
572 self._fullnodes = fullnodes
572 self._fullnodes = fullnodes
573
573
574 # Maps ellipsis revs to their roots at the changelog level.
574 # Maps ellipsis revs to their roots at the changelog level.
575 self._precomputedellipsis = ellipsisroots
575 self._precomputedellipsis = ellipsisroots
576
576
577 # experimental config: bundle.reorder
577 # experimental config: bundle.reorder
578 reorder = repo.ui.config('bundle', 'reorder')
578 reorder = repo.ui.config('bundle', 'reorder')
579 if reorder == 'auto':
579 if reorder == 'auto':
580 self._reorder = allowreorder
580 self._reorder = allowreorder
581 else:
581 else:
582 self._reorder = stringutil.parsebool(reorder)
582 self._reorder = stringutil.parsebool(reorder)
583
583
584 self._repo = repo
584 self._repo = repo
585
585
586 if self._repo.ui.verbose and not self._repo.ui.debugflag:
586 if self._repo.ui.verbose and not self._repo.ui.debugflag:
587 self._verbosenote = self._repo.ui.note
587 self._verbosenote = self._repo.ui.note
588 else:
588 else:
589 self._verbosenote = lambda s: None
589 self._verbosenote = lambda s: None
590
590
591 # TODO the functionality keyed off of this should probably be
591 # TODO the functionality keyed off of this should probably be
592 # controlled via arguments to group() that influence behavior.
592 # controlled via arguments to group() that influence behavior.
593 self._changelogdone = False
593 self._changelogdone = False
594
594
595 # Maps CL revs to per-revlog revisions. Cleared in close() at
595 # Maps CL revs to per-revlog revisions. Cleared in close() at
596 # the end of each group.
596 # the end of each group.
597 self._clrevtolocalrev = {}
597 self._clrevtolocalrev = {}
598 self._nextclrevtolocalrev = {}
598 self._nextclrevtolocalrev = {}
599
599
600 # Maps changelog nodes to changelog revs. Filled in once
600 # Maps changelog nodes to changelog revs. Filled in once
601 # during changelog stage and then left unmodified.
601 # during changelog stage and then left unmodified.
602 self._clnodetorev = {}
602 self._clnodetorev = {}
603
603
604 def _close(self):
604 def _close(self):
605 # Ellipses serving mode.
605 # Ellipses serving mode.
606 self._clrevtolocalrev.clear()
606 self._clrevtolocalrev.clear()
607 if self._nextclrevtolocalrev is not None:
607 if self._nextclrevtolocalrev is not None:
608 self._clrevtolocalrev = self._nextclrevtolocalrev
608 self._clrevtolocalrev = self._nextclrevtolocalrev
609 self._nextclrevtolocalrev = None
609 self._nextclrevtolocalrev = None
610 self._changelogdone = True
610 self._changelogdone = True
611
611
612 return closechunk()
612 return closechunk()
613
613
614 def _fileheader(self, fname):
614 def _fileheader(self, fname):
615 return chunkheader(len(fname)) + fname
615 return chunkheader(len(fname)) + fname
616
616
617 # Extracted both for clarity and for overriding in extensions.
617 # Extracted both for clarity and for overriding in extensions.
618 def _sortgroup(self, store, nodelist, lookup):
618 def _sortgroup(self, store, nodelist, lookup):
619 """Sort nodes for change group and turn them into revnums."""
619 """Sort nodes for change group and turn them into revnums."""
620 # Ellipses serving mode.
620 # Ellipses serving mode.
621 #
621 #
622 # In a perfect world, we'd generate better ellipsis-ified graphs
622 # In a perfect world, we'd generate better ellipsis-ified graphs
623 # for non-changelog revlogs. In practice, we haven't started doing
623 # for non-changelog revlogs. In practice, we haven't started doing
624 # that yet, so the resulting DAGs for the manifestlog and filelogs
624 # that yet, so the resulting DAGs for the manifestlog and filelogs
625 # are actually full of bogus parentage on all the ellipsis
625 # are actually full of bogus parentage on all the ellipsis
626 # nodes. This has the side effect that, while the contents are
626 # nodes. This has the side effect that, while the contents are
627 # correct, the individual DAGs might be completely out of whack in
627 # correct, the individual DAGs might be completely out of whack in
628 # a case like 882681bc3166 and its ancestors (back about 10
628 # a case like 882681bc3166 and its ancestors (back about 10
629 # revisions or so) in the main hg repo.
629 # revisions or so) in the main hg repo.
630 #
630 #
631 # The one invariant we *know* holds is that the new (potentially
631 # The one invariant we *know* holds is that the new (potentially
632 # bogus) DAG shape will be valid if we order the nodes in the
632 # bogus) DAG shape will be valid if we order the nodes in the
633 # order that they're introduced in dramatis personae by the
633 # order that they're introduced in dramatis personae by the
634 # changelog, so what we do is we sort the non-changelog histories
634 # changelog, so what we do is we sort the non-changelog histories
635 # by the order in which they are used by the changelog.
635 # by the order in which they are used by the changelog.
636 if self._ellipses and self._clnodetorev:
636 if self._ellipses and self._clnodetorev:
637 key = lambda n: self._clnodetorev[lookup(n)]
637 key = lambda n: self._clnodetorev[lookup(n)]
638 return [store.rev(n) for n in sorted(nodelist, key=key)]
638 return [store.rev(n) for n in sorted(nodelist, key=key)]
639
639
640 # for generaldelta revlogs, we linearize the revs; this will both be
640 # for generaldelta revlogs, we linearize the revs; this will both be
641 # much quicker and generate a much smaller bundle
641 # much quicker and generate a much smaller bundle
642 if (store._generaldelta and self._reorder is None) or self._reorder:
642 if (store._generaldelta and self._reorder is None) or self._reorder:
643 dag = dagutil.revlogdag(store)
643 dag = dagutil.revlogdag(store)
644 return dag.linearize(set(store.rev(n) for n in nodelist))
644 return dag.linearize(set(store.rev(n) for n in nodelist))
645 else:
645 else:
646 return sorted([store.rev(n) for n in nodelist])
646 return sorted([store.rev(n) for n in nodelist])
647
647
648 def group(self, nodelist, store, lookup, units=None):
648 def group(self, nodelist, store, lookup, units=None):
649 """Calculate a delta group, yielding a sequence of changegroup chunks
649 """Calculate a delta group, yielding a sequence of changegroup chunks
650 (strings).
650 (strings).
651
651
652 Given a list of changeset revs, return a set of deltas and
652 Given a list of changeset revs, return a set of deltas and
653 metadata corresponding to nodes. The first delta is
653 metadata corresponding to nodes. The first delta is
654 first parent(nodelist[0]) -> nodelist[0], the receiver is
654 first parent(nodelist[0]) -> nodelist[0], the receiver is
655 guaranteed to have this parent as it has all history before
655 guaranteed to have this parent as it has all history before
656 these changesets. In the case firstparent is nullrev the
656 these changesets. In the case firstparent is nullrev the
657 changegroup starts with a full revision.
657 changegroup starts with a full revision.
658
658
659 If units is not None, progress detail will be generated, units specifies
659 If units is not None, progress detail will be generated, units specifies
660 the type of revlog that is touched (changelog, manifest, etc.).
660 the type of revlog that is touched (changelog, manifest, etc.).
661 """
661 """
662 # if we don't have any revisions touched by these changesets, bail
662 # if we don't have any revisions touched by these changesets, bail
663 if len(nodelist) == 0:
663 if len(nodelist) == 0:
664 yield self._close()
664 yield self._close()
665 return
665 return
666
666
667 revs = self._sortgroup(store, nodelist, lookup)
667 revs = self._sortgroup(store, nodelist, lookup)
668
668
669 # add the parent of the first rev
669 # add the parent of the first rev
670 p = store.parentrevs(revs[0])[0]
670 p = store.parentrevs(revs[0])[0]
671 revs.insert(0, p)
671 revs.insert(0, p)
672
672
673 # build deltas
673 # build deltas
674 progress = None
674 progress = None
675 if units is not None:
675 if units is not None:
676 progress = self._repo.ui.makeprogress(_('bundling'), unit=units,
676 progress = self._repo.ui.makeprogress(_('bundling'), unit=units,
677 total=(len(revs) - 1))
677 total=(len(revs) - 1))
678 for r in pycompat.xrange(len(revs) - 1):
678 for r in pycompat.xrange(len(revs) - 1):
679 if progress:
679 if progress:
680 progress.update(r + 1)
680 progress.update(r + 1)
681 prev, curr = revs[r], revs[r + 1]
681 prev, curr = revs[r], revs[r + 1]
682 linknode = lookup(store.node(curr))
682 linknode = lookup(store.node(curr))
683 for c in self._revchunk(store, curr, prev, linknode):
683 for c in self._revchunk(store, curr, prev, linknode):
684 yield c
684 yield c
685
685
686 if progress:
686 if progress:
687 progress.complete()
687 progress.complete()
688 yield self._close()
688 yield self._close()
689
689
690 # filter any nodes that claim to be part of the known set
690 # filter any nodes that claim to be part of the known set
691 def _prune(self, store, missing, commonrevs):
691 def _prune(self, store, missing, commonrevs):
692 # TODO this violates storage abstraction for manifests.
692 # TODO this violates storage abstraction for manifests.
693 if isinstance(store, manifest.manifestrevlog):
693 if isinstance(store, manifest.manifestrevlog):
694 if not self._filematcher.visitdir(store._dir[:-1] or '.'):
694 if not self._filematcher.visitdir(store._dir[:-1] or '.'):
695 return []
695 return []
696
696
697 rr, rl = store.rev, store.linkrev
697 rr, rl = store.rev, store.linkrev
698 return [n for n in missing if rl(rr(n)) not in commonrevs]
698 return [n for n in missing if rl(rr(n)) not in commonrevs]
699
699
700 def _packmanifests(self, dir, mfnodes, lookuplinknode):
700 def _packmanifests(self, dir, mfnodes, lookuplinknode):
701 """Pack manifests into a changegroup stream.
701 """Pack manifests into a changegroup stream.
702
702
703 Encodes the directory name in the output so multiple manifests
703 Encodes the directory name in the output so multiple manifests
704 can be sent. Multiple manifests is not supported by cg1 and cg2.
704 can be sent. Multiple manifests is not supported by cg1 and cg2.
705 """
705 """
706
706
707 if dir:
707 if dir:
708 assert self.version == b'03'
708 assert self.version == b'03'
709 yield self._fileheader(dir)
709 yield self._fileheader(dir)
710
710
711 # TODO violates storage abstractions by assuming revlogs.
711 # TODO violates storage abstractions by assuming revlogs.
712 dirlog = self._repo.manifestlog._revlog.dirlog(dir)
712 dirlog = self._repo.manifestlog._revlog.dirlog(dir)
713 for chunk in self.group(mfnodes, dirlog, lookuplinknode,
713 for chunk in self.group(mfnodes, dirlog, lookuplinknode,
714 units=_('manifests')):
714 units=_('manifests')):
715 yield chunk
715 yield chunk
716
716
717 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
717 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
718 '''yield a sequence of changegroup chunks (strings)'''
718 """Yield a sequence of changegroup byte chunks."""
719
719 repo = self._repo
720 repo = self._repo
720 cl = repo.changelog
721 cl = repo.changelog
721
722
723 self._verbosenote(_('uncompressed size of bundle content:\n'))
724 size = 0
725
726 clstate, chunks = self._generatechangelog(cl, clnodes)
727 for chunk in chunks:
728 size += len(chunk)
729 yield chunk
730
731 self._verbosenote(_('%8.i (changelog)\n') % size)
732
733 clrevorder = clstate['clrevorder']
734 mfs = clstate['mfs']
735 changedfiles = clstate['changedfiles']
736
737 # We need to make sure that the linkrev in the changegroup refers to
738 # the first changeset that introduced the manifest or file revision.
739 # The fastpath is usually safer than the slowpath, because the filelogs
740 # are walked in revlog order.
741 #
742 # When taking the slowpath with reorder=None and the manifest revlog
743 # uses generaldelta, the manifest may be walked in the "wrong" order.
744 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
745 # cc0ff93d0c0c).
746 #
747 # When taking the fastpath, we are only vulnerable to reordering
748 # of the changelog itself. The changelog never uses generaldelta, so
749 # it is only reordered when reorder=True. To handle this case, we
750 # simply take the slowpath, which already has the 'clrevorder' logic.
751 # This was also fixed in cc0ff93d0c0c.
752 fastpathlinkrev = fastpathlinkrev and not self._reorder
753 # Treemanifests don't work correctly with fastpathlinkrev
754 # either, because we don't discover which directory nodes to
755 # send along with files. This could probably be fixed.
756 fastpathlinkrev = fastpathlinkrev and (
757 'treemanifest' not in repo.requirements)
758
759 fnodes = {} # needed file nodes
760
761 for chunk in self.generatemanifests(commonrevs, clrevorder,
762 fastpathlinkrev, mfs, fnodes, source):
763 yield chunk
764
765 if self._ellipses:
766 mfdicts = None
767 if self._isshallow:
768 mfdicts = [(self._repo.manifestlog[n].read(), lr)
769 for (n, lr) in mfs.iteritems()]
770
771 mfs.clear()
772 clrevs = set(cl.rev(x) for x in clnodes)
773
774 if not fastpathlinkrev:
775 def linknodes(unused, fname):
776 return fnodes.get(fname, {})
777 else:
778 cln = cl.node
779 def linknodes(filerevlog, fname):
780 llr = filerevlog.linkrev
781 fln = filerevlog.node
782 revs = ((r, llr(r)) for r in filerevlog)
783 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
784
785 if self._ellipses:
786 # We need to pass the mfdicts variable down into
787 # generatefiles(), but more than one command might have
788 # wrapped generatefiles so we can't modify the function
789 # signature. Instead, we pass the data to ourselves using an
790 # instance attribute. I'm sorry.
791 self._mfdicts = mfdicts
792
793 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
794 source):
795 yield chunk
796
797 yield self._close()
798
799 if clnodes:
800 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
801
802 def _generatechangelog(self, cl, nodes):
803 """Generate data for changelog chunks.
804
805 Returns a 2-tuple of a dict containing state and an iterable of
806 byte chunks. The state will not be fully populated until the
807 chunk stream has been fully consumed.
808 """
722 clrevorder = {}
809 clrevorder = {}
723 mfs = {} # needed manifests
810 mfs = {} # needed manifests
724 fnodes = {} # needed file nodes
811 mfl = self._repo.manifestlog
725 mfl = repo.manifestlog
726 # TODO violates storage abstraction.
812 # TODO violates storage abstraction.
727 mfrevlog = mfl._revlog
813 mfrevlog = mfl._revlog
728 changedfiles = set()
814 changedfiles = set()
729
815
730 # Callback for the changelog, used to collect changed files and
816 # Callback for the changelog, used to collect changed files and
731 # manifest nodes.
817 # manifest nodes.
732 # Returns the linkrev node (identity in the changelog case).
818 # Returns the linkrev node (identity in the changelog case).
733 def lookupcl(x):
819 def lookupcl(x):
734 c = cl.read(x)
820 c = cl.read(x)
735 clrevorder[x] = len(clrevorder)
821 clrevorder[x] = len(clrevorder)
736
822
737 if self._ellipses:
823 if self._ellipses:
738 # Only update mfs if x is going to be sent. Otherwise we
824 # Only update mfs if x is going to be sent. Otherwise we
739 # end up with bogus linkrevs specified for manifests and
825 # end up with bogus linkrevs specified for manifests and
740 # we skip some manifest nodes that we should otherwise
826 # we skip some manifest nodes that we should otherwise
741 # have sent.
827 # have sent.
742 if (x in self._fullnodes
828 if (x in self._fullnodes
743 or cl.rev(x) in self._precomputedellipsis):
829 or cl.rev(x) in self._precomputedellipsis):
744 n = c[0]
830 n = c[0]
745 # Record the first changeset introducing this manifest
831 # Record the first changeset introducing this manifest
746 # version.
832 # version.
747 mfs.setdefault(n, x)
833 mfs.setdefault(n, x)
748 # Set this narrow-specific dict so we have the lowest
834 # Set this narrow-specific dict so we have the lowest
749 # manifest revnum to look up for this cl revnum. (Part of
835 # manifest revnum to look up for this cl revnum. (Part of
750 # mapping changelog ellipsis parents to manifest ellipsis
836 # mapping changelog ellipsis parents to manifest ellipsis
751 # parents)
837 # parents)
752 self._nextclrevtolocalrev.setdefault(cl.rev(x),
838 self._nextclrevtolocalrev.setdefault(cl.rev(x),
753 mfrevlog.rev(n))
839 mfrevlog.rev(n))
754 # We can't trust the changed files list in the changeset if the
840 # We can't trust the changed files list in the changeset if the
755 # client requested a shallow clone.
841 # client requested a shallow clone.
756 if self._isshallow:
842 if self._isshallow:
757 changedfiles.update(mfl[c[0]].read().keys())
843 changedfiles.update(mfl[c[0]].read().keys())
758 else:
844 else:
759 changedfiles.update(c[3])
845 changedfiles.update(c[3])
760 else:
846 else:
761
847
762 n = c[0]
848 n = c[0]
763 # record the first changeset introducing this manifest version
849 # record the first changeset introducing this manifest version
764 mfs.setdefault(n, x)
850 mfs.setdefault(n, x)
765 # Record a complete list of potentially-changed files in
851 # Record a complete list of potentially-changed files in
766 # this manifest.
852 # this manifest.
767 changedfiles.update(c[3])
853 changedfiles.update(c[3])
768
854
769 return x
855 return x
770
856
771 self._verbosenote(_('uncompressed size of bundle content:\n'))
857 state = {
772 size = 0
858 'clrevorder': clrevorder,
773 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
859 'mfs': mfs,
774 size += len(chunk)
860 'changedfiles': changedfiles,
775 yield chunk
861 }
776 self._verbosenote(_('%8.i (changelog)\n') % size)
777
778 # We need to make sure that the linkrev in the changegroup refers to
779 # the first changeset that introduced the manifest or file revision.
780 # The fastpath is usually safer than the slowpath, because the filelogs
781 # are walked in revlog order.
782 #
783 # When taking the slowpath with reorder=None and the manifest revlog
784 # uses generaldelta, the manifest may be walked in the "wrong" order.
785 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
786 # cc0ff93d0c0c).
787 #
788 # When taking the fastpath, we are only vulnerable to reordering
789 # of the changelog itself. The changelog never uses generaldelta, so
790 # it is only reordered when reorder=True. To handle this case, we
791 # simply take the slowpath, which already has the 'clrevorder' logic.
792 # This was also fixed in cc0ff93d0c0c.
793 fastpathlinkrev = fastpathlinkrev and not self._reorder
794 # Treemanifests don't work correctly with fastpathlinkrev
795 # either, because we don't discover which directory nodes to
796 # send along with files. This could probably be fixed.
797 fastpathlinkrev = fastpathlinkrev and (
798 'treemanifest' not in repo.requirements)
799
800 for chunk in self.generatemanifests(commonrevs, clrevorder,
801 fastpathlinkrev, mfs, fnodes, source):
802 yield chunk
803
862
804 if self._ellipses:
863 gen = self.group(nodes, cl, lookupcl, units=_('changesets'))
805 mfdicts = None
806 if self._isshallow:
807 mfdicts = [(self._repo.manifestlog[n].read(), lr)
808 for (n, lr) in mfs.iteritems()]
809
810 mfs.clear()
811 clrevs = set(cl.rev(x) for x in clnodes)
812
864
813 if not fastpathlinkrev:
865 return state, gen
814 def linknodes(unused, fname):
815 return fnodes.get(fname, {})
816 else:
817 cln = cl.node
818 def linknodes(filerevlog, fname):
819 llr = filerevlog.linkrev
820 fln = filerevlog.node
821 revs = ((r, llr(r)) for r in filerevlog)
822 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
823
824 if self._ellipses:
825 # We need to pass the mfdicts variable down into
826 # generatefiles(), but more than one command might have
827 # wrapped generatefiles so we can't modify the function
828 # signature. Instead, we pass the data to ourselves using an
829 # instance attribute. I'm sorry.
830 self._mfdicts = mfdicts
831
832 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
833 source):
834 yield chunk
835
836 yield self._close()
837
838 if clnodes:
839 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
840
866
841 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
867 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
842 fnodes, source):
868 fnodes, source):
843 """Returns an iterator of changegroup chunks containing manifests.
869 """Returns an iterator of changegroup chunks containing manifests.
844
870
845 `source` is unused here, but is used by extensions like remotefilelog to
871 `source` is unused here, but is used by extensions like remotefilelog to
846 change what is sent based in pulls vs pushes, etc.
872 change what is sent based in pulls vs pushes, etc.
847 """
873 """
848 repo = self._repo
874 repo = self._repo
849 mfl = repo.manifestlog
875 mfl = repo.manifestlog
850 dirlog = mfl._revlog.dirlog
876 dirlog = mfl._revlog.dirlog
851 tmfnodes = {'': mfs}
877 tmfnodes = {'': mfs}
852
878
853 # Callback for the manifest, used to collect linkrevs for filelog
879 # Callback for the manifest, used to collect linkrevs for filelog
854 # revisions.
880 # revisions.
855 # Returns the linkrev node (collected in lookupcl).
881 # Returns the linkrev node (collected in lookupcl).
856 def makelookupmflinknode(dir, nodes):
882 def makelookupmflinknode(dir, nodes):
857 if fastpathlinkrev:
883 if fastpathlinkrev:
858 assert not dir
884 assert not dir
859 return mfs.__getitem__
885 return mfs.__getitem__
860
886
861 def lookupmflinknode(x):
887 def lookupmflinknode(x):
862 """Callback for looking up the linknode for manifests.
888 """Callback for looking up the linknode for manifests.
863
889
864 Returns the linkrev node for the specified manifest.
890 Returns the linkrev node for the specified manifest.
865
891
866 SIDE EFFECT:
892 SIDE EFFECT:
867
893
868 1) fclnodes gets populated with the list of relevant
894 1) fclnodes gets populated with the list of relevant
869 file nodes if we're not using fastpathlinkrev
895 file nodes if we're not using fastpathlinkrev
870 2) When treemanifests are in use, collects treemanifest nodes
896 2) When treemanifests are in use, collects treemanifest nodes
871 to send
897 to send
872
898
873 Note that this means manifests must be completely sent to
899 Note that this means manifests must be completely sent to
874 the client before you can trust the list of files and
900 the client before you can trust the list of files and
875 treemanifests to send.
901 treemanifests to send.
876 """
902 """
877 clnode = nodes[x]
903 clnode = nodes[x]
878 mdata = mfl.get(dir, x).readfast(shallow=True)
904 mdata = mfl.get(dir, x).readfast(shallow=True)
879 for p, n, fl in mdata.iterentries():
905 for p, n, fl in mdata.iterentries():
880 if fl == 't': # subdirectory manifest
906 if fl == 't': # subdirectory manifest
881 subdir = dir + p + '/'
907 subdir = dir + p + '/'
882 tmfclnodes = tmfnodes.setdefault(subdir, {})
908 tmfclnodes = tmfnodes.setdefault(subdir, {})
883 tmfclnode = tmfclnodes.setdefault(n, clnode)
909 tmfclnode = tmfclnodes.setdefault(n, clnode)
884 if clrevorder[clnode] < clrevorder[tmfclnode]:
910 if clrevorder[clnode] < clrevorder[tmfclnode]:
885 tmfclnodes[n] = clnode
911 tmfclnodes[n] = clnode
886 else:
912 else:
887 f = dir + p
913 f = dir + p
888 fclnodes = fnodes.setdefault(f, {})
914 fclnodes = fnodes.setdefault(f, {})
889 fclnode = fclnodes.setdefault(n, clnode)
915 fclnode = fclnodes.setdefault(n, clnode)
890 if clrevorder[clnode] < clrevorder[fclnode]:
916 if clrevorder[clnode] < clrevorder[fclnode]:
891 fclnodes[n] = clnode
917 fclnodes[n] = clnode
892 return clnode
918 return clnode
893 return lookupmflinknode
919 return lookupmflinknode
894
920
895 size = 0
921 size = 0
896 while tmfnodes:
922 while tmfnodes:
897 dir, nodes = tmfnodes.popitem()
923 dir, nodes = tmfnodes.popitem()
898 prunednodes = self._prune(dirlog(dir), nodes, commonrevs)
924 prunednodes = self._prune(dirlog(dir), nodes, commonrevs)
899 if not dir or prunednodes:
925 if not dir or prunednodes:
900 for x in self._packmanifests(dir, prunednodes,
926 for x in self._packmanifests(dir, prunednodes,
901 makelookupmflinknode(dir, nodes)):
927 makelookupmflinknode(dir, nodes)):
902 size += len(x)
928 size += len(x)
903 yield x
929 yield x
904 self._verbosenote(_('%8.i (manifests)\n') % size)
930 self._verbosenote(_('%8.i (manifests)\n') % size)
905 yield self._manifestsend
931 yield self._manifestsend
906
932
907 # The 'source' parameter is useful for extensions
933 # The 'source' parameter is useful for extensions
908 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
934 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
909 changedfiles = list(filter(self._filematcher, changedfiles))
935 changedfiles = list(filter(self._filematcher, changedfiles))
910
936
911 if self._isshallow:
937 if self._isshallow:
912 # See comment in generate() for why this sadness is a thing.
938 # See comment in generate() for why this sadness is a thing.
913 mfdicts = self._mfdicts
939 mfdicts = self._mfdicts
914 del self._mfdicts
940 del self._mfdicts
915 # In a shallow clone, the linknodes callback needs to also include
941 # In a shallow clone, the linknodes callback needs to also include
916 # those file nodes that are in the manifests we sent but weren't
942 # those file nodes that are in the manifests we sent but weren't
917 # introduced by those manifests.
943 # introduced by those manifests.
918 commonctxs = [self._repo[c] for c in commonrevs]
944 commonctxs = [self._repo[c] for c in commonrevs]
919 oldlinknodes = linknodes
945 oldlinknodes = linknodes
920 clrev = self._repo.changelog.rev
946 clrev = self._repo.changelog.rev
921
947
922 # Defining this function has a side-effect of overriding the
948 # Defining this function has a side-effect of overriding the
923 # function of the same name that was passed in as an argument.
949 # function of the same name that was passed in as an argument.
924 # TODO have caller pass in appropriate function.
950 # TODO have caller pass in appropriate function.
925 def linknodes(flog, fname):
951 def linknodes(flog, fname):
926 for c in commonctxs:
952 for c in commonctxs:
927 try:
953 try:
928 fnode = c.filenode(fname)
954 fnode = c.filenode(fname)
929 self._clrevtolocalrev[c.rev()] = flog.rev(fnode)
955 self._clrevtolocalrev[c.rev()] = flog.rev(fnode)
930 except error.ManifestLookupError:
956 except error.ManifestLookupError:
931 pass
957 pass
932 links = oldlinknodes(flog, fname)
958 links = oldlinknodes(flog, fname)
933 if len(links) != len(mfdicts):
959 if len(links) != len(mfdicts):
934 for mf, lr in mfdicts:
960 for mf, lr in mfdicts:
935 fnode = mf.get(fname, None)
961 fnode = mf.get(fname, None)
936 if fnode in links:
962 if fnode in links:
937 links[fnode] = min(links[fnode], lr, key=clrev)
963 links[fnode] = min(links[fnode], lr, key=clrev)
938 elif fnode:
964 elif fnode:
939 links[fnode] = lr
965 links[fnode] = lr
940 return links
966 return links
941
967
942 return self._generatefiles(changedfiles, linknodes, commonrevs, source)
968 return self._generatefiles(changedfiles, linknodes, commonrevs, source)
943
969
944 def _generatefiles(self, changedfiles, linknodes, commonrevs, source):
970 def _generatefiles(self, changedfiles, linknodes, commonrevs, source):
945 repo = self._repo
971 repo = self._repo
946 progress = repo.ui.makeprogress(_('bundling'), unit=_('files'),
972 progress = repo.ui.makeprogress(_('bundling'), unit=_('files'),
947 total=len(changedfiles))
973 total=len(changedfiles))
948 for i, fname in enumerate(sorted(changedfiles)):
974 for i, fname in enumerate(sorted(changedfiles)):
949 filerevlog = repo.file(fname)
975 filerevlog = repo.file(fname)
950 if not filerevlog:
976 if not filerevlog:
951 raise error.Abort(_("empty or missing file data for %s") %
977 raise error.Abort(_("empty or missing file data for %s") %
952 fname)
978 fname)
953
979
954 linkrevnodes = linknodes(filerevlog, fname)
980 linkrevnodes = linknodes(filerevlog, fname)
955 # Lookup for filenodes, we collected the linkrev nodes above in the
981 # Lookup for filenodes, we collected the linkrev nodes above in the
956 # fastpath case and with lookupmf in the slowpath case.
982 # fastpath case and with lookupmf in the slowpath case.
957 def lookupfilelog(x):
983 def lookupfilelog(x):
958 return linkrevnodes[x]
984 return linkrevnodes[x]
959
985
960 filenodes = self._prune(filerevlog, linkrevnodes, commonrevs)
986 filenodes = self._prune(filerevlog, linkrevnodes, commonrevs)
961 if filenodes:
987 if filenodes:
962 progress.update(i + 1, item=fname)
988 progress.update(i + 1, item=fname)
963 h = self._fileheader(fname)
989 h = self._fileheader(fname)
964 size = len(h)
990 size = len(h)
965 yield h
991 yield h
966 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
992 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
967 size += len(chunk)
993 size += len(chunk)
968 yield chunk
994 yield chunk
969 self._verbosenote(_('%8.i %s\n') % (size, fname))
995 self._verbosenote(_('%8.i %s\n') % (size, fname))
970 progress.complete()
996 progress.complete()
971
997
972 def _revchunk(self, store, rev, prev, linknode):
998 def _revchunk(self, store, rev, prev, linknode):
973 if self._ellipses:
999 if self._ellipses:
974 fn = self._revisiondeltanarrow
1000 fn = self._revisiondeltanarrow
975 else:
1001 else:
976 fn = self._revisiondeltanormal
1002 fn = self._revisiondeltanormal
977
1003
978 delta = fn(store, rev, prev, linknode)
1004 delta = fn(store, rev, prev, linknode)
979 if not delta:
1005 if not delta:
980 return
1006 return
981
1007
982 meta = self._builddeltaheader(delta)
1008 meta = self._builddeltaheader(delta)
983 l = len(meta) + sum(len(x) for x in delta.deltachunks)
1009 l = len(meta) + sum(len(x) for x in delta.deltachunks)
984
1010
985 yield chunkheader(l)
1011 yield chunkheader(l)
986 yield meta
1012 yield meta
987 for x in delta.deltachunks:
1013 for x in delta.deltachunks:
988 yield x
1014 yield x
989
1015
990 def _revisiondeltanormal(self, store, rev, prev, linknode):
1016 def _revisiondeltanormal(self, store, rev, prev, linknode):
991 node = store.node(rev)
1017 node = store.node(rev)
992 p1, p2 = store.parentrevs(rev)
1018 p1, p2 = store.parentrevs(rev)
993 base = self._deltaparentfn(store, rev, p1, p2, prev)
1019 base = self._deltaparentfn(store, rev, p1, p2, prev)
994
1020
995 prefix = ''
1021 prefix = ''
996 if store.iscensored(base) or store.iscensored(rev):
1022 if store.iscensored(base) or store.iscensored(rev):
997 try:
1023 try:
998 delta = store.revision(node, raw=True)
1024 delta = store.revision(node, raw=True)
999 except error.CensoredNodeError as e:
1025 except error.CensoredNodeError as e:
1000 delta = e.tombstone
1026 delta = e.tombstone
1001 if base == nullrev:
1027 if base == nullrev:
1002 prefix = mdiff.trivialdiffheader(len(delta))
1028 prefix = mdiff.trivialdiffheader(len(delta))
1003 else:
1029 else:
1004 baselen = store.rawsize(base)
1030 baselen = store.rawsize(base)
1005 prefix = mdiff.replacediffheader(baselen, len(delta))
1031 prefix = mdiff.replacediffheader(baselen, len(delta))
1006 elif base == nullrev:
1032 elif base == nullrev:
1007 delta = store.revision(node, raw=True)
1033 delta = store.revision(node, raw=True)
1008 prefix = mdiff.trivialdiffheader(len(delta))
1034 prefix = mdiff.trivialdiffheader(len(delta))
1009 else:
1035 else:
1010 delta = store.revdiff(base, rev)
1036 delta = store.revdiff(base, rev)
1011 p1n, p2n = store.parents(node)
1037 p1n, p2n = store.parents(node)
1012
1038
1013 return revisiondelta(
1039 return revisiondelta(
1014 node=node,
1040 node=node,
1015 p1node=p1n,
1041 p1node=p1n,
1016 p2node=p2n,
1042 p2node=p2n,
1017 basenode=store.node(base),
1043 basenode=store.node(base),
1018 linknode=linknode,
1044 linknode=linknode,
1019 flags=store.flags(rev),
1045 flags=store.flags(rev),
1020 deltachunks=(prefix, delta),
1046 deltachunks=(prefix, delta),
1021 )
1047 )
1022
1048
1023 def _revisiondeltanarrow(self, store, rev, prev, linknode):
1049 def _revisiondeltanarrow(self, store, rev, prev, linknode):
1024 # build up some mapping information that's useful later. See
1050 # build up some mapping information that's useful later. See
1025 # the local() nested function below.
1051 # the local() nested function below.
1026 if not self._changelogdone:
1052 if not self._changelogdone:
1027 self._clnodetorev[linknode] = rev
1053 self._clnodetorev[linknode] = rev
1028 linkrev = rev
1054 linkrev = rev
1029 self._clrevtolocalrev[linkrev] = rev
1055 self._clrevtolocalrev[linkrev] = rev
1030 else:
1056 else:
1031 linkrev = self._clnodetorev[linknode]
1057 linkrev = self._clnodetorev[linknode]
1032 self._clrevtolocalrev[linkrev] = rev
1058 self._clrevtolocalrev[linkrev] = rev
1033
1059
1034 # This is a node to send in full, because the changeset it
1060 # This is a node to send in full, because the changeset it
1035 # corresponds to was a full changeset.
1061 # corresponds to was a full changeset.
1036 if linknode in self._fullnodes:
1062 if linknode in self._fullnodes:
1037 return self._revisiondeltanormal(store, rev, prev, linknode)
1063 return self._revisiondeltanormal(store, rev, prev, linknode)
1038
1064
1039 # At this point, a node can either be one we should skip or an
1065 # At this point, a node can either be one we should skip or an
1040 # ellipsis. If it's not an ellipsis, bail immediately.
1066 # ellipsis. If it's not an ellipsis, bail immediately.
1041 if linkrev not in self._precomputedellipsis:
1067 if linkrev not in self._precomputedellipsis:
1042 return
1068 return
1043
1069
1044 linkparents = self._precomputedellipsis[linkrev]
1070 linkparents = self._precomputedellipsis[linkrev]
1045 def local(clrev):
1071 def local(clrev):
1046 """Turn a changelog revnum into a local revnum.
1072 """Turn a changelog revnum into a local revnum.
1047
1073
1048 The ellipsis dag is stored as revnums on the changelog,
1074 The ellipsis dag is stored as revnums on the changelog,
1049 but when we're producing ellipsis entries for
1075 but when we're producing ellipsis entries for
1050 non-changelog revlogs, we need to turn those numbers into
1076 non-changelog revlogs, we need to turn those numbers into
1051 something local. This does that for us, and during the
1077 something local. This does that for us, and during the
1052 changelog sending phase will also expand the stored
1078 changelog sending phase will also expand the stored
1053 mappings as needed.
1079 mappings as needed.
1054 """
1080 """
1055 if clrev == nullrev:
1081 if clrev == nullrev:
1056 return nullrev
1082 return nullrev
1057
1083
1058 if not self._changelogdone:
1084 if not self._changelogdone:
1059 # If we're doing the changelog, it's possible that we
1085 # If we're doing the changelog, it's possible that we
1060 # have a parent that is already on the client, and we
1086 # have a parent that is already on the client, and we
1061 # need to store some extra mapping information so that
1087 # need to store some extra mapping information so that
1062 # our contained ellipsis nodes will be able to resolve
1088 # our contained ellipsis nodes will be able to resolve
1063 # their parents.
1089 # their parents.
1064 if clrev not in self._clrevtolocalrev:
1090 if clrev not in self._clrevtolocalrev:
1065 clnode = store.node(clrev)
1091 clnode = store.node(clrev)
1066 self._clnodetorev[clnode] = clrev
1092 self._clnodetorev[clnode] = clrev
1067 return clrev
1093 return clrev
1068
1094
1069 # Walk the ellipsis-ized changelog breadth-first looking for a
1095 # Walk the ellipsis-ized changelog breadth-first looking for a
1070 # change that has been linked from the current revlog.
1096 # change that has been linked from the current revlog.
1071 #
1097 #
1072 # For a flat manifest revlog only a single step should be necessary
1098 # For a flat manifest revlog only a single step should be necessary
1073 # as all relevant changelog entries are relevant to the flat
1099 # as all relevant changelog entries are relevant to the flat
1074 # manifest.
1100 # manifest.
1075 #
1101 #
1076 # For a filelog or tree manifest dirlog however not every changelog
1102 # For a filelog or tree manifest dirlog however not every changelog
1077 # entry will have been relevant, so we need to skip some changelog
1103 # entry will have been relevant, so we need to skip some changelog
1078 # nodes even after ellipsis-izing.
1104 # nodes even after ellipsis-izing.
1079 walk = [clrev]
1105 walk = [clrev]
1080 while walk:
1106 while walk:
1081 p = walk[0]
1107 p = walk[0]
1082 walk = walk[1:]
1108 walk = walk[1:]
1083 if p in self._clrevtolocalrev:
1109 if p in self._clrevtolocalrev:
1084 return self._clrevtolocalrev[p]
1110 return self._clrevtolocalrev[p]
1085 elif p in self._fullnodes:
1111 elif p in self._fullnodes:
1086 walk.extend([pp for pp in self._repo.changelog.parentrevs(p)
1112 walk.extend([pp for pp in self._repo.changelog.parentrevs(p)
1087 if pp != nullrev])
1113 if pp != nullrev])
1088 elif p in self._precomputedellipsis:
1114 elif p in self._precomputedellipsis:
1089 walk.extend([pp for pp in self._precomputedellipsis[p]
1115 walk.extend([pp for pp in self._precomputedellipsis[p]
1090 if pp != nullrev])
1116 if pp != nullrev])
1091 else:
1117 else:
1092 # In this case, we've got an ellipsis with parents
1118 # In this case, we've got an ellipsis with parents
1093 # outside the current bundle (likely an
1119 # outside the current bundle (likely an
1094 # incremental pull). We "know" that we can use the
1120 # incremental pull). We "know" that we can use the
1095 # value of this same revlog at whatever revision
1121 # value of this same revlog at whatever revision
1096 # is pointed to by linknode. "Know" is in scare
1122 # is pointed to by linknode. "Know" is in scare
1097 # quotes because I haven't done enough examination
1123 # quotes because I haven't done enough examination
1098 # of edge cases to convince myself this is really
1124 # of edge cases to convince myself this is really
1099 # a fact - it works for all the (admittedly
1125 # a fact - it works for all the (admittedly
1100 # thorough) cases in our testsuite, but I would be
1126 # thorough) cases in our testsuite, but I would be
1101 # somewhat unsurprised to find a case in the wild
1127 # somewhat unsurprised to find a case in the wild
1102 # where this breaks down a bit. That said, I don't
1128 # where this breaks down a bit. That said, I don't
1103 # know if it would hurt anything.
1129 # know if it would hurt anything.
1104 for i in pycompat.xrange(rev, 0, -1):
1130 for i in pycompat.xrange(rev, 0, -1):
1105 if store.linkrev(i) == clrev:
1131 if store.linkrev(i) == clrev:
1106 return i
1132 return i
1107 # We failed to resolve a parent for this node, so
1133 # We failed to resolve a parent for this node, so
1108 # we crash the changegroup construction.
1134 # we crash the changegroup construction.
1109 raise error.Abort(
1135 raise error.Abort(
1110 'unable to resolve parent while packing %r %r'
1136 'unable to resolve parent while packing %r %r'
1111 ' for changeset %r' % (store.indexfile, rev, clrev))
1137 ' for changeset %r' % (store.indexfile, rev, clrev))
1112
1138
1113 return nullrev
1139 return nullrev
1114
1140
1115 if not linkparents or (
1141 if not linkparents or (
1116 store.parentrevs(rev) == (nullrev, nullrev)):
1142 store.parentrevs(rev) == (nullrev, nullrev)):
1117 p1, p2 = nullrev, nullrev
1143 p1, p2 = nullrev, nullrev
1118 elif len(linkparents) == 1:
1144 elif len(linkparents) == 1:
1119 p1, = sorted(local(p) for p in linkparents)
1145 p1, = sorted(local(p) for p in linkparents)
1120 p2 = nullrev
1146 p2 = nullrev
1121 else:
1147 else:
1122 p1, p2 = sorted(local(p) for p in linkparents)
1148 p1, p2 = sorted(local(p) for p in linkparents)
1123
1149
1124 n = store.node(rev)
1150 n = store.node(rev)
1125 p1n, p2n = store.node(p1), store.node(p2)
1151 p1n, p2n = store.node(p1), store.node(p2)
1126 flags = store.flags(rev)
1152 flags = store.flags(rev)
1127 flags |= revlog.REVIDX_ELLIPSIS
1153 flags |= revlog.REVIDX_ELLIPSIS
1128
1154
1129 # TODO: try and actually send deltas for ellipsis data blocks
1155 # TODO: try and actually send deltas for ellipsis data blocks
1130 data = store.revision(n)
1156 data = store.revision(n)
1131 diffheader = mdiff.trivialdiffheader(len(data))
1157 diffheader = mdiff.trivialdiffheader(len(data))
1132
1158
1133 return revisiondelta(
1159 return revisiondelta(
1134 node=n,
1160 node=n,
1135 p1node=p1n,
1161 p1node=p1n,
1136 p2node=p2n,
1162 p2node=p2n,
1137 basenode=nullid,
1163 basenode=nullid,
1138 linknode=linknode,
1164 linknode=linknode,
1139 flags=flags,
1165 flags=flags,
1140 deltachunks=(diffheader, data),
1166 deltachunks=(diffheader, data),
1141 )
1167 )
1142
1168
1143 def _deltaparentprev(store, rev, p1, p2, prev):
1169 def _deltaparentprev(store, rev, p1, p2, prev):
1144 """Resolve a delta parent to the previous revision.
1170 """Resolve a delta parent to the previous revision.
1145
1171
1146 Used for version 1 changegroups, which don't support generaldelta.
1172 Used for version 1 changegroups, which don't support generaldelta.
1147 """
1173 """
1148 return prev
1174 return prev
1149
1175
1150 def _deltaparentgeneraldelta(store, rev, p1, p2, prev):
1176 def _deltaparentgeneraldelta(store, rev, p1, p2, prev):
1151 """Resolve a delta parent when general deltas are supported."""
1177 """Resolve a delta parent when general deltas are supported."""
1152 dp = store.deltaparent(rev)
1178 dp = store.deltaparent(rev)
1153 if dp == nullrev and store.storedeltachains:
1179 if dp == nullrev and store.storedeltachains:
1154 # Avoid sending full revisions when delta parent is null. Pick prev
1180 # Avoid sending full revisions when delta parent is null. Pick prev
1155 # in that case. It's tempting to pick p1 in this case, as p1 will
1181 # in that case. It's tempting to pick p1 in this case, as p1 will
1156 # be smaller in the common case. However, computing a delta against
1182 # be smaller in the common case. However, computing a delta against
1157 # p1 may require resolving the raw text of p1, which could be
1183 # p1 may require resolving the raw text of p1, which could be
1158 # expensive. The revlog caches should have prev cached, meaning
1184 # expensive. The revlog caches should have prev cached, meaning
1159 # less CPU for changegroup generation. There is likely room to add
1185 # less CPU for changegroup generation. There is likely room to add
1160 # a flag and/or config option to control this behavior.
1186 # a flag and/or config option to control this behavior.
1161 base = prev
1187 base = prev
1162 elif dp == nullrev:
1188 elif dp == nullrev:
1163 # revlog is configured to use full snapshot for a reason,
1189 # revlog is configured to use full snapshot for a reason,
1164 # stick to full snapshot.
1190 # stick to full snapshot.
1165 base = nullrev
1191 base = nullrev
1166 elif dp not in (p1, p2, prev):
1192 elif dp not in (p1, p2, prev):
1167 # Pick prev when we can't be sure remote has the base revision.
1193 # Pick prev when we can't be sure remote has the base revision.
1168 return prev
1194 return prev
1169 else:
1195 else:
1170 base = dp
1196 base = dp
1171
1197
1172 if base != nullrev and not store.candelta(base, rev):
1198 if base != nullrev and not store.candelta(base, rev):
1173 base = nullrev
1199 base = nullrev
1174
1200
1175 return base
1201 return base
1176
1202
1177 def _deltaparentellipses(store, rev, p1, p2, prev):
1203 def _deltaparentellipses(store, rev, p1, p2, prev):
1178 """Resolve a delta parent when in ellipses mode."""
1204 """Resolve a delta parent when in ellipses mode."""
1179 # TODO: send better deltas when in narrow mode.
1205 # TODO: send better deltas when in narrow mode.
1180 #
1206 #
1181 # changegroup.group() loops over revisions to send,
1207 # changegroup.group() loops over revisions to send,
1182 # including revisions we'll skip. What this means is that
1208 # including revisions we'll skip. What this means is that
1183 # `prev` will be a potentially useless delta base for all
1209 # `prev` will be a potentially useless delta base for all
1184 # ellipsis nodes, as the client likely won't have it. In
1210 # ellipsis nodes, as the client likely won't have it. In
1185 # the future we should do bookkeeping about which nodes
1211 # the future we should do bookkeeping about which nodes
1186 # have been sent to the client, and try to be
1212 # have been sent to the client, and try to be
1187 # significantly smarter about delta bases. This is
1213 # significantly smarter about delta bases. This is
1188 # slightly tricky because this same code has to work for
1214 # slightly tricky because this same code has to work for
1189 # all revlogs, and we don't have the linkrev/linknode here.
1215 # all revlogs, and we don't have the linkrev/linknode here.
1190 return p1
1216 return p1
1191
1217
1192 def _makecg1packer(repo, filematcher, bundlecaps, ellipses=False,
1218 def _makecg1packer(repo, filematcher, bundlecaps, ellipses=False,
1193 shallow=False, ellipsisroots=None, fullnodes=None):
1219 shallow=False, ellipsisroots=None, fullnodes=None):
1194 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1220 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1195 d.node, d.p1node, d.p2node, d.linknode)
1221 d.node, d.p1node, d.p2node, d.linknode)
1196
1222
1197 return cgpacker(repo, filematcher, b'01',
1223 return cgpacker(repo, filematcher, b'01',
1198 deltaparentfn=_deltaparentprev,
1224 deltaparentfn=_deltaparentprev,
1199 allowreorder=None,
1225 allowreorder=None,
1200 builddeltaheader=builddeltaheader,
1226 builddeltaheader=builddeltaheader,
1201 manifestsend=b'',
1227 manifestsend=b'',
1202 bundlecaps=bundlecaps,
1228 bundlecaps=bundlecaps,
1203 ellipses=ellipses,
1229 ellipses=ellipses,
1204 shallow=shallow,
1230 shallow=shallow,
1205 ellipsisroots=ellipsisroots,
1231 ellipsisroots=ellipsisroots,
1206 fullnodes=fullnodes)
1232 fullnodes=fullnodes)
1207
1233
1208 def _makecg2packer(repo, filematcher, bundlecaps, ellipses=False,
1234 def _makecg2packer(repo, filematcher, bundlecaps, ellipses=False,
1209 shallow=False, ellipsisroots=None, fullnodes=None):
1235 shallow=False, ellipsisroots=None, fullnodes=None):
1210 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1236 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1211 d.node, d.p1node, d.p2node, d.basenode, d.linknode)
1237 d.node, d.p1node, d.p2node, d.basenode, d.linknode)
1212
1238
1213 # Since generaldelta is directly supported by cg2, reordering
1239 # Since generaldelta is directly supported by cg2, reordering
1214 # generally doesn't help, so we disable it by default (treating
1240 # generally doesn't help, so we disable it by default (treating
1215 # bundle.reorder=auto just like bundle.reorder=False).
1241 # bundle.reorder=auto just like bundle.reorder=False).
1216 return cgpacker(repo, filematcher, b'02',
1242 return cgpacker(repo, filematcher, b'02',
1217 deltaparentfn=_deltaparentgeneraldelta,
1243 deltaparentfn=_deltaparentgeneraldelta,
1218 allowreorder=False,
1244 allowreorder=False,
1219 builddeltaheader=builddeltaheader,
1245 builddeltaheader=builddeltaheader,
1220 manifestsend=b'',
1246 manifestsend=b'',
1221 bundlecaps=bundlecaps,
1247 bundlecaps=bundlecaps,
1222 ellipses=ellipses,
1248 ellipses=ellipses,
1223 shallow=shallow,
1249 shallow=shallow,
1224 ellipsisroots=ellipsisroots,
1250 ellipsisroots=ellipsisroots,
1225 fullnodes=fullnodes)
1251 fullnodes=fullnodes)
1226
1252
1227 def _makecg3packer(repo, filematcher, bundlecaps, ellipses=False,
1253 def _makecg3packer(repo, filematcher, bundlecaps, ellipses=False,
1228 shallow=False, ellipsisroots=None, fullnodes=None):
1254 shallow=False, ellipsisroots=None, fullnodes=None):
1229 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1255 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1230 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1256 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1231
1257
1232 deltaparentfn = (_deltaparentellipses if ellipses
1258 deltaparentfn = (_deltaparentellipses if ellipses
1233 else _deltaparentgeneraldelta)
1259 else _deltaparentgeneraldelta)
1234
1260
1235 return cgpacker(repo, filematcher, b'03',
1261 return cgpacker(repo, filematcher, b'03',
1236 deltaparentfn=deltaparentfn,
1262 deltaparentfn=deltaparentfn,
1237 allowreorder=False,
1263 allowreorder=False,
1238 builddeltaheader=builddeltaheader,
1264 builddeltaheader=builddeltaheader,
1239 manifestsend=closechunk(),
1265 manifestsend=closechunk(),
1240 bundlecaps=bundlecaps,
1266 bundlecaps=bundlecaps,
1241 ellipses=ellipses,
1267 ellipses=ellipses,
1242 shallow=shallow,
1268 shallow=shallow,
1243 ellipsisroots=ellipsisroots,
1269 ellipsisroots=ellipsisroots,
1244 fullnodes=fullnodes)
1270 fullnodes=fullnodes)
1245
1271
1246 _packermap = {'01': (_makecg1packer, cg1unpacker),
1272 _packermap = {'01': (_makecg1packer, cg1unpacker),
1247 # cg2 adds support for exchanging generaldelta
1273 # cg2 adds support for exchanging generaldelta
1248 '02': (_makecg2packer, cg2unpacker),
1274 '02': (_makecg2packer, cg2unpacker),
1249 # cg3 adds support for exchanging revlog flags and treemanifests
1275 # cg3 adds support for exchanging revlog flags and treemanifests
1250 '03': (_makecg3packer, cg3unpacker),
1276 '03': (_makecg3packer, cg3unpacker),
1251 }
1277 }
1252
1278
1253 def allsupportedversions(repo):
1279 def allsupportedversions(repo):
1254 versions = set(_packermap.keys())
1280 versions = set(_packermap.keys())
1255 if not (repo.ui.configbool('experimental', 'changegroup3') or
1281 if not (repo.ui.configbool('experimental', 'changegroup3') or
1256 repo.ui.configbool('experimental', 'treemanifest') or
1282 repo.ui.configbool('experimental', 'treemanifest') or
1257 'treemanifest' in repo.requirements):
1283 'treemanifest' in repo.requirements):
1258 versions.discard('03')
1284 versions.discard('03')
1259 return versions
1285 return versions
1260
1286
1261 # Changegroup versions that can be applied to the repo
1287 # Changegroup versions that can be applied to the repo
1262 def supportedincomingversions(repo):
1288 def supportedincomingversions(repo):
1263 return allsupportedversions(repo)
1289 return allsupportedversions(repo)
1264
1290
1265 # Changegroup versions that can be created from the repo
1291 # Changegroup versions that can be created from the repo
1266 def supportedoutgoingversions(repo):
1292 def supportedoutgoingversions(repo):
1267 versions = allsupportedversions(repo)
1293 versions = allsupportedversions(repo)
1268 if 'treemanifest' in repo.requirements:
1294 if 'treemanifest' in repo.requirements:
1269 # Versions 01 and 02 support only flat manifests and it's just too
1295 # Versions 01 and 02 support only flat manifests and it's just too
1270 # expensive to convert between the flat manifest and tree manifest on
1296 # expensive to convert between the flat manifest and tree manifest on
1271 # the fly. Since tree manifests are hashed differently, all of history
1297 # the fly. Since tree manifests are hashed differently, all of history
1272 # would have to be converted. Instead, we simply don't even pretend to
1298 # would have to be converted. Instead, we simply don't even pretend to
1273 # support versions 01 and 02.
1299 # support versions 01 and 02.
1274 versions.discard('01')
1300 versions.discard('01')
1275 versions.discard('02')
1301 versions.discard('02')
1276 if repository.NARROW_REQUIREMENT in repo.requirements:
1302 if repository.NARROW_REQUIREMENT in repo.requirements:
1277 # Versions 01 and 02 don't support revlog flags, and we need to
1303 # Versions 01 and 02 don't support revlog flags, and we need to
1278 # support that for stripping and unbundling to work.
1304 # support that for stripping and unbundling to work.
1279 versions.discard('01')
1305 versions.discard('01')
1280 versions.discard('02')
1306 versions.discard('02')
1281 if LFS_REQUIREMENT in repo.requirements:
1307 if LFS_REQUIREMENT in repo.requirements:
1282 # Versions 01 and 02 don't support revlog flags, and we need to
1308 # Versions 01 and 02 don't support revlog flags, and we need to
1283 # mark LFS entries with REVIDX_EXTSTORED.
1309 # mark LFS entries with REVIDX_EXTSTORED.
1284 versions.discard('01')
1310 versions.discard('01')
1285 versions.discard('02')
1311 versions.discard('02')
1286
1312
1287 return versions
1313 return versions
1288
1314
1289 def localversion(repo):
1315 def localversion(repo):
1290 # Finds the best version to use for bundles that are meant to be used
1316 # Finds the best version to use for bundles that are meant to be used
1291 # locally, such as those from strip and shelve, and temporary bundles.
1317 # locally, such as those from strip and shelve, and temporary bundles.
1292 return max(supportedoutgoingversions(repo))
1318 return max(supportedoutgoingversions(repo))
1293
1319
1294 def safeversion(repo):
1320 def safeversion(repo):
1295 # Finds the smallest version that it's safe to assume clients of the repo
1321 # Finds the smallest version that it's safe to assume clients of the repo
1296 # will support. For example, all hg versions that support generaldelta also
1322 # will support. For example, all hg versions that support generaldelta also
1297 # support changegroup 02.
1323 # support changegroup 02.
1298 versions = supportedoutgoingversions(repo)
1324 versions = supportedoutgoingversions(repo)
1299 if 'generaldelta' in repo.requirements:
1325 if 'generaldelta' in repo.requirements:
1300 versions.discard('01')
1326 versions.discard('01')
1301 assert versions
1327 assert versions
1302 return min(versions)
1328 return min(versions)
1303
1329
1304 def getbundler(version, repo, bundlecaps=None, filematcher=None,
1330 def getbundler(version, repo, bundlecaps=None, filematcher=None,
1305 ellipses=False, shallow=False, ellipsisroots=None,
1331 ellipses=False, shallow=False, ellipsisroots=None,
1306 fullnodes=None):
1332 fullnodes=None):
1307 assert version in supportedoutgoingversions(repo)
1333 assert version in supportedoutgoingversions(repo)
1308
1334
1309 if filematcher is None:
1335 if filematcher is None:
1310 filematcher = matchmod.alwaysmatcher(repo.root, '')
1336 filematcher = matchmod.alwaysmatcher(repo.root, '')
1311
1337
1312 if version == '01' and not filematcher.always():
1338 if version == '01' and not filematcher.always():
1313 raise error.ProgrammingError('version 01 changegroups do not support '
1339 raise error.ProgrammingError('version 01 changegroups do not support '
1314 'sparse file matchers')
1340 'sparse file matchers')
1315
1341
1316 if ellipses and version in (b'01', b'02'):
1342 if ellipses and version in (b'01', b'02'):
1317 raise error.Abort(
1343 raise error.Abort(
1318 _('ellipsis nodes require at least cg3 on client and server, '
1344 _('ellipsis nodes require at least cg3 on client and server, '
1319 'but negotiated version %s') % version)
1345 'but negotiated version %s') % version)
1320
1346
1321 # Requested files could include files not in the local store. So
1347 # Requested files could include files not in the local store. So
1322 # filter those out.
1348 # filter those out.
1323 filematcher = matchmod.intersectmatchers(repo.narrowmatch(),
1349 filematcher = matchmod.intersectmatchers(repo.narrowmatch(),
1324 filematcher)
1350 filematcher)
1325
1351
1326 fn = _packermap[version][0]
1352 fn = _packermap[version][0]
1327 return fn(repo, filematcher, bundlecaps, ellipses=ellipses,
1353 return fn(repo, filematcher, bundlecaps, ellipses=ellipses,
1328 shallow=shallow, ellipsisroots=ellipsisroots,
1354 shallow=shallow, ellipsisroots=ellipsisroots,
1329 fullnodes=fullnodes)
1355 fullnodes=fullnodes)
1330
1356
1331 def getunbundler(version, fh, alg, extras=None):
1357 def getunbundler(version, fh, alg, extras=None):
1332 return _packermap[version][1](fh, alg, extras=extras)
1358 return _packermap[version][1](fh, alg, extras=extras)
1333
1359
1334 def _changegroupinfo(repo, nodes, source):
1360 def _changegroupinfo(repo, nodes, source):
1335 if repo.ui.verbose or source == 'bundle':
1361 if repo.ui.verbose or source == 'bundle':
1336 repo.ui.status(_("%d changesets found\n") % len(nodes))
1362 repo.ui.status(_("%d changesets found\n") % len(nodes))
1337 if repo.ui.debugflag:
1363 if repo.ui.debugflag:
1338 repo.ui.debug("list of changesets:\n")
1364 repo.ui.debug("list of changesets:\n")
1339 for node in nodes:
1365 for node in nodes:
1340 repo.ui.debug("%s\n" % hex(node))
1366 repo.ui.debug("%s\n" % hex(node))
1341
1367
1342 def makechangegroup(repo, outgoing, version, source, fastpath=False,
1368 def makechangegroup(repo, outgoing, version, source, fastpath=False,
1343 bundlecaps=None):
1369 bundlecaps=None):
1344 cgstream = makestream(repo, outgoing, version, source,
1370 cgstream = makestream(repo, outgoing, version, source,
1345 fastpath=fastpath, bundlecaps=bundlecaps)
1371 fastpath=fastpath, bundlecaps=bundlecaps)
1346 return getunbundler(version, util.chunkbuffer(cgstream), None,
1372 return getunbundler(version, util.chunkbuffer(cgstream), None,
1347 {'clcount': len(outgoing.missing) })
1373 {'clcount': len(outgoing.missing) })
1348
1374
1349 def makestream(repo, outgoing, version, source, fastpath=False,
1375 def makestream(repo, outgoing, version, source, fastpath=False,
1350 bundlecaps=None, filematcher=None):
1376 bundlecaps=None, filematcher=None):
1351 bundler = getbundler(version, repo, bundlecaps=bundlecaps,
1377 bundler = getbundler(version, repo, bundlecaps=bundlecaps,
1352 filematcher=filematcher)
1378 filematcher=filematcher)
1353
1379
1354 repo = repo.unfiltered()
1380 repo = repo.unfiltered()
1355 commonrevs = outgoing.common
1381 commonrevs = outgoing.common
1356 csets = outgoing.missing
1382 csets = outgoing.missing
1357 heads = outgoing.missingheads
1383 heads = outgoing.missingheads
1358 # We go through the fast path if we get told to, or if all (unfiltered
1384 # We go through the fast path if we get told to, or if all (unfiltered
1359 # heads have been requested (since we then know there all linkrevs will
1385 # heads have been requested (since we then know there all linkrevs will
1360 # be pulled by the client).
1386 # be pulled by the client).
1361 heads.sort()
1387 heads.sort()
1362 fastpathlinkrev = fastpath or (
1388 fastpathlinkrev = fastpath or (
1363 repo.filtername is None and heads == sorted(repo.heads()))
1389 repo.filtername is None and heads == sorted(repo.heads()))
1364
1390
1365 repo.hook('preoutgoing', throw=True, source=source)
1391 repo.hook('preoutgoing', throw=True, source=source)
1366 _changegroupinfo(repo, csets, source)
1392 _changegroupinfo(repo, csets, source)
1367 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1393 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1368
1394
1369 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
1395 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
1370 revisions = 0
1396 revisions = 0
1371 files = 0
1397 files = 0
1372 progress = repo.ui.makeprogress(_('files'), unit=_('files'),
1398 progress = repo.ui.makeprogress(_('files'), unit=_('files'),
1373 total=expectedfiles)
1399 total=expectedfiles)
1374 for chunkdata in iter(source.filelogheader, {}):
1400 for chunkdata in iter(source.filelogheader, {}):
1375 files += 1
1401 files += 1
1376 f = chunkdata["filename"]
1402 f = chunkdata["filename"]
1377 repo.ui.debug("adding %s revisions\n" % f)
1403 repo.ui.debug("adding %s revisions\n" % f)
1378 progress.increment()
1404 progress.increment()
1379 fl = repo.file(f)
1405 fl = repo.file(f)
1380 o = len(fl)
1406 o = len(fl)
1381 try:
1407 try:
1382 deltas = source.deltaiter()
1408 deltas = source.deltaiter()
1383 if not fl.addgroup(deltas, revmap, trp):
1409 if not fl.addgroup(deltas, revmap, trp):
1384 raise error.Abort(_("received file revlog group is empty"))
1410 raise error.Abort(_("received file revlog group is empty"))
1385 except error.CensoredBaseError as e:
1411 except error.CensoredBaseError as e:
1386 raise error.Abort(_("received delta base is censored: %s") % e)
1412 raise error.Abort(_("received delta base is censored: %s") % e)
1387 revisions += len(fl) - o
1413 revisions += len(fl) - o
1388 if f in needfiles:
1414 if f in needfiles:
1389 needs = needfiles[f]
1415 needs = needfiles[f]
1390 for new in pycompat.xrange(o, len(fl)):
1416 for new in pycompat.xrange(o, len(fl)):
1391 n = fl.node(new)
1417 n = fl.node(new)
1392 if n in needs:
1418 if n in needs:
1393 needs.remove(n)
1419 needs.remove(n)
1394 else:
1420 else:
1395 raise error.Abort(
1421 raise error.Abort(
1396 _("received spurious file revlog entry"))
1422 _("received spurious file revlog entry"))
1397 if not needs:
1423 if not needs:
1398 del needfiles[f]
1424 del needfiles[f]
1399 progress.complete()
1425 progress.complete()
1400
1426
1401 for f, needs in needfiles.iteritems():
1427 for f, needs in needfiles.iteritems():
1402 fl = repo.file(f)
1428 fl = repo.file(f)
1403 for n in needs:
1429 for n in needs:
1404 try:
1430 try:
1405 fl.rev(n)
1431 fl.rev(n)
1406 except error.LookupError:
1432 except error.LookupError:
1407 raise error.Abort(
1433 raise error.Abort(
1408 _('missing file data for %s:%s - run hg verify') %
1434 _('missing file data for %s:%s - run hg verify') %
1409 (f, hex(n)))
1435 (f, hex(n)))
1410
1436
1411 return revisions, files
1437 return revisions, files
General Comments 0
You need to be logged in to leave comments. Login now