90 } |
90 } |
91 |
91 |
92 # hgweb uses this list to communicate its preferred type |
92 # hgweb uses this list to communicate its preferred type |
93 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] |
93 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] |
94 |
94 |
95 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None): |
95 def writechunks(ui, chunks, filename, vfs=None): |
96 """Write a bundle file and return its filename. |
96 """Write chunks to a file and return its filename. |
97 |
97 |
|
98 The stream is assumed to be a bundle file. |
98 Existing files will not be overwritten. |
99 Existing files will not be overwritten. |
99 If no filename is specified, a temporary file is created. |
100 If no filename is specified, a temporary file is created. |
100 bz2 compression can be turned off. |
|
101 The bundle file will be deleted in case of errors. |
|
102 """ |
101 """ |
103 |
|
104 fh = None |
102 fh = None |
105 cleanup = None |
103 cleanup = None |
106 try: |
104 try: |
107 if filename: |
105 if filename: |
108 if vfs: |
106 if vfs: |
111 fh = open(filename, "wb") |
109 fh = open(filename, "wb") |
112 else: |
110 else: |
113 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") |
111 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") |
114 fh = os.fdopen(fd, "wb") |
112 fh = os.fdopen(fd, "wb") |
115 cleanup = filename |
113 cleanup = filename |
116 |
114 for c in chunks: |
117 if bundletype == "HG20": |
115 fh.write(c) |
118 from . import bundle2 |
|
119 bundle = bundle2.bundle20(ui) |
|
120 bundle.setcompression(compression) |
|
121 part = bundle.newpart('changegroup', data=cg.getchunks()) |
|
122 part.addparam('version', cg.version) |
|
123 z = util.compressors[None]() |
|
124 chunkiter = bundle.getchunks() |
|
125 else: |
|
126 # compression argument is only for the bundle2 case |
|
127 assert compression is None |
|
128 if cg.version != '01': |
|
129 raise util.Abort(_('old bundle types only supports v1 ' |
|
130 'changegroups')) |
|
131 header, comp = bundletypes[bundletype] |
|
132 fh.write(header) |
|
133 if comp not in util.compressors: |
|
134 raise util.Abort(_('unknown stream compression type: %s') |
|
135 % comp) |
|
136 z = util.compressors[comp]() |
|
137 chunkiter = cg.getchunks() |
|
138 |
|
139 # parse the changegroup data, otherwise we will block |
|
140 # in case of sshrepo because we don't know the end of the stream |
|
141 |
|
142 # an empty chunkgroup is the end of the changegroup |
|
143 # a changegroup has at least 2 chunkgroups (changelog and manifest). |
|
144 # after that, an empty chunkgroup is the end of the changegroup |
|
145 for chunk in chunkiter: |
|
146 fh.write(z.compress(chunk)) |
|
147 fh.write(z.flush()) |
|
148 cleanup = None |
116 cleanup = None |
149 return filename |
117 return filename |
150 finally: |
118 finally: |
151 if fh is not None: |
119 if fh is not None: |
152 fh.close() |
120 fh.close() |
153 if cleanup is not None: |
121 if cleanup is not None: |
154 if filename and vfs: |
122 if filename and vfs: |
155 vfs.unlink(cleanup) |
123 vfs.unlink(cleanup) |
156 else: |
124 else: |
157 os.unlink(cleanup) |
125 os.unlink(cleanup) |
|
126 |
|
127 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None): |
|
128 """Write a bundle file and return its filename. |
|
129 |
|
130 Existing files will not be overwritten. |
|
131 If no filename is specified, a temporary file is created. |
|
132 bz2 compression can be turned off. |
|
133 The bundle file will be deleted in case of errors. |
|
134 """ |
|
135 |
|
136 if bundletype == "HG20": |
|
137 from . import bundle2 |
|
138 bundle = bundle2.bundle20(ui) |
|
139 bundle.setcompression(compression) |
|
140 part = bundle.newpart('changegroup', data=cg.getchunks()) |
|
141 part.addparam('version', cg.version) |
|
142 chunkiter = bundle.getchunks() |
|
143 else: |
|
144 # compression argument is only for the bundle2 case |
|
145 assert compression is None |
|
146 if cg.version != '01': |
|
147 raise util.Abort(_('old bundle types only supports v1 ' |
|
148 'changegroups')) |
|
149 header, comp = bundletypes[bundletype] |
|
150 if comp not in util.compressors: |
|
151 raise util.Abort(_('unknown stream compression type: %s') |
|
152 % comp) |
|
153 z = util.compressors[comp]() |
|
154 subchunkiter = cg.getchunks() |
|
155 def chunkiter(): |
|
156 yield header |
|
157 for chunk in subchunkiter: |
|
158 yield z.compress(chunk) |
|
159 yield z.flush() |
|
160 chunkiter = chunkiter() |
|
161 |
|
162 # parse the changegroup data, otherwise we will block |
|
163 # in case of sshrepo because we don't know the end of the stream |
|
164 |
|
165 # an empty chunkgroup is the end of the changegroup |
|
166 # a changegroup has at least 2 chunkgroups (changelog and manifest). |
|
167 # after that, an empty chunkgroup is the end of the changegroup |
|
168 return writechunks(ui, chunkiter, filename, vfs=vfs) |
158 |
169 |
159 class cg1unpacker(object): |
170 class cg1unpacker(object): |
160 deltaheader = _CHANGEGROUPV1_DELTA_HEADER |
171 deltaheader = _CHANGEGROUPV1_DELTA_HEADER |
161 deltaheadersize = struct.calcsize(deltaheader) |
172 deltaheadersize = struct.calcsize(deltaheader) |
162 version = '01' |
173 version = '01' |