##// END OF EJS Templates
allow the creation of bundles with empty changelog/manifest chunks
Alexis S. L. Carvalho -
r5906:0136d7f5 default
parent child Browse files
Show More
@@ -1,122 +1,126 b''
1 """
1 """
2 changegroup.py - Mercurial changegroup manipulation functions
2 changegroup.py - Mercurial changegroup manipulation functions
3
3
4 Copyright 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from i18n import _
10 from i18n import _
11 import struct, os, bz2, zlib, util, tempfile
11 import struct, os, bz2, zlib, util, tempfile
12
12
13 def getchunk(source):
13 def getchunk(source):
14 """get a chunk from a changegroup"""
14 """get a chunk from a changegroup"""
15 d = source.read(4)
15 d = source.read(4)
16 if not d:
16 if not d:
17 return ""
17 return ""
18 l = struct.unpack(">l", d)[0]
18 l = struct.unpack(">l", d)[0]
19 if l <= 4:
19 if l <= 4:
20 return ""
20 return ""
21 d = source.read(l - 4)
21 d = source.read(l - 4)
22 if len(d) < l - 4:
22 if len(d) < l - 4:
23 raise util.Abort(_("premature EOF reading chunk"
23 raise util.Abort(_("premature EOF reading chunk"
24 " (got %d bytes, expected %d)")
24 " (got %d bytes, expected %d)")
25 % (len(d), l - 4))
25 % (len(d), l - 4))
26 return d
26 return d
27
27
28 def chunkiter(source):
28 def chunkiter(source):
29 """iterate through the chunks in source"""
29 """iterate through the chunks in source"""
30 while 1:
30 while 1:
31 c = getchunk(source)
31 c = getchunk(source)
32 if not c:
32 if not c:
33 break
33 break
34 yield c
34 yield c
35
35
36 def chunkheader(length):
36 def chunkheader(length):
37 """build a changegroup chunk header"""
37 """build a changegroup chunk header"""
38 return struct.pack(">l", length + 4)
38 return struct.pack(">l", length + 4)
39
39
40 def closechunk():
40 def closechunk():
41 return struct.pack(">l", 0)
41 return struct.pack(">l", 0)
42
42
43 class nocompress(object):
43 class nocompress(object):
44 def compress(self, x):
44 def compress(self, x):
45 return x
45 return x
46 def flush(self):
46 def flush(self):
47 return ""
47 return ""
48
48
49 bundletypes = {
49 bundletypes = {
50 "": ("", nocompress),
50 "": ("", nocompress),
51 "HG10UN": ("HG10UN", nocompress),
51 "HG10UN": ("HG10UN", nocompress),
52 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
52 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
53 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
53 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
54 }
54 }
55
55
56 def writebundle(cg, filename, bundletype):
56 def writebundle(cg, filename, bundletype):
57 """Write a bundle file and return its filename.
57 """Write a bundle file and return its filename.
58
58
59 Existing files will not be overwritten.
59 Existing files will not be overwritten.
60 If no filename is specified, a temporary file is created.
60 If no filename is specified, a temporary file is created.
61 bz2 compression can be turned off.
61 bz2 compression can be turned off.
62 The bundle file will be deleted in case of errors.
62 The bundle file will be deleted in case of errors.
63 """
63 """
64
64
65 fh = None
65 fh = None
66 cleanup = None
66 cleanup = None
67 try:
67 try:
68 if filename:
68 if filename:
69 fh = open(filename, "wb")
69 fh = open(filename, "wb")
70 else:
70 else:
71 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
71 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
72 fh = os.fdopen(fd, "wb")
72 fh = os.fdopen(fd, "wb")
73 cleanup = filename
73 cleanup = filename
74
74
75 header, compressor = bundletypes[bundletype]
75 header, compressor = bundletypes[bundletype]
76 fh.write(header)
76 fh.write(header)
77 z = compressor()
77 z = compressor()
78
78
79 # parse the changegroup data, otherwise we will block
79 # parse the changegroup data, otherwise we will block
80 # in case of sshrepo because we don't know the end of the stream
80 # in case of sshrepo because we don't know the end of the stream
81
81
82 # an empty chunkiter is the end of the changegroup
82 # an empty chunkiter is the end of the changegroup
83 # a changegroup has at least 2 chunkiters (changelog and manifest).
84 # after that, an empty chunkiter is the end of the changegroup
83 empty = False
85 empty = False
84 while not empty:
86 count = 0
87 while not empty or count <= 2:
85 empty = True
88 empty = True
89 count += 1
86 for chunk in chunkiter(cg):
90 for chunk in chunkiter(cg):
87 empty = False
91 empty = False
88 fh.write(z.compress(chunkheader(len(chunk))))
92 fh.write(z.compress(chunkheader(len(chunk))))
89 pos = 0
93 pos = 0
90 while pos < len(chunk):
94 while pos < len(chunk):
91 next = pos + 2**20
95 next = pos + 2**20
92 fh.write(z.compress(chunk[pos:next]))
96 fh.write(z.compress(chunk[pos:next]))
93 pos = next
97 pos = next
94 fh.write(z.compress(closechunk()))
98 fh.write(z.compress(closechunk()))
95 fh.write(z.flush())
99 fh.write(z.flush())
96 cleanup = None
100 cleanup = None
97 return filename
101 return filename
98 finally:
102 finally:
99 if fh is not None:
103 if fh is not None:
100 fh.close()
104 fh.close()
101 if cleanup is not None:
105 if cleanup is not None:
102 os.unlink(cleanup)
106 os.unlink(cleanup)
103
107
104 def readbundle(fh, fname):
108 def readbundle(fh, fname):
105 header = fh.read(6)
109 header = fh.read(6)
106 if not header.startswith("HG"):
110 if not header.startswith("HG"):
107 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
111 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
108 elif not header.startswith("HG10"):
112 elif not header.startswith("HG10"):
109 raise util.Abort(_("%s: unknown bundle version") % fname)
113 raise util.Abort(_("%s: unknown bundle version") % fname)
110
114
111 if header == "HG10BZ":
115 if header == "HG10BZ":
112 def generator(f):
116 def generator(f):
113 zd = bz2.BZ2Decompressor()
117 zd = bz2.BZ2Decompressor()
114 zd.decompress("BZ")
118 zd.decompress("BZ")
115 for chunk in util.filechunkiter(f, 4096):
119 for chunk in util.filechunkiter(f, 4096):
116 yield zd.decompress(chunk)
120 yield zd.decompress(chunk)
117 return util.chunkbuffer(generator(fh))
121 return util.chunkbuffer(generator(fh))
118 elif header == "HG10UN":
122 elif header == "HG10UN":
119 return fh
123 return fh
120
124
121 raise util.Abort(_("%s: unknown bundle compression type")
125 raise util.Abort(_("%s: unknown bundle compression type")
122 % fname)
126 % fname)
General Comments 0
You need to be logged in to leave comments. Login now