##// END OF EJS Templates
bundle2: support bundling simple parameter...
Pierre-Yves David -
r20804:db9d3991 default
parent child Browse files
Show More
@@ -1,136 +1,165 b''
1 # bundle2.py - generic container format to transmit arbitrary data.
1 # bundle2.py - generic container format to transmit arbitrary data.
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, 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 """Handling of the new bundle2 format
7 """Handling of the new bundle2 format
8
8
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 payloads in an application agnostic way. It consist in a sequence of "parts"
10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 that will be handed to and processed by the application layer.
11 that will be handed to and processed by the application layer.
12
12
13
13
14 General format architecture
14 General format architecture
15 ===========================
15 ===========================
16
16
17 The format is architectured as follow
17 The format is architectured as follow
18
18
19 - magic string
19 - magic string
20 - stream level parameters
20 - stream level parameters
21 - payload parts (any number)
21 - payload parts (any number)
22 - end of stream marker.
22 - end of stream marker.
23
23
24 The current implementation is limited to empty bundle.
24 The current implementation accept some stream level option but no part.
25
25
26 Details on the Binary format
26 Details on the Binary format
27 ============================
27 ============================
28
28
29 All numbers are unsigned and big endian.
29 All numbers are unsigned and big endian.
30
30
31 stream level parameters
31 stream level parameters
32 ------------------------
32 ------------------------
33
33
34 Binary format is as follow
34 Binary format is as follow
35
35
36 :params size: (16 bits integer)
36 :params size: (16 bits integer)
37
37
38 The total number of Bytes used by the parameters
38 The total number of Bytes used by the parameters
39
39
40 Currently force to 0.
41
42 :params value: arbitrary number of Bytes
40 :params value: arbitrary number of Bytes
43
41
44 A blob of `params size` containing the serialized version of all stream level
42 A blob of `params size` containing the serialized version of all stream level
45 parameters.
43 parameters.
46
44
47 Currently always empty.
45 The blob contains a space separated list of parameters.
46
47 Parameter value are not supported yet.
48
49 Special character in param name are not supported yet.
50
51
48
52
49
53
50 Payload part
54 Payload part
51 ------------------------
55 ------------------------
52
56
53 Binary format is as follow
57 Binary format is as follow
54
58
55 :header size: (16 bits inter)
59 :header size: (16 bits inter)
56
60
57 The total number of Bytes used by the part headers. When the header is empty
61 The total number of Bytes used by the part headers. When the header is empty
58 (size = 0) this is interpreted as the end of stream marker.
62 (size = 0) this is interpreted as the end of stream marker.
59
63
60 Currently forced to 0 in the current state of the implementation
64 Currently forced to 0 in the current state of the implementation
61 """
65 """
62
66
63 import util
67 import util
68 import struct
69
64 import changegroup
70 import changegroup
65 from i18n import _
71 from i18n import _
66
72
73 _pack = struct.pack
74 _unpack = struct.unpack
75
67 _magicstring = 'HG20'
76 _magicstring = 'HG20'
68
77
78 _fstreamparamsize = '>H'
79
69 class bundle20(object):
80 class bundle20(object):
70 """represent an outgoing bundle2 container
81 """represent an outgoing bundle2 container
71
82
72 People will eventually be able to add param and parts to this object and
83 Use the `addparam` method to add stream level parameter. Then call
73 generated a stream from it."""
84 `getchunks` to retrieve all the binary chunks of datathat compose the
85 bundle2 container.
86
87 This object does not support payload part yet."""
74
88
75 def __init__(self):
89 def __init__(self):
76 self._params = []
90 self._params = []
77 self._parts = []
91 self._parts = []
78
92
93 def addparam(self, name, value=None):
94 """add a stream level parameter"""
95 self._params.append((name, value))
96
79 def getchunks(self):
97 def getchunks(self):
80 yield _magicstring
98 yield _magicstring
81 # no support for any param yet
99 param = self._paramchunk()
82 # to be obviously fixed soon.
100 yield _pack(_fstreamparamsize, len(param))
83 assert not self._params
101 if param:
84 yield '\0\0'
102 yield param
103
85 # no support for parts
104 # no support for parts
86 # to be obviously fixed soon.
105 # to be obviously fixed soon.
87 assert not self._parts
106 assert not self._parts
88 yield '\0\0'
107 yield '\0\0'
89
108
109 def _paramchunk(self):
110 """return a encoded version of all stream parameters"""
111 blocks = []
112 for key, value in self._params:
113 # XXX no support for value yet
114 assert value is None
115 # XXX no escaping yet
116 blocks.append(key)
117 return ' '.join(blocks)
118
90 class unbundle20(object):
119 class unbundle20(object):
91 """interpret a bundle2 stream
120 """interpret a bundle2 stream
92
121
93 (this will eventually yield parts)"""
122 (this will eventually yield parts)"""
94
123
95 def __init__(self, fp):
124 def __init__(self, fp):
96 self._fp = fp
125 self._fp = fp
97 header = self._readexact(4)
126 header = self._readexact(4)
98 magic, version = header[0:2], header[2:4]
127 magic, version = header[0:2], header[2:4]
99 if magic != 'HG':
128 if magic != 'HG':
100 raise util.Abort(_('not a Mercurial bundle'))
129 raise util.Abort(_('not a Mercurial bundle'))
101 if version != '20':
130 if version != '20':
102 raise util.Abort(_('unknown bundle version %s') % version)
131 raise util.Abort(_('unknown bundle version %s') % version)
103
132
104 def _unpack(self, format):
133 def _unpack(self, format):
105 """unpack this struct format from the stream"""
134 """unpack this struct format from the stream"""
106 data = self._readexact(struct.calcsize(format))
135 data = self._readexact(struct.calcsize(format))
107 return _unpack(format, data)
136 return _unpack(format, data)
108
137
109 def _readexact(self, size):
138 def _readexact(self, size):
110 """read exactly <size> bytes from the stream"""
139 """read exactly <size> bytes from the stream"""
111 return changegroup.readexactly(self._fp, size)
140 return changegroup.readexactly(self._fp, size)
112
141
113 @util.propertycache
142 @util.propertycache
114 def params(self):
143 def params(self):
115 """dictionnary of stream level parameters"""
144 """dictionnary of stream level parameters"""
116 paramsize = self._readexact(2)
145 paramsize = self._readexact(2)
117 assert paramsize == '\0\0'
146 assert paramsize == '\0\0'
118 return {}
147 return {}
119
148
120 def __iter__(self):
149 def __iter__(self):
121 """yield all parts contained in the stream"""
150 """yield all parts contained in the stream"""
122 # make sure param have been loaded
151 # make sure param have been loaded
123 self.params
152 self.params
124 part = self._readpart()
153 part = self._readpart()
125 while part is not None:
154 while part is not None:
126 yield part
155 yield part
127 part = self._readpart()
156 part = self._readpart()
128
157
129 def _readpart(self):
158 def _readpart(self):
130 """return None when an end of stream markers is reach"""
159 """return None when an end of stream markers is reach"""
131 headersize = self._readexact(2)
160 headersize = self._readexact(2)
132 assert headersize == '\0\0'
161 assert headersize == '\0\0'
133 return None
162 return None
134
163
135
164
136
165
@@ -1,62 +1,94 b''
1
1
2 Create an extension to test bundle2 API
2 Create an extension to test bundle2 API
3
3
4 $ cat > bundle2.py << EOF
4 $ cat > bundle2.py << EOF
5 > """A small extension to test bundle2 implementation
5 > """A small extension to test bundle2 implementation
6 >
6 >
7 > Current bundle2 implementation is far too limited to be used in any core
7 > Current bundle2 implementation is far too limited to be used in any core
8 > code. We still need to be able to test it while it grow up.
8 > code. We still need to be able to test it while it grow up.
9 > """
9 > """
10 >
10 >
11 > import sys
11 > import sys
12 > from mercurial import cmdutil
12 > from mercurial import cmdutil
13 > from mercurial import bundle2
13 > from mercurial import bundle2
14 > cmdtable = {}
14 > cmdtable = {}
15 > command = cmdutil.command(cmdtable)
15 > command = cmdutil.command(cmdtable)
16 >
16 >
17 > @command('bundle2', [], '')
17 > @command('bundle2',
18 > def cmdbundle2(ui, repo):
18 > [('', 'param', [], 'stream level parameter'),],
19 > '')
20 > def cmdbundle2(ui, repo, **opts):
19 > """write a bundle2 container on standard ouput"""
21 > """write a bundle2 container on standard ouput"""
20 > bundle = bundle2.bundle20()
22 > bundler = bundle2.bundle20()
21 > for chunk in bundle.getchunks():
23 > for p in opts['param']:
24 > bundler.addparam(p)
25 > for chunk in bundler.getchunks():
22 > ui.write(chunk)
26 > ui.write(chunk)
23 >
27 >
24 > @command('unbundle2', [], '')
28 > @command('unbundle2', [], '')
25 > def cmdunbundle2(ui, repo):
29 > def cmdunbundle2(ui, repo):
26 > """read a bundle2 container from standard input"""
30 > """read a bundle2 container from standard input"""
27 > unbundler = bundle2.unbundle20(sys.stdin)
31 > unbundler = bundle2.unbundle20(sys.stdin)
28 > ui.write('options count: %i\n' % len(unbundler.params))
32 > ui.write('options count: %i\n' % len(unbundler.params))
29 > parts = list(unbundler)
33 > parts = list(unbundler)
30 > ui.write('parts count: %i\n' % len(parts))
34 > ui.write('parts count: %i\n' % len(parts))
31 > EOF
35 > EOF
32 $ cat >> $HGRCPATH << EOF
36 $ cat >> $HGRCPATH << EOF
33 > [extensions]
37 > [extensions]
34 > bundle2=$TESTTMP/bundle2.py
38 > bundle2=$TESTTMP/bundle2.py
35 > EOF
39 > EOF
36
40
37 The extension requires a repo (currently unused)
41 The extension requires a repo (currently unused)
38
42
39 $ hg init main
43 $ hg init main
40 $ cd main
44 $ cd main
41 $ touch a
45 $ touch a
42 $ hg add a
46 $ hg add a
43 $ hg commit -m 'a'
47 $ hg commit -m 'a'
44
48
45 Test simple generation of empty bundle
49
50 Empty bundle
51 =================
52
53 - no option
54 - no parts
55
56 Test bundling
46
57
47 $ hg bundle2
58 $ hg bundle2
48 HG20\x00\x00\x00\x00 (no-eol) (esc)
59 HG20\x00\x00\x00\x00 (no-eol) (esc)
49
60
50 Test parsing of an empty bundle
61 Test unbundling
51
62
52 $ hg bundle2 | hg unbundle2
63 $ hg bundle2 | hg unbundle2
53 options count: 0
64 options count: 0
54 parts count: 0
65 parts count: 0
55
66
56 Test old style bundle are detected and refused
67 Test old style bundle are detected and refused
57
68
58 $ hg bundle --all ../bundle.hg
69 $ hg bundle --all ../bundle.hg
59 1 changesets found
70 1 changesets found
60 $ hg unbundle2 < ../bundle.hg
71 $ hg unbundle2 < ../bundle.hg
61 abort: unknown bundle version 10
72 abort: unknown bundle version 10
62 [255]
73 [255]
74
75 Test parameters
76 =================
77
78 - some options
79 - no parts
80
81 advisory parameters, no value
82 -------------------------------
83
84 Simplest possible parameters form
85
86 Test generation
87
88 $ hg bundle2 --param 'caution'
89 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
90
91 Test generation multiple option
92
93 $ hg bundle2 --param 'caution' --param 'meal'
94 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
General Comments 0
You need to be logged in to leave comments. Login now