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