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