##// END OF EJS Templates
changegroup: control reordering via constructor argument...
Gregory Szorc -
r38936:6e999a2d default
parent child Browse files
Show More
@@ -1,1388 +1,1387 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 cg1packer(object):
522 class cg1packer(object):
523 def __init__(self, repo, filematcher, version, builddeltaheader,
523 def __init__(self, repo, filematcher, version, allowreorder,
524 manifestsend, sendtreemanifests,
524 builddeltaheader, manifestsend, sendtreemanifests,
525 bundlecaps=None):
525 bundlecaps=None):
526 """Given a source repo, construct a bundler.
526 """Given a source repo, construct a bundler.
527
527
528 filematcher is a matcher that matches on files to include in the
528 filematcher is a matcher that matches on files to include in the
529 changegroup. Used to facilitate sparse changegroups.
529 changegroup. Used to facilitate sparse changegroups.
530
530
531 allowreorder controls whether reordering of revisions is allowed.
532 This value is used when ``bundle.reorder`` is ``auto`` or isn't
533 set.
534
531 builddeltaheader is a callable that constructs the header for a group
535 builddeltaheader is a callable that constructs the header for a group
532 delta.
536 delta.
533
537
534 manifestsend is a chunk to send after manifests have been fully emitted.
538 manifestsend is a chunk to send after manifests have been fully emitted.
535
539
536 sendtreemanifests indicates whether tree manifests should be emitted.
540 sendtreemanifests indicates whether tree manifests should be emitted.
537
541
538 bundlecaps is optional and can be used to specify the set of
542 bundlecaps is optional and can be used to specify the set of
539 capabilities which can be used to build the bundle. While bundlecaps is
543 capabilities which can be used to build the bundle. While bundlecaps is
540 unused in core Mercurial, extensions rely on this feature to communicate
544 unused in core Mercurial, extensions rely on this feature to communicate
541 capabilities to customize the changegroup packer.
545 capabilities to customize the changegroup packer.
542 """
546 """
543 assert filematcher
547 assert filematcher
544 self._filematcher = filematcher
548 self._filematcher = filematcher
545
549
546 self.version = version
550 self.version = version
547 self._builddeltaheader = builddeltaheader
551 self._builddeltaheader = builddeltaheader
548 self._manifestsend = manifestsend
552 self._manifestsend = manifestsend
549 self._sendtreemanifests = sendtreemanifests
553 self._sendtreemanifests = sendtreemanifests
550
554
551 # Set of capabilities we can use to build the bundle.
555 # Set of capabilities we can use to build the bundle.
552 if bundlecaps is None:
556 if bundlecaps is None:
553 bundlecaps = set()
557 bundlecaps = set()
554 self._bundlecaps = bundlecaps
558 self._bundlecaps = bundlecaps
559
555 # experimental config: bundle.reorder
560 # experimental config: bundle.reorder
556 reorder = repo.ui.config('bundle', 'reorder')
561 reorder = repo.ui.config('bundle', 'reorder')
557 if reorder == 'auto':
562 if reorder == 'auto':
558 reorder = None
563 self._reorder = allowreorder
559 else:
564 else:
560 reorder = stringutil.parsebool(reorder)
565 self._reorder = stringutil.parsebool(reorder)
566
561 self._repo = repo
567 self._repo = repo
562 self._reorder = reorder
568
563 if self._repo.ui.verbose and not self._repo.ui.debugflag:
569 if self._repo.ui.verbose and not self._repo.ui.debugflag:
564 self._verbosenote = self._repo.ui.note
570 self._verbosenote = self._repo.ui.note
565 else:
571 else:
566 self._verbosenote = lambda s: None
572 self._verbosenote = lambda s: None
567
573
568 def close(self):
574 def close(self):
569 # Ellipses serving mode.
575 # Ellipses serving mode.
570 getattr(self, 'clrev_to_localrev', {}).clear()
576 getattr(self, 'clrev_to_localrev', {}).clear()
571 if getattr(self, 'next_clrev_to_localrev', {}):
577 if getattr(self, 'next_clrev_to_localrev', {}):
572 self.clrev_to_localrev = self.next_clrev_to_localrev
578 self.clrev_to_localrev = self.next_clrev_to_localrev
573 del self.next_clrev_to_localrev
579 del self.next_clrev_to_localrev
574 self.changelog_done = True
580 self.changelog_done = True
575
581
576 return closechunk()
582 return closechunk()
577
583
578 def fileheader(self, fname):
584 def fileheader(self, fname):
579 return chunkheader(len(fname)) + fname
585 return chunkheader(len(fname)) + fname
580
586
581 # Extracted both for clarity and for overriding in extensions.
587 # Extracted both for clarity and for overriding in extensions.
582 def _sortgroup(self, store, nodelist, lookup):
588 def _sortgroup(self, store, nodelist, lookup):
583 """Sort nodes for change group and turn them into revnums."""
589 """Sort nodes for change group and turn them into revnums."""
584 # Ellipses serving mode.
590 # Ellipses serving mode.
585 #
591 #
586 # In a perfect world, we'd generate better ellipsis-ified graphs
592 # In a perfect world, we'd generate better ellipsis-ified graphs
587 # for non-changelog revlogs. In practice, we haven't started doing
593 # for non-changelog revlogs. In practice, we haven't started doing
588 # that yet, so the resulting DAGs for the manifestlog and filelogs
594 # that yet, so the resulting DAGs for the manifestlog and filelogs
589 # are actually full of bogus parentage on all the ellipsis
595 # are actually full of bogus parentage on all the ellipsis
590 # nodes. This has the side effect that, while the contents are
596 # nodes. This has the side effect that, while the contents are
591 # correct, the individual DAGs might be completely out of whack in
597 # correct, the individual DAGs might be completely out of whack in
592 # a case like 882681bc3166 and its ancestors (back about 10
598 # a case like 882681bc3166 and its ancestors (back about 10
593 # revisions or so) in the main hg repo.
599 # revisions or so) in the main hg repo.
594 #
600 #
595 # The one invariant we *know* holds is that the new (potentially
601 # The one invariant we *know* holds is that the new (potentially
596 # bogus) DAG shape will be valid if we order the nodes in the
602 # bogus) DAG shape will be valid if we order the nodes in the
597 # order that they're introduced in dramatis personae by the
603 # order that they're introduced in dramatis personae by the
598 # changelog, so what we do is we sort the non-changelog histories
604 # changelog, so what we do is we sort the non-changelog histories
599 # by the order in which they are used by the changelog.
605 # by the order in which they are used by the changelog.
600 if util.safehasattr(self, 'full_nodes') and self.clnode_to_rev:
606 if util.safehasattr(self, 'full_nodes') and self.clnode_to_rev:
601 key = lambda n: self.clnode_to_rev[lookup(n)]
607 key = lambda n: self.clnode_to_rev[lookup(n)]
602 return [store.rev(n) for n in sorted(nodelist, key=key)]
608 return [store.rev(n) for n in sorted(nodelist, key=key)]
603
609
604 # for generaldelta revlogs, we linearize the revs; this will both be
610 # for generaldelta revlogs, we linearize the revs; this will both be
605 # much quicker and generate a much smaller bundle
611 # much quicker and generate a much smaller bundle
606 if (store._generaldelta and self._reorder is None) or self._reorder:
612 if (store._generaldelta and self._reorder is None) or self._reorder:
607 dag = dagutil.revlogdag(store)
613 dag = dagutil.revlogdag(store)
608 return dag.linearize(set(store.rev(n) for n in nodelist))
614 return dag.linearize(set(store.rev(n) for n in nodelist))
609 else:
615 else:
610 return sorted([store.rev(n) for n in nodelist])
616 return sorted([store.rev(n) for n in nodelist])
611
617
612 def group(self, nodelist, store, lookup, units=None):
618 def group(self, nodelist, store, lookup, units=None):
613 """Calculate a delta group, yielding a sequence of changegroup chunks
619 """Calculate a delta group, yielding a sequence of changegroup chunks
614 (strings).
620 (strings).
615
621
616 Given a list of changeset revs, return a set of deltas and
622 Given a list of changeset revs, return a set of deltas and
617 metadata corresponding to nodes. The first delta is
623 metadata corresponding to nodes. The first delta is
618 first parent(nodelist[0]) -> nodelist[0], the receiver is
624 first parent(nodelist[0]) -> nodelist[0], the receiver is
619 guaranteed to have this parent as it has all history before
625 guaranteed to have this parent as it has all history before
620 these changesets. In the case firstparent is nullrev the
626 these changesets. In the case firstparent is nullrev the
621 changegroup starts with a full revision.
627 changegroup starts with a full revision.
622
628
623 If units is not None, progress detail will be generated, units specifies
629 If units is not None, progress detail will be generated, units specifies
624 the type of revlog that is touched (changelog, manifest, etc.).
630 the type of revlog that is touched (changelog, manifest, etc.).
625 """
631 """
626 # if we don't have any revisions touched by these changesets, bail
632 # if we don't have any revisions touched by these changesets, bail
627 if len(nodelist) == 0:
633 if len(nodelist) == 0:
628 yield self.close()
634 yield self.close()
629 return
635 return
630
636
631 revs = self._sortgroup(store, nodelist, lookup)
637 revs = self._sortgroup(store, nodelist, lookup)
632
638
633 # add the parent of the first rev
639 # add the parent of the first rev
634 p = store.parentrevs(revs[0])[0]
640 p = store.parentrevs(revs[0])[0]
635 revs.insert(0, p)
641 revs.insert(0, p)
636
642
637 # build deltas
643 # build deltas
638 progress = None
644 progress = None
639 if units is not None:
645 if units is not None:
640 progress = self._repo.ui.makeprogress(_('bundling'), unit=units,
646 progress = self._repo.ui.makeprogress(_('bundling'), unit=units,
641 total=(len(revs) - 1))
647 total=(len(revs) - 1))
642 for r in pycompat.xrange(len(revs) - 1):
648 for r in pycompat.xrange(len(revs) - 1):
643 if progress:
649 if progress:
644 progress.update(r + 1)
650 progress.update(r + 1)
645 prev, curr = revs[r], revs[r + 1]
651 prev, curr = revs[r], revs[r + 1]
646 linknode = lookup(store.node(curr))
652 linknode = lookup(store.node(curr))
647 for c in self.revchunk(store, curr, prev, linknode):
653 for c in self.revchunk(store, curr, prev, linknode):
648 yield c
654 yield c
649
655
650 if progress:
656 if progress:
651 progress.complete()
657 progress.complete()
652 yield self.close()
658 yield self.close()
653
659
654 # filter any nodes that claim to be part of the known set
660 # filter any nodes that claim to be part of the known set
655 def prune(self, store, missing, commonrevs):
661 def prune(self, store, missing, commonrevs):
656 # TODO this violates storage abstraction for manifests.
662 # TODO this violates storage abstraction for manifests.
657 if isinstance(store, manifest.manifestrevlog):
663 if isinstance(store, manifest.manifestrevlog):
658 if not self._filematcher.visitdir(store._dir[:-1] or '.'):
664 if not self._filematcher.visitdir(store._dir[:-1] or '.'):
659 return []
665 return []
660
666
661 rr, rl = store.rev, store.linkrev
667 rr, rl = store.rev, store.linkrev
662 return [n for n in missing if rl(rr(n)) not in commonrevs]
668 return [n for n in missing if rl(rr(n)) not in commonrevs]
663
669
664 def _packmanifests(self, dir, mfnodes, lookuplinknode):
670 def _packmanifests(self, dir, mfnodes, lookuplinknode):
665 """Pack flat manifests into a changegroup stream."""
671 """Pack flat manifests into a changegroup stream."""
666 assert not dir
672 assert not dir
667 for chunk in self.group(mfnodes, self._repo.manifestlog._revlog,
673 for chunk in self.group(mfnodes, self._repo.manifestlog._revlog,
668 lookuplinknode, units=_('manifests')):
674 lookuplinknode, units=_('manifests')):
669 yield chunk
675 yield chunk
670
676
671 def _packtreemanifests(self, dir, mfnodes, lookuplinknode):
677 def _packtreemanifests(self, dir, mfnodes, lookuplinknode):
672 """Version of _packmanifests that operates on directory manifests.
678 """Version of _packmanifests that operates on directory manifests.
673
679
674 Encodes the directory name in the output so multiple manifests
680 Encodes the directory name in the output so multiple manifests
675 can be sent.
681 can be sent.
676 """
682 """
677 assert self.version == b'03'
683 assert self.version == b'03'
678
684
679 if dir:
685 if dir:
680 yield self.fileheader(dir)
686 yield self.fileheader(dir)
681
687
682 # TODO violates storage abstractions by assuming revlogs.
688 # TODO violates storage abstractions by assuming revlogs.
683 dirlog = self._repo.manifestlog._revlog.dirlog(dir)
689 dirlog = self._repo.manifestlog._revlog.dirlog(dir)
684 for chunk in self.group(mfnodes, dirlog, lookuplinknode,
690 for chunk in self.group(mfnodes, dirlog, lookuplinknode,
685 units=_('manifests')):
691 units=_('manifests')):
686 yield chunk
692 yield chunk
687
693
688 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
694 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
689 '''yield a sequence of changegroup chunks (strings)'''
695 '''yield a sequence of changegroup chunks (strings)'''
690 repo = self._repo
696 repo = self._repo
691 cl = repo.changelog
697 cl = repo.changelog
692
698
693 clrevorder = {}
699 clrevorder = {}
694 mfs = {} # needed manifests
700 mfs = {} # needed manifests
695 fnodes = {} # needed file nodes
701 fnodes = {} # needed file nodes
696 mfl = repo.manifestlog
702 mfl = repo.manifestlog
697 # TODO violates storage abstraction.
703 # TODO violates storage abstraction.
698 mfrevlog = mfl._revlog
704 mfrevlog = mfl._revlog
699 changedfiles = set()
705 changedfiles = set()
700
706
701 ellipsesmode = util.safehasattr(self, 'full_nodes')
707 ellipsesmode = util.safehasattr(self, 'full_nodes')
702
708
703 # Callback for the changelog, used to collect changed files and
709 # Callback for the changelog, used to collect changed files and
704 # manifest nodes.
710 # manifest nodes.
705 # Returns the linkrev node (identity in the changelog case).
711 # Returns the linkrev node (identity in the changelog case).
706 def lookupcl(x):
712 def lookupcl(x):
707 c = cl.read(x)
713 c = cl.read(x)
708 clrevorder[x] = len(clrevorder)
714 clrevorder[x] = len(clrevorder)
709
715
710 if ellipsesmode:
716 if ellipsesmode:
711 # Only update mfs if x is going to be sent. Otherwise we
717 # Only update mfs if x is going to be sent. Otherwise we
712 # end up with bogus linkrevs specified for manifests and
718 # end up with bogus linkrevs specified for manifests and
713 # we skip some manifest nodes that we should otherwise
719 # we skip some manifest nodes that we should otherwise
714 # have sent.
720 # have sent.
715 if (x in self.full_nodes
721 if (x in self.full_nodes
716 or cl.rev(x) in self.precomputed_ellipsis):
722 or cl.rev(x) in self.precomputed_ellipsis):
717 n = c[0]
723 n = c[0]
718 # Record the first changeset introducing this manifest
724 # Record the first changeset introducing this manifest
719 # version.
725 # version.
720 mfs.setdefault(n, x)
726 mfs.setdefault(n, x)
721 # Set this narrow-specific dict so we have the lowest
727 # Set this narrow-specific dict so we have the lowest
722 # manifest revnum to look up for this cl revnum. (Part of
728 # manifest revnum to look up for this cl revnum. (Part of
723 # mapping changelog ellipsis parents to manifest ellipsis
729 # mapping changelog ellipsis parents to manifest ellipsis
724 # parents)
730 # parents)
725 self.next_clrev_to_localrev.setdefault(cl.rev(x),
731 self.next_clrev_to_localrev.setdefault(cl.rev(x),
726 mfrevlog.rev(n))
732 mfrevlog.rev(n))
727 # We can't trust the changed files list in the changeset if the
733 # We can't trust the changed files list in the changeset if the
728 # client requested a shallow clone.
734 # client requested a shallow clone.
729 if self.is_shallow:
735 if self.is_shallow:
730 changedfiles.update(mfl[c[0]].read().keys())
736 changedfiles.update(mfl[c[0]].read().keys())
731 else:
737 else:
732 changedfiles.update(c[3])
738 changedfiles.update(c[3])
733 else:
739 else:
734
740
735 n = c[0]
741 n = c[0]
736 # record the first changeset introducing this manifest version
742 # record the first changeset introducing this manifest version
737 mfs.setdefault(n, x)
743 mfs.setdefault(n, x)
738 # Record a complete list of potentially-changed files in
744 # Record a complete list of potentially-changed files in
739 # this manifest.
745 # this manifest.
740 changedfiles.update(c[3])
746 changedfiles.update(c[3])
741
747
742 return x
748 return x
743
749
744 self._verbosenote(_('uncompressed size of bundle content:\n'))
750 self._verbosenote(_('uncompressed size of bundle content:\n'))
745 size = 0
751 size = 0
746 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
752 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
747 size += len(chunk)
753 size += len(chunk)
748 yield chunk
754 yield chunk
749 self._verbosenote(_('%8.i (changelog)\n') % size)
755 self._verbosenote(_('%8.i (changelog)\n') % size)
750
756
751 # We need to make sure that the linkrev in the changegroup refers to
757 # We need to make sure that the linkrev in the changegroup refers to
752 # the first changeset that introduced the manifest or file revision.
758 # the first changeset that introduced the manifest or file revision.
753 # The fastpath is usually safer than the slowpath, because the filelogs
759 # The fastpath is usually safer than the slowpath, because the filelogs
754 # are walked in revlog order.
760 # are walked in revlog order.
755 #
761 #
756 # When taking the slowpath with reorder=None and the manifest revlog
762 # When taking the slowpath with reorder=None and the manifest revlog
757 # uses generaldelta, the manifest may be walked in the "wrong" order.
763 # uses generaldelta, the manifest may be walked in the "wrong" order.
758 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
764 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
759 # cc0ff93d0c0c).
765 # cc0ff93d0c0c).
760 #
766 #
761 # When taking the fastpath, we are only vulnerable to reordering
767 # When taking the fastpath, we are only vulnerable to reordering
762 # of the changelog itself. The changelog never uses generaldelta, so
768 # of the changelog itself. The changelog never uses generaldelta, so
763 # it is only reordered when reorder=True. To handle this case, we
769 # it is only reordered when reorder=True. To handle this case, we
764 # simply take the slowpath, which already has the 'clrevorder' logic.
770 # simply take the slowpath, which already has the 'clrevorder' logic.
765 # This was also fixed in cc0ff93d0c0c.
771 # This was also fixed in cc0ff93d0c0c.
766 fastpathlinkrev = fastpathlinkrev and not self._reorder
772 fastpathlinkrev = fastpathlinkrev and not self._reorder
767 # Treemanifests don't work correctly with fastpathlinkrev
773 # Treemanifests don't work correctly with fastpathlinkrev
768 # either, because we don't discover which directory nodes to
774 # either, because we don't discover which directory nodes to
769 # send along with files. This could probably be fixed.
775 # send along with files. This could probably be fixed.
770 fastpathlinkrev = fastpathlinkrev and (
776 fastpathlinkrev = fastpathlinkrev and (
771 'treemanifest' not in repo.requirements)
777 'treemanifest' not in repo.requirements)
772
778
773 for chunk in self.generatemanifests(commonrevs, clrevorder,
779 for chunk in self.generatemanifests(commonrevs, clrevorder,
774 fastpathlinkrev, mfs, fnodes, source):
780 fastpathlinkrev, mfs, fnodes, source):
775 yield chunk
781 yield chunk
776
782
777 if ellipsesmode:
783 if ellipsesmode:
778 mfdicts = None
784 mfdicts = None
779 if self.is_shallow:
785 if self.is_shallow:
780 mfdicts = [(self._repo.manifestlog[n].read(), lr)
786 mfdicts = [(self._repo.manifestlog[n].read(), lr)
781 for (n, lr) in mfs.iteritems()]
787 for (n, lr) in mfs.iteritems()]
782
788
783 mfs.clear()
789 mfs.clear()
784 clrevs = set(cl.rev(x) for x in clnodes)
790 clrevs = set(cl.rev(x) for x in clnodes)
785
791
786 if not fastpathlinkrev:
792 if not fastpathlinkrev:
787 def linknodes(unused, fname):
793 def linknodes(unused, fname):
788 return fnodes.get(fname, {})
794 return fnodes.get(fname, {})
789 else:
795 else:
790 cln = cl.node
796 cln = cl.node
791 def linknodes(filerevlog, fname):
797 def linknodes(filerevlog, fname):
792 llr = filerevlog.linkrev
798 llr = filerevlog.linkrev
793 fln = filerevlog.node
799 fln = filerevlog.node
794 revs = ((r, llr(r)) for r in filerevlog)
800 revs = ((r, llr(r)) for r in filerevlog)
795 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
801 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
796
802
797 if ellipsesmode:
803 if ellipsesmode:
798 # We need to pass the mfdicts variable down into
804 # We need to pass the mfdicts variable down into
799 # generatefiles(), but more than one command might have
805 # generatefiles(), but more than one command might have
800 # wrapped generatefiles so we can't modify the function
806 # wrapped generatefiles so we can't modify the function
801 # signature. Instead, we pass the data to ourselves using an
807 # signature. Instead, we pass the data to ourselves using an
802 # instance attribute. I'm sorry.
808 # instance attribute. I'm sorry.
803 self._mfdicts = mfdicts
809 self._mfdicts = mfdicts
804
810
805 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
811 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
806 source):
812 source):
807 yield chunk
813 yield chunk
808
814
809 yield self.close()
815 yield self.close()
810
816
811 if clnodes:
817 if clnodes:
812 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
818 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
813
819
814 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
820 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
815 fnodes, source):
821 fnodes, source):
816 """Returns an iterator of changegroup chunks containing manifests.
822 """Returns an iterator of changegroup chunks containing manifests.
817
823
818 `source` is unused here, but is used by extensions like remotefilelog to
824 `source` is unused here, but is used by extensions like remotefilelog to
819 change what is sent based in pulls vs pushes, etc.
825 change what is sent based in pulls vs pushes, etc.
820 """
826 """
821 repo = self._repo
827 repo = self._repo
822 mfl = repo.manifestlog
828 mfl = repo.manifestlog
823 dirlog = mfl._revlog.dirlog
829 dirlog = mfl._revlog.dirlog
824 tmfnodes = {'': mfs}
830 tmfnodes = {'': mfs}
825
831
826 # Callback for the manifest, used to collect linkrevs for filelog
832 # Callback for the manifest, used to collect linkrevs for filelog
827 # revisions.
833 # revisions.
828 # Returns the linkrev node (collected in lookupcl).
834 # Returns the linkrev node (collected in lookupcl).
829 def makelookupmflinknode(dir, nodes):
835 def makelookupmflinknode(dir, nodes):
830 if fastpathlinkrev:
836 if fastpathlinkrev:
831 assert not dir
837 assert not dir
832 return mfs.__getitem__
838 return mfs.__getitem__
833
839
834 def lookupmflinknode(x):
840 def lookupmflinknode(x):
835 """Callback for looking up the linknode for manifests.
841 """Callback for looking up the linknode for manifests.
836
842
837 Returns the linkrev node for the specified manifest.
843 Returns the linkrev node for the specified manifest.
838
844
839 SIDE EFFECT:
845 SIDE EFFECT:
840
846
841 1) fclnodes gets populated with the list of relevant
847 1) fclnodes gets populated with the list of relevant
842 file nodes if we're not using fastpathlinkrev
848 file nodes if we're not using fastpathlinkrev
843 2) When treemanifests are in use, collects treemanifest nodes
849 2) When treemanifests are in use, collects treemanifest nodes
844 to send
850 to send
845
851
846 Note that this means manifests must be completely sent to
852 Note that this means manifests must be completely sent to
847 the client before you can trust the list of files and
853 the client before you can trust the list of files and
848 treemanifests to send.
854 treemanifests to send.
849 """
855 """
850 clnode = nodes[x]
856 clnode = nodes[x]
851 mdata = mfl.get(dir, x).readfast(shallow=True)
857 mdata = mfl.get(dir, x).readfast(shallow=True)
852 for p, n, fl in mdata.iterentries():
858 for p, n, fl in mdata.iterentries():
853 if fl == 't': # subdirectory manifest
859 if fl == 't': # subdirectory manifest
854 subdir = dir + p + '/'
860 subdir = dir + p + '/'
855 tmfclnodes = tmfnodes.setdefault(subdir, {})
861 tmfclnodes = tmfnodes.setdefault(subdir, {})
856 tmfclnode = tmfclnodes.setdefault(n, clnode)
862 tmfclnode = tmfclnodes.setdefault(n, clnode)
857 if clrevorder[clnode] < clrevorder[tmfclnode]:
863 if clrevorder[clnode] < clrevorder[tmfclnode]:
858 tmfclnodes[n] = clnode
864 tmfclnodes[n] = clnode
859 else:
865 else:
860 f = dir + p
866 f = dir + p
861 fclnodes = fnodes.setdefault(f, {})
867 fclnodes = fnodes.setdefault(f, {})
862 fclnode = fclnodes.setdefault(n, clnode)
868 fclnode = fclnodes.setdefault(n, clnode)
863 if clrevorder[clnode] < clrevorder[fclnode]:
869 if clrevorder[clnode] < clrevorder[fclnode]:
864 fclnodes[n] = clnode
870 fclnodes[n] = clnode
865 return clnode
871 return clnode
866 return lookupmflinknode
872 return lookupmflinknode
867
873
868 fn = (self._packtreemanifests if self._sendtreemanifests
874 fn = (self._packtreemanifests if self._sendtreemanifests
869 else self._packmanifests)
875 else self._packmanifests)
870 size = 0
876 size = 0
871 while tmfnodes:
877 while tmfnodes:
872 dir, nodes = tmfnodes.popitem()
878 dir, nodes = tmfnodes.popitem()
873 prunednodes = self.prune(dirlog(dir), nodes, commonrevs)
879 prunednodes = self.prune(dirlog(dir), nodes, commonrevs)
874 if not dir or prunednodes:
880 if not dir or prunednodes:
875 for x in fn(dir, prunednodes, makelookupmflinknode(dir, nodes)):
881 for x in fn(dir, prunednodes, makelookupmflinknode(dir, nodes)):
876 size += len(x)
882 size += len(x)
877 yield x
883 yield x
878 self._verbosenote(_('%8.i (manifests)\n') % size)
884 self._verbosenote(_('%8.i (manifests)\n') % size)
879 yield self._manifestsend
885 yield self._manifestsend
880
886
881 # The 'source' parameter is useful for extensions
887 # The 'source' parameter is useful for extensions
882 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
888 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
883 changedfiles = list(filter(self._filematcher, changedfiles))
889 changedfiles = list(filter(self._filematcher, changedfiles))
884
890
885 if getattr(self, 'is_shallow', False):
891 if getattr(self, 'is_shallow', False):
886 # See comment in generate() for why this sadness is a thing.
892 # See comment in generate() for why this sadness is a thing.
887 mfdicts = self._mfdicts
893 mfdicts = self._mfdicts
888 del self._mfdicts
894 del self._mfdicts
889 # In a shallow clone, the linknodes callback needs to also include
895 # In a shallow clone, the linknodes callback needs to also include
890 # those file nodes that are in the manifests we sent but weren't
896 # those file nodes that are in the manifests we sent but weren't
891 # introduced by those manifests.
897 # introduced by those manifests.
892 commonctxs = [self._repo[c] for c in commonrevs]
898 commonctxs = [self._repo[c] for c in commonrevs]
893 oldlinknodes = linknodes
899 oldlinknodes = linknodes
894 clrev = self._repo.changelog.rev
900 clrev = self._repo.changelog.rev
895
901
896 # Defining this function has a side-effect of overriding the
902 # Defining this function has a side-effect of overriding the
897 # function of the same name that was passed in as an argument.
903 # function of the same name that was passed in as an argument.
898 # TODO have caller pass in appropriate function.
904 # TODO have caller pass in appropriate function.
899 def linknodes(flog, fname):
905 def linknodes(flog, fname):
900 for c in commonctxs:
906 for c in commonctxs:
901 try:
907 try:
902 fnode = c.filenode(fname)
908 fnode = c.filenode(fname)
903 self.clrev_to_localrev[c.rev()] = flog.rev(fnode)
909 self.clrev_to_localrev[c.rev()] = flog.rev(fnode)
904 except error.ManifestLookupError:
910 except error.ManifestLookupError:
905 pass
911 pass
906 links = oldlinknodes(flog, fname)
912 links = oldlinknodes(flog, fname)
907 if len(links) != len(mfdicts):
913 if len(links) != len(mfdicts):
908 for mf, lr in mfdicts:
914 for mf, lr in mfdicts:
909 fnode = mf.get(fname, None)
915 fnode = mf.get(fname, None)
910 if fnode in links:
916 if fnode in links:
911 links[fnode] = min(links[fnode], lr, key=clrev)
917 links[fnode] = min(links[fnode], lr, key=clrev)
912 elif fnode:
918 elif fnode:
913 links[fnode] = lr
919 links[fnode] = lr
914 return links
920 return links
915
921
916 return self._generatefiles(changedfiles, linknodes, commonrevs, source)
922 return self._generatefiles(changedfiles, linknodes, commonrevs, source)
917
923
918 def _generatefiles(self, changedfiles, linknodes, commonrevs, source):
924 def _generatefiles(self, changedfiles, linknodes, commonrevs, source):
919 repo = self._repo
925 repo = self._repo
920 progress = repo.ui.makeprogress(_('bundling'), unit=_('files'),
926 progress = repo.ui.makeprogress(_('bundling'), unit=_('files'),
921 total=len(changedfiles))
927 total=len(changedfiles))
922 for i, fname in enumerate(sorted(changedfiles)):
928 for i, fname in enumerate(sorted(changedfiles)):
923 filerevlog = repo.file(fname)
929 filerevlog = repo.file(fname)
924 if not filerevlog:
930 if not filerevlog:
925 raise error.Abort(_("empty or missing file data for %s") %
931 raise error.Abort(_("empty or missing file data for %s") %
926 fname)
932 fname)
927
933
928 linkrevnodes = linknodes(filerevlog, fname)
934 linkrevnodes = linknodes(filerevlog, fname)
929 # Lookup for filenodes, we collected the linkrev nodes above in the
935 # Lookup for filenodes, we collected the linkrev nodes above in the
930 # fastpath case and with lookupmf in the slowpath case.
936 # fastpath case and with lookupmf in the slowpath case.
931 def lookupfilelog(x):
937 def lookupfilelog(x):
932 return linkrevnodes[x]
938 return linkrevnodes[x]
933
939
934 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs)
940 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs)
935 if filenodes:
941 if filenodes:
936 progress.update(i + 1, item=fname)
942 progress.update(i + 1, item=fname)
937 h = self.fileheader(fname)
943 h = self.fileheader(fname)
938 size = len(h)
944 size = len(h)
939 yield h
945 yield h
940 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
946 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
941 size += len(chunk)
947 size += len(chunk)
942 yield chunk
948 yield chunk
943 self._verbosenote(_('%8.i %s\n') % (size, fname))
949 self._verbosenote(_('%8.i %s\n') % (size, fname))
944 progress.complete()
950 progress.complete()
945
951
946 def deltaparent(self, store, rev, p1, p2, prev):
952 def deltaparent(self, store, rev, p1, p2, prev):
947 if not store.candelta(prev, rev):
953 if not store.candelta(prev, rev):
948 raise error.ProgrammingError('cg1 should not be used in this case')
954 raise error.ProgrammingError('cg1 should not be used in this case')
949 return prev
955 return prev
950
956
951 def revchunk(self, store, rev, prev, linknode):
957 def revchunk(self, store, rev, prev, linknode):
952 if util.safehasattr(self, 'full_nodes'):
958 if util.safehasattr(self, 'full_nodes'):
953 fn = self._revisiondeltanarrow
959 fn = self._revisiondeltanarrow
954 else:
960 else:
955 fn = self._revisiondeltanormal
961 fn = self._revisiondeltanormal
956
962
957 delta = fn(store, rev, prev, linknode)
963 delta = fn(store, rev, prev, linknode)
958 if not delta:
964 if not delta:
959 return
965 return
960
966
961 meta = self._builddeltaheader(delta)
967 meta = self._builddeltaheader(delta)
962 l = len(meta) + sum(len(x) for x in delta.deltachunks)
968 l = len(meta) + sum(len(x) for x in delta.deltachunks)
963
969
964 yield chunkheader(l)
970 yield chunkheader(l)
965 yield meta
971 yield meta
966 for x in delta.deltachunks:
972 for x in delta.deltachunks:
967 yield x
973 yield x
968
974
969 def _revisiondeltanormal(self, store, rev, prev, linknode):
975 def _revisiondeltanormal(self, store, rev, prev, linknode):
970 node = store.node(rev)
976 node = store.node(rev)
971 p1, p2 = store.parentrevs(rev)
977 p1, p2 = store.parentrevs(rev)
972 base = self.deltaparent(store, rev, p1, p2, prev)
978 base = self.deltaparent(store, rev, p1, p2, prev)
973
979
974 prefix = ''
980 prefix = ''
975 if store.iscensored(base) or store.iscensored(rev):
981 if store.iscensored(base) or store.iscensored(rev):
976 try:
982 try:
977 delta = store.revision(node, raw=True)
983 delta = store.revision(node, raw=True)
978 except error.CensoredNodeError as e:
984 except error.CensoredNodeError as e:
979 delta = e.tombstone
985 delta = e.tombstone
980 if base == nullrev:
986 if base == nullrev:
981 prefix = mdiff.trivialdiffheader(len(delta))
987 prefix = mdiff.trivialdiffheader(len(delta))
982 else:
988 else:
983 baselen = store.rawsize(base)
989 baselen = store.rawsize(base)
984 prefix = mdiff.replacediffheader(baselen, len(delta))
990 prefix = mdiff.replacediffheader(baselen, len(delta))
985 elif base == nullrev:
991 elif base == nullrev:
986 delta = store.revision(node, raw=True)
992 delta = store.revision(node, raw=True)
987 prefix = mdiff.trivialdiffheader(len(delta))
993 prefix = mdiff.trivialdiffheader(len(delta))
988 else:
994 else:
989 delta = store.revdiff(base, rev)
995 delta = store.revdiff(base, rev)
990 p1n, p2n = store.parents(node)
996 p1n, p2n = store.parents(node)
991
997
992 return revisiondelta(
998 return revisiondelta(
993 node=node,
999 node=node,
994 p1node=p1n,
1000 p1node=p1n,
995 p2node=p2n,
1001 p2node=p2n,
996 basenode=store.node(base),
1002 basenode=store.node(base),
997 linknode=linknode,
1003 linknode=linknode,
998 flags=store.flags(rev),
1004 flags=store.flags(rev),
999 deltachunks=(prefix, delta),
1005 deltachunks=(prefix, delta),
1000 )
1006 )
1001
1007
1002 def _revisiondeltanarrow(self, store, rev, prev, linknode):
1008 def _revisiondeltanarrow(self, store, rev, prev, linknode):
1003 # build up some mapping information that's useful later. See
1009 # build up some mapping information that's useful later. See
1004 # the local() nested function below.
1010 # the local() nested function below.
1005 if not self.changelog_done:
1011 if not self.changelog_done:
1006 self.clnode_to_rev[linknode] = rev
1012 self.clnode_to_rev[linknode] = rev
1007 linkrev = rev
1013 linkrev = rev
1008 self.clrev_to_localrev[linkrev] = rev
1014 self.clrev_to_localrev[linkrev] = rev
1009 else:
1015 else:
1010 linkrev = self.clnode_to_rev[linknode]
1016 linkrev = self.clnode_to_rev[linknode]
1011 self.clrev_to_localrev[linkrev] = rev
1017 self.clrev_to_localrev[linkrev] = rev
1012
1018
1013 # This is a node to send in full, because the changeset it
1019 # This is a node to send in full, because the changeset it
1014 # corresponds to was a full changeset.
1020 # corresponds to was a full changeset.
1015 if linknode in self.full_nodes:
1021 if linknode in self.full_nodes:
1016 return self._revisiondeltanormal(store, rev, prev, linknode)
1022 return self._revisiondeltanormal(store, rev, prev, linknode)
1017
1023
1018 # At this point, a node can either be one we should skip or an
1024 # At this point, a node can either be one we should skip or an
1019 # ellipsis. If it's not an ellipsis, bail immediately.
1025 # ellipsis. If it's not an ellipsis, bail immediately.
1020 if linkrev not in self.precomputed_ellipsis:
1026 if linkrev not in self.precomputed_ellipsis:
1021 return
1027 return
1022
1028
1023 linkparents = self.precomputed_ellipsis[linkrev]
1029 linkparents = self.precomputed_ellipsis[linkrev]
1024 def local(clrev):
1030 def local(clrev):
1025 """Turn a changelog revnum into a local revnum.
1031 """Turn a changelog revnum into a local revnum.
1026
1032
1027 The ellipsis dag is stored as revnums on the changelog,
1033 The ellipsis dag is stored as revnums on the changelog,
1028 but when we're producing ellipsis entries for
1034 but when we're producing ellipsis entries for
1029 non-changelog revlogs, we need to turn those numbers into
1035 non-changelog revlogs, we need to turn those numbers into
1030 something local. This does that for us, and during the
1036 something local. This does that for us, and during the
1031 changelog sending phase will also expand the stored
1037 changelog sending phase will also expand the stored
1032 mappings as needed.
1038 mappings as needed.
1033 """
1039 """
1034 if clrev == nullrev:
1040 if clrev == nullrev:
1035 return nullrev
1041 return nullrev
1036
1042
1037 if not self.changelog_done:
1043 if not self.changelog_done:
1038 # If we're doing the changelog, it's possible that we
1044 # If we're doing the changelog, it's possible that we
1039 # have a parent that is already on the client, and we
1045 # have a parent that is already on the client, and we
1040 # need to store some extra mapping information so that
1046 # need to store some extra mapping information so that
1041 # our contained ellipsis nodes will be able to resolve
1047 # our contained ellipsis nodes will be able to resolve
1042 # their parents.
1048 # their parents.
1043 if clrev not in self.clrev_to_localrev:
1049 if clrev not in self.clrev_to_localrev:
1044 clnode = store.node(clrev)
1050 clnode = store.node(clrev)
1045 self.clnode_to_rev[clnode] = clrev
1051 self.clnode_to_rev[clnode] = clrev
1046 return clrev
1052 return clrev
1047
1053
1048 # Walk the ellipsis-ized changelog breadth-first looking for a
1054 # Walk the ellipsis-ized changelog breadth-first looking for a
1049 # change that has been linked from the current revlog.
1055 # change that has been linked from the current revlog.
1050 #
1056 #
1051 # For a flat manifest revlog only a single step should be necessary
1057 # For a flat manifest revlog only a single step should be necessary
1052 # as all relevant changelog entries are relevant to the flat
1058 # as all relevant changelog entries are relevant to the flat
1053 # manifest.
1059 # manifest.
1054 #
1060 #
1055 # For a filelog or tree manifest dirlog however not every changelog
1061 # For a filelog or tree manifest dirlog however not every changelog
1056 # entry will have been relevant, so we need to skip some changelog
1062 # entry will have been relevant, so we need to skip some changelog
1057 # nodes even after ellipsis-izing.
1063 # nodes even after ellipsis-izing.
1058 walk = [clrev]
1064 walk = [clrev]
1059 while walk:
1065 while walk:
1060 p = walk[0]
1066 p = walk[0]
1061 walk = walk[1:]
1067 walk = walk[1:]
1062 if p in self.clrev_to_localrev:
1068 if p in self.clrev_to_localrev:
1063 return self.clrev_to_localrev[p]
1069 return self.clrev_to_localrev[p]
1064 elif p in self.full_nodes:
1070 elif p in self.full_nodes:
1065 walk.extend([pp for pp in self._repo.changelog.parentrevs(p)
1071 walk.extend([pp for pp in self._repo.changelog.parentrevs(p)
1066 if pp != nullrev])
1072 if pp != nullrev])
1067 elif p in self.precomputed_ellipsis:
1073 elif p in self.precomputed_ellipsis:
1068 walk.extend([pp for pp in self.precomputed_ellipsis[p]
1074 walk.extend([pp for pp in self.precomputed_ellipsis[p]
1069 if pp != nullrev])
1075 if pp != nullrev])
1070 else:
1076 else:
1071 # In this case, we've got an ellipsis with parents
1077 # In this case, we've got an ellipsis with parents
1072 # outside the current bundle (likely an
1078 # outside the current bundle (likely an
1073 # incremental pull). We "know" that we can use the
1079 # incremental pull). We "know" that we can use the
1074 # value of this same revlog at whatever revision
1080 # value of this same revlog at whatever revision
1075 # is pointed to by linknode. "Know" is in scare
1081 # is pointed to by linknode. "Know" is in scare
1076 # quotes because I haven't done enough examination
1082 # quotes because I haven't done enough examination
1077 # of edge cases to convince myself this is really
1083 # of edge cases to convince myself this is really
1078 # a fact - it works for all the (admittedly
1084 # a fact - it works for all the (admittedly
1079 # thorough) cases in our testsuite, but I would be
1085 # thorough) cases in our testsuite, but I would be
1080 # somewhat unsurprised to find a case in the wild
1086 # somewhat unsurprised to find a case in the wild
1081 # where this breaks down a bit. That said, I don't
1087 # where this breaks down a bit. That said, I don't
1082 # know if it would hurt anything.
1088 # know if it would hurt anything.
1083 for i in pycompat.xrange(rev, 0, -1):
1089 for i in pycompat.xrange(rev, 0, -1):
1084 if store.linkrev(i) == clrev:
1090 if store.linkrev(i) == clrev:
1085 return i
1091 return i
1086 # We failed to resolve a parent for this node, so
1092 # We failed to resolve a parent for this node, so
1087 # we crash the changegroup construction.
1093 # we crash the changegroup construction.
1088 raise error.Abort(
1094 raise error.Abort(
1089 'unable to resolve parent while packing %r %r'
1095 'unable to resolve parent while packing %r %r'
1090 ' for changeset %r' % (store.indexfile, rev, clrev))
1096 ' for changeset %r' % (store.indexfile, rev, clrev))
1091
1097
1092 return nullrev
1098 return nullrev
1093
1099
1094 if not linkparents or (
1100 if not linkparents or (
1095 store.parentrevs(rev) == (nullrev, nullrev)):
1101 store.parentrevs(rev) == (nullrev, nullrev)):
1096 p1, p2 = nullrev, nullrev
1102 p1, p2 = nullrev, nullrev
1097 elif len(linkparents) == 1:
1103 elif len(linkparents) == 1:
1098 p1, = sorted(local(p) for p in linkparents)
1104 p1, = sorted(local(p) for p in linkparents)
1099 p2 = nullrev
1105 p2 = nullrev
1100 else:
1106 else:
1101 p1, p2 = sorted(local(p) for p in linkparents)
1107 p1, p2 = sorted(local(p) for p in linkparents)
1102
1108
1103 n = store.node(rev)
1109 n = store.node(rev)
1104 p1n, p2n = store.node(p1), store.node(p2)
1110 p1n, p2n = store.node(p1), store.node(p2)
1105 flags = store.flags(rev)
1111 flags = store.flags(rev)
1106 flags |= revlog.REVIDX_ELLIPSIS
1112 flags |= revlog.REVIDX_ELLIPSIS
1107
1113
1108 # TODO: try and actually send deltas for ellipsis data blocks
1114 # TODO: try and actually send deltas for ellipsis data blocks
1109 data = store.revision(n)
1115 data = store.revision(n)
1110 diffheader = mdiff.trivialdiffheader(len(data))
1116 diffheader = mdiff.trivialdiffheader(len(data))
1111
1117
1112 return revisiondelta(
1118 return revisiondelta(
1113 node=n,
1119 node=n,
1114 p1node=p1n,
1120 p1node=p1n,
1115 p2node=p2n,
1121 p2node=p2n,
1116 basenode=nullid,
1122 basenode=nullid,
1117 linknode=linknode,
1123 linknode=linknode,
1118 flags=flags,
1124 flags=flags,
1119 deltachunks=(diffheader, data),
1125 deltachunks=(diffheader, data),
1120 )
1126 )
1121
1127
1122 class cg2packer(cg1packer):
1128 class cg2packer(cg1packer):
1123 def __init__(self, repo, filematcher, version, builddeltaheader,
1124 manifestsend, sendtreemanifests, bundlecaps=None):
1125 super(cg2packer, self).__init__(repo, filematcher, version,
1126 builddeltaheader, manifestsend,
1127 sendtreemanifests,
1128 bundlecaps=bundlecaps)
1129
1130 if self._reorder is None:
1131 # Since generaldelta is directly supported by cg2, reordering
1132 # generally doesn't help, so we disable it by default (treating
1133 # bundle.reorder=auto just like bundle.reorder=False).
1134 self._reorder = False
1135
1136 def deltaparent(self, store, rev, p1, p2, prev):
1129 def deltaparent(self, store, rev, p1, p2, prev):
1137 # Narrow ellipses mode.
1130 # Narrow ellipses mode.
1138 if util.safehasattr(self, 'full_nodes'):
1131 if util.safehasattr(self, 'full_nodes'):
1139 # TODO: send better deltas when in narrow mode.
1132 # TODO: send better deltas when in narrow mode.
1140 #
1133 #
1141 # changegroup.group() loops over revisions to send,
1134 # changegroup.group() loops over revisions to send,
1142 # including revisions we'll skip. What this means is that
1135 # including revisions we'll skip. What this means is that
1143 # `prev` will be a potentially useless delta base for all
1136 # `prev` will be a potentially useless delta base for all
1144 # ellipsis nodes, as the client likely won't have it. In
1137 # ellipsis nodes, as the client likely won't have it. In
1145 # the future we should do bookkeeping about which nodes
1138 # the future we should do bookkeeping about which nodes
1146 # have been sent to the client, and try to be
1139 # have been sent to the client, and try to be
1147 # significantly smarter about delta bases. This is
1140 # significantly smarter about delta bases. This is
1148 # slightly tricky because this same code has to work for
1141 # slightly tricky because this same code has to work for
1149 # all revlogs, and we don't have the linkrev/linknode here.
1142 # all revlogs, and we don't have the linkrev/linknode here.
1150 return p1
1143 return p1
1151
1144
1152 dp = store.deltaparent(rev)
1145 dp = store.deltaparent(rev)
1153 if dp == nullrev and store.storedeltachains:
1146 if dp == nullrev and store.storedeltachains:
1154 # Avoid sending full revisions when delta parent is null. Pick prev
1147 # Avoid sending full revisions when delta parent is null. Pick prev
1155 # in that case. It's tempting to pick p1 in this case, as p1 will
1148 # in that case. It's tempting to pick p1 in this case, as p1 will
1156 # be smaller in the common case. However, computing a delta against
1149 # be smaller in the common case. However, computing a delta against
1157 # p1 may require resolving the raw text of p1, which could be
1150 # p1 may require resolving the raw text of p1, which could be
1158 # expensive. The revlog caches should have prev cached, meaning
1151 # expensive. The revlog caches should have prev cached, meaning
1159 # less CPU for changegroup generation. There is likely room to add
1152 # less CPU for changegroup generation. There is likely room to add
1160 # a flag and/or config option to control this behavior.
1153 # a flag and/or config option to control this behavior.
1161 base = prev
1154 base = prev
1162 elif dp == nullrev:
1155 elif dp == nullrev:
1163 # revlog is configured to use full snapshot for a reason,
1156 # revlog is configured to use full snapshot for a reason,
1164 # stick to full snapshot.
1157 # stick to full snapshot.
1165 base = nullrev
1158 base = nullrev
1166 elif dp not in (p1, p2, prev):
1159 elif dp not in (p1, p2, prev):
1167 # Pick prev when we can't be sure remote has the base revision.
1160 # Pick prev when we can't be sure remote has the base revision.
1168 return prev
1161 return prev
1169 else:
1162 else:
1170 base = dp
1163 base = dp
1171 if base != nullrev and not store.candelta(base, rev):
1164 if base != nullrev and not store.candelta(base, rev):
1172 base = nullrev
1165 base = nullrev
1173 return base
1166 return base
1174
1167
1175 def _makecg1packer(repo, filematcher, bundlecaps):
1168 def _makecg1packer(repo, filematcher, bundlecaps):
1176 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1169 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1177 d.node, d.p1node, d.p2node, d.linknode)
1170 d.node, d.p1node, d.p2node, d.linknode)
1178
1171
1179 return cg1packer(repo, filematcher, b'01', builddeltaheader,
1172 return cg1packer(repo, filematcher, b'01', allowreorder=None,
1173 builddeltaheader=builddeltaheader,
1180 manifestsend=b'', sendtreemanifests=False,
1174 manifestsend=b'', sendtreemanifests=False,
1181 bundlecaps=bundlecaps)
1175 bundlecaps=bundlecaps)
1182
1176
1183 def _makecg2packer(repo, filematcher, bundlecaps):
1177 def _makecg2packer(repo, filematcher, bundlecaps):
1184 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1178 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1185 d.node, d.p1node, d.p2node, d.basenode, d.linknode)
1179 d.node, d.p1node, d.p2node, d.basenode, d.linknode)
1186
1180
1187 return cg2packer(repo, filematcher, b'02', builddeltaheader,
1181 # Since generaldelta is directly supported by cg2, reordering
1182 # generally doesn't help, so we disable it by default (treating
1183 # bundle.reorder=auto just like bundle.reorder=False).
1184 return cg2packer(repo, filematcher, b'02', allowreorder=False,
1185 builddeltaheader=builddeltaheader,
1188 manifestsend=b'', sendtreemanifests=False,
1186 manifestsend=b'', sendtreemanifests=False,
1189 bundlecaps=bundlecaps)
1187 bundlecaps=bundlecaps)
1190
1188
1191 def _makecg3packer(repo, filematcher, bundlecaps):
1189 def _makecg3packer(repo, filematcher, bundlecaps):
1192 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1190 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1193 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1191 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1194
1192
1195 return cg2packer(repo, filematcher, b'03', builddeltaheader,
1193 return cg2packer(repo, filematcher, b'03', allowreorder=False,
1194 builddeltaheader=builddeltaheader,
1196 manifestsend=closechunk(), sendtreemanifests=True,
1195 manifestsend=closechunk(), sendtreemanifests=True,
1197 bundlecaps=bundlecaps)
1196 bundlecaps=bundlecaps)
1198
1197
1199 _packermap = {'01': (_makecg1packer, cg1unpacker),
1198 _packermap = {'01': (_makecg1packer, cg1unpacker),
1200 # cg2 adds support for exchanging generaldelta
1199 # cg2 adds support for exchanging generaldelta
1201 '02': (_makecg2packer, cg2unpacker),
1200 '02': (_makecg2packer, cg2unpacker),
1202 # cg3 adds support for exchanging revlog flags and treemanifests
1201 # cg3 adds support for exchanging revlog flags and treemanifests
1203 '03': (_makecg3packer, cg3unpacker),
1202 '03': (_makecg3packer, cg3unpacker),
1204 }
1203 }
1205
1204
1206 def allsupportedversions(repo):
1205 def allsupportedversions(repo):
1207 versions = set(_packermap.keys())
1206 versions = set(_packermap.keys())
1208 if not (repo.ui.configbool('experimental', 'changegroup3') or
1207 if not (repo.ui.configbool('experimental', 'changegroup3') or
1209 repo.ui.configbool('experimental', 'treemanifest') or
1208 repo.ui.configbool('experimental', 'treemanifest') or
1210 'treemanifest' in repo.requirements):
1209 'treemanifest' in repo.requirements):
1211 versions.discard('03')
1210 versions.discard('03')
1212 return versions
1211 return versions
1213
1212
1214 # Changegroup versions that can be applied to the repo
1213 # Changegroup versions that can be applied to the repo
1215 def supportedincomingversions(repo):
1214 def supportedincomingversions(repo):
1216 return allsupportedversions(repo)
1215 return allsupportedversions(repo)
1217
1216
1218 # Changegroup versions that can be created from the repo
1217 # Changegroup versions that can be created from the repo
1219 def supportedoutgoingversions(repo):
1218 def supportedoutgoingversions(repo):
1220 versions = allsupportedversions(repo)
1219 versions = allsupportedversions(repo)
1221 if 'treemanifest' in repo.requirements:
1220 if 'treemanifest' in repo.requirements:
1222 # Versions 01 and 02 support only flat manifests and it's just too
1221 # Versions 01 and 02 support only flat manifests and it's just too
1223 # expensive to convert between the flat manifest and tree manifest on
1222 # expensive to convert between the flat manifest and tree manifest on
1224 # the fly. Since tree manifests are hashed differently, all of history
1223 # the fly. Since tree manifests are hashed differently, all of history
1225 # would have to be converted. Instead, we simply don't even pretend to
1224 # would have to be converted. Instead, we simply don't even pretend to
1226 # support versions 01 and 02.
1225 # support versions 01 and 02.
1227 versions.discard('01')
1226 versions.discard('01')
1228 versions.discard('02')
1227 versions.discard('02')
1229 if repository.NARROW_REQUIREMENT in repo.requirements:
1228 if repository.NARROW_REQUIREMENT in repo.requirements:
1230 # Versions 01 and 02 don't support revlog flags, and we need to
1229 # Versions 01 and 02 don't support revlog flags, and we need to
1231 # support that for stripping and unbundling to work.
1230 # support that for stripping and unbundling to work.
1232 versions.discard('01')
1231 versions.discard('01')
1233 versions.discard('02')
1232 versions.discard('02')
1234 if LFS_REQUIREMENT in repo.requirements:
1233 if LFS_REQUIREMENT in repo.requirements:
1235 # Versions 01 and 02 don't support revlog flags, and we need to
1234 # Versions 01 and 02 don't support revlog flags, and we need to
1236 # mark LFS entries with REVIDX_EXTSTORED.
1235 # mark LFS entries with REVIDX_EXTSTORED.
1237 versions.discard('01')
1236 versions.discard('01')
1238 versions.discard('02')
1237 versions.discard('02')
1239
1238
1240 return versions
1239 return versions
1241
1240
1242 def localversion(repo):
1241 def localversion(repo):
1243 # Finds the best version to use for bundles that are meant to be used
1242 # Finds the best version to use for bundles that are meant to be used
1244 # locally, such as those from strip and shelve, and temporary bundles.
1243 # locally, such as those from strip and shelve, and temporary bundles.
1245 return max(supportedoutgoingversions(repo))
1244 return max(supportedoutgoingversions(repo))
1246
1245
1247 def safeversion(repo):
1246 def safeversion(repo):
1248 # Finds the smallest version that it's safe to assume clients of the repo
1247 # Finds the smallest version that it's safe to assume clients of the repo
1249 # will support. For example, all hg versions that support generaldelta also
1248 # will support. For example, all hg versions that support generaldelta also
1250 # support changegroup 02.
1249 # support changegroup 02.
1251 versions = supportedoutgoingversions(repo)
1250 versions = supportedoutgoingversions(repo)
1252 if 'generaldelta' in repo.requirements:
1251 if 'generaldelta' in repo.requirements:
1253 versions.discard('01')
1252 versions.discard('01')
1254 assert versions
1253 assert versions
1255 return min(versions)
1254 return min(versions)
1256
1255
1257 def getbundler(version, repo, bundlecaps=None, filematcher=None):
1256 def getbundler(version, repo, bundlecaps=None, filematcher=None):
1258 assert version in supportedoutgoingversions(repo)
1257 assert version in supportedoutgoingversions(repo)
1259
1258
1260 if filematcher is None:
1259 if filematcher is None:
1261 filematcher = matchmod.alwaysmatcher(repo.root, '')
1260 filematcher = matchmod.alwaysmatcher(repo.root, '')
1262
1261
1263 if version == '01' and not filematcher.always():
1262 if version == '01' and not filematcher.always():
1264 raise error.ProgrammingError('version 01 changegroups do not support '
1263 raise error.ProgrammingError('version 01 changegroups do not support '
1265 'sparse file matchers')
1264 'sparse file matchers')
1266
1265
1267 # Requested files could include files not in the local store. So
1266 # Requested files could include files not in the local store. So
1268 # filter those out.
1267 # filter those out.
1269 filematcher = matchmod.intersectmatchers(repo.narrowmatch(),
1268 filematcher = matchmod.intersectmatchers(repo.narrowmatch(),
1270 filematcher)
1269 filematcher)
1271
1270
1272 fn = _packermap[version][0]
1271 fn = _packermap[version][0]
1273 return fn(repo, filematcher, bundlecaps)
1272 return fn(repo, filematcher, bundlecaps)
1274
1273
1275 def getunbundler(version, fh, alg, extras=None):
1274 def getunbundler(version, fh, alg, extras=None):
1276 return _packermap[version][1](fh, alg, extras=extras)
1275 return _packermap[version][1](fh, alg, extras=extras)
1277
1276
1278 def _changegroupinfo(repo, nodes, source):
1277 def _changegroupinfo(repo, nodes, source):
1279 if repo.ui.verbose or source == 'bundle':
1278 if repo.ui.verbose or source == 'bundle':
1280 repo.ui.status(_("%d changesets found\n") % len(nodes))
1279 repo.ui.status(_("%d changesets found\n") % len(nodes))
1281 if repo.ui.debugflag:
1280 if repo.ui.debugflag:
1282 repo.ui.debug("list of changesets:\n")
1281 repo.ui.debug("list of changesets:\n")
1283 for node in nodes:
1282 for node in nodes:
1284 repo.ui.debug("%s\n" % hex(node))
1283 repo.ui.debug("%s\n" % hex(node))
1285
1284
1286 def makechangegroup(repo, outgoing, version, source, fastpath=False,
1285 def makechangegroup(repo, outgoing, version, source, fastpath=False,
1287 bundlecaps=None):
1286 bundlecaps=None):
1288 cgstream = makestream(repo, outgoing, version, source,
1287 cgstream = makestream(repo, outgoing, version, source,
1289 fastpath=fastpath, bundlecaps=bundlecaps)
1288 fastpath=fastpath, bundlecaps=bundlecaps)
1290 return getunbundler(version, util.chunkbuffer(cgstream), None,
1289 return getunbundler(version, util.chunkbuffer(cgstream), None,
1291 {'clcount': len(outgoing.missing) })
1290 {'clcount': len(outgoing.missing) })
1292
1291
1293 def makestream(repo, outgoing, version, source, fastpath=False,
1292 def makestream(repo, outgoing, version, source, fastpath=False,
1294 bundlecaps=None, filematcher=None):
1293 bundlecaps=None, filematcher=None):
1295 bundler = getbundler(version, repo, bundlecaps=bundlecaps,
1294 bundler = getbundler(version, repo, bundlecaps=bundlecaps,
1296 filematcher=filematcher)
1295 filematcher=filematcher)
1297
1296
1298 repo = repo.unfiltered()
1297 repo = repo.unfiltered()
1299 commonrevs = outgoing.common
1298 commonrevs = outgoing.common
1300 csets = outgoing.missing
1299 csets = outgoing.missing
1301 heads = outgoing.missingheads
1300 heads = outgoing.missingheads
1302 # We go through the fast path if we get told to, or if all (unfiltered
1301 # We go through the fast path if we get told to, or if all (unfiltered
1303 # heads have been requested (since we then know there all linkrevs will
1302 # heads have been requested (since we then know there all linkrevs will
1304 # be pulled by the client).
1303 # be pulled by the client).
1305 heads.sort()
1304 heads.sort()
1306 fastpathlinkrev = fastpath or (
1305 fastpathlinkrev = fastpath or (
1307 repo.filtername is None and heads == sorted(repo.heads()))
1306 repo.filtername is None and heads == sorted(repo.heads()))
1308
1307
1309 repo.hook('preoutgoing', throw=True, source=source)
1308 repo.hook('preoutgoing', throw=True, source=source)
1310 _changegroupinfo(repo, csets, source)
1309 _changegroupinfo(repo, csets, source)
1311 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1310 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1312
1311
1313 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
1312 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
1314 revisions = 0
1313 revisions = 0
1315 files = 0
1314 files = 0
1316 progress = repo.ui.makeprogress(_('files'), unit=_('files'),
1315 progress = repo.ui.makeprogress(_('files'), unit=_('files'),
1317 total=expectedfiles)
1316 total=expectedfiles)
1318 for chunkdata in iter(source.filelogheader, {}):
1317 for chunkdata in iter(source.filelogheader, {}):
1319 files += 1
1318 files += 1
1320 f = chunkdata["filename"]
1319 f = chunkdata["filename"]
1321 repo.ui.debug("adding %s revisions\n" % f)
1320 repo.ui.debug("adding %s revisions\n" % f)
1322 progress.increment()
1321 progress.increment()
1323 fl = repo.file(f)
1322 fl = repo.file(f)
1324 o = len(fl)
1323 o = len(fl)
1325 try:
1324 try:
1326 deltas = source.deltaiter()
1325 deltas = source.deltaiter()
1327 if not fl.addgroup(deltas, revmap, trp):
1326 if not fl.addgroup(deltas, revmap, trp):
1328 raise error.Abort(_("received file revlog group is empty"))
1327 raise error.Abort(_("received file revlog group is empty"))
1329 except error.CensoredBaseError as e:
1328 except error.CensoredBaseError as e:
1330 raise error.Abort(_("received delta base is censored: %s") % e)
1329 raise error.Abort(_("received delta base is censored: %s") % e)
1331 revisions += len(fl) - o
1330 revisions += len(fl) - o
1332 if f in needfiles:
1331 if f in needfiles:
1333 needs = needfiles[f]
1332 needs = needfiles[f]
1334 for new in pycompat.xrange(o, len(fl)):
1333 for new in pycompat.xrange(o, len(fl)):
1335 n = fl.node(new)
1334 n = fl.node(new)
1336 if n in needs:
1335 if n in needs:
1337 needs.remove(n)
1336 needs.remove(n)
1338 else:
1337 else:
1339 raise error.Abort(
1338 raise error.Abort(
1340 _("received spurious file revlog entry"))
1339 _("received spurious file revlog entry"))
1341 if not needs:
1340 if not needs:
1342 del needfiles[f]
1341 del needfiles[f]
1343 progress.complete()
1342 progress.complete()
1344
1343
1345 for f, needs in needfiles.iteritems():
1344 for f, needs in needfiles.iteritems():
1346 fl = repo.file(f)
1345 fl = repo.file(f)
1347 for n in needs:
1346 for n in needs:
1348 try:
1347 try:
1349 fl.rev(n)
1348 fl.rev(n)
1350 except error.LookupError:
1349 except error.LookupError:
1351 raise error.Abort(
1350 raise error.Abort(
1352 _('missing file data for %s:%s - run hg verify') %
1351 _('missing file data for %s:%s - run hg verify') %
1353 (f, hex(n)))
1352 (f, hex(n)))
1354
1353
1355 return revisions, files
1354 return revisions, files
1356
1355
1357 def _packellipsischangegroup(repo, common, match, relevant_nodes,
1356 def _packellipsischangegroup(repo, common, match, relevant_nodes,
1358 ellipsisroots, visitnodes, depth, source, version):
1357 ellipsisroots, visitnodes, depth, source, version):
1359 if version in ('01', '02'):
1358 if version in ('01', '02'):
1360 raise error.Abort(
1359 raise error.Abort(
1361 'ellipsis nodes require at least cg3 on client and server, '
1360 'ellipsis nodes require at least cg3 on client and server, '
1362 'but negotiated version %s' % version)
1361 'but negotiated version %s' % version)
1363 # We wrap cg1packer.revchunk, using a side channel to pass
1362 # We wrap cg1packer.revchunk, using a side channel to pass
1364 # relevant_nodes into that area. Then if linknode isn't in the
1363 # relevant_nodes into that area. Then if linknode isn't in the
1365 # set, we know we have an ellipsis node and we should defer
1364 # set, we know we have an ellipsis node and we should defer
1366 # sending that node's data. We override close() to detect
1365 # sending that node's data. We override close() to detect
1367 # pending ellipsis nodes and flush them.
1366 # pending ellipsis nodes and flush them.
1368 packer = getbundler(version, repo, filematcher=match)
1367 packer = getbundler(version, repo, filematcher=match)
1369 # Give the packer the list of nodes which should not be
1368 # Give the packer the list of nodes which should not be
1370 # ellipsis nodes. We store this rather than the set of nodes
1369 # ellipsis nodes. We store this rather than the set of nodes
1371 # that should be an ellipsis because for very large histories
1370 # that should be an ellipsis because for very large histories
1372 # we expect this to be significantly smaller.
1371 # we expect this to be significantly smaller.
1373 packer.full_nodes = relevant_nodes
1372 packer.full_nodes = relevant_nodes
1374 # Maps ellipsis revs to their roots at the changelog level.
1373 # Maps ellipsis revs to their roots at the changelog level.
1375 packer.precomputed_ellipsis = ellipsisroots
1374 packer.precomputed_ellipsis = ellipsisroots
1376 # Maps CL revs to per-revlog revisions. Cleared in close() at
1375 # Maps CL revs to per-revlog revisions. Cleared in close() at
1377 # the end of each group.
1376 # the end of each group.
1378 packer.clrev_to_localrev = {}
1377 packer.clrev_to_localrev = {}
1379 packer.next_clrev_to_localrev = {}
1378 packer.next_clrev_to_localrev = {}
1380 # Maps changelog nodes to changelog revs. Filled in once
1379 # Maps changelog nodes to changelog revs. Filled in once
1381 # during changelog stage and then left unmodified.
1380 # during changelog stage and then left unmodified.
1382 packer.clnode_to_rev = {}
1381 packer.clnode_to_rev = {}
1383 packer.changelog_done = False
1382 packer.changelog_done = False
1384 # If true, informs the packer that it is serving shallow content and might
1383 # If true, informs the packer that it is serving shallow content and might
1385 # need to pack file contents not introduced by the changes being packed.
1384 # need to pack file contents not introduced by the changes being packed.
1386 packer.is_shallow = depth is not None
1385 packer.is_shallow = depth is not None
1387
1386
1388 return packer.generate(common, visitnodes, False, source)
1387 return packer.generate(common, visitnodes, False, source)
General Comments 0
You need to be logged in to leave comments. Login now