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